2 * readprofile.c - used to read /proc/profile
4 * Copyright (C) 1994,1996 Alessandro Rubini (rubini@ipvvis.unipv.it)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL>
23 * - added Native Language Support
24 * 1999-09-01 Stephane Eranian <eranian@cello.hpl.hp.com>
26 * 3Feb2001 Andrew Morton <andrewm@uow.edu.au>
27 * - -M option to write profile multiplier.
28 * 2001-11-07 Werner Almesberger <wa@almesberger.net>
29 * - byte order auto-detection and -n option
30 * 2001-11-09 Werner Almesberger <wa@almesberger.net>
31 * - skip step size (index 0)
32 * 2002-03-09 John Levon <moz@compsoc.man.ac.uk>
33 * - make maplineno do something
34 * 2002-11-28 Mads Martin Joergensen +
35 * - also try /boot/System.map-`uname -r`
36 * 2003-04-09 Werner Almesberger <wa@almesberger.net>
37 * - fixed off-by eight error and improved heuristics in byte order detection
38 * 2003-08-12 Nikita Danilov <Nikita@Namesys.COM>
39 * - added -s option; example of use:
40 * "readprofile -s -m /boot/System.map-test | grep __d_lookup | sort -n -k3"
50 #include <sys/types.h>
51 #include <sys/utsname.h>
56 #include "closestream.h"
60 /* These are the defaults */
61 static char defaultmap
[]="/boot/System.map";
62 static char defaultpro
[]="/proc/profile";
64 static FILE *myopen(char *name
, char *mode
, int *flag
)
66 int len
= strlen(name
);
68 if (!strcmp(name
+ len
- 3, ".gz")) {
70 char *cmdline
= xmalloc(len
+ 6);
71 sprintf(cmdline
, "zcat %s", name
);
72 res
= popen(cmdline
, mode
);
78 return fopen(name
, mode
);
81 #ifndef BOOT_SYSTEM_MAP
82 #define BOOT_SYSTEM_MAP "/boot/System.map-"
85 static char *boot_uname_r_str(void)
87 struct utsname uname_info
;
91 if (uname(&uname_info
))
93 len
= strlen(BOOT_SYSTEM_MAP
) + strlen(uname_info
.release
) + 1;
95 strcpy(s
, BOOT_SYSTEM_MAP
);
96 strcat(s
, uname_info
.release
);
100 static void __attribute__((__noreturn__
)) usage(void)
103 fputs(USAGE_HEADER
, out
);
104 fprintf(out
, _(" %s [options]\n"), program_invocation_short_name
);
106 fputs(USAGE_SEPARATOR
, out
);
107 fputs(_("Display kernel profiling information.\n"), out
);
109 fputs(USAGE_OPTIONS
, out
);
111 _(" -m, --mapfile <mapfile> (defaults: \"%s\" and\n"), defaultmap
);
113 _(" \"%s\")\n"), boot_uname_r_str());
115 _(" -p, --profile <pro-file> (default: \"%s\")\n"), defaultpro
);
116 fputs(_(" -M, --multiplier <mult> set the profiling multiplier to <mult>\n"), out
);
117 fputs(_(" -i, --info print only info about the sampling step\n"), out
);
118 fputs(_(" -v, --verbose print verbose data\n"), out
);
119 fputs(_(" -a, --all print all symbols, even if count is 0\n"), out
);
120 fputs(_(" -b, --histbin print individual histogram-bin counts\n"), out
);
121 fputs(_(" -s, --counters print individual counters within functions\n"), out
);
122 fputs(_(" -r, --reset reset all the counters (root only)\n"), out
);
123 fputs(_(" -n, --no-auto disable byte order auto-detection\n"), out
);
124 fputs(USAGE_SEPARATOR
, out
);
125 printf(USAGE_HELP_OPTIONS(27));
126 printf(USAGE_MAN_TAIL("readprofile(8)"));
130 int main(int argc
, char **argv
)
134 char *mapFile
, *proFile
, *mult
= NULL
;
135 size_t len
= 0, indx
= 1;
136 unsigned long long add0
= 0;
138 unsigned int *buf
, total
, fn_len
;
139 unsigned long long fn_add
= 0, next_add
; /* current and next address */
140 char fn_name
[S_LEN
], next_name
[S_LEN
]; /* current and next name */
144 int optAll
= 0, optInfo
= 0, optReset
= 0, optVerbose
= 0, optNative
= 0;
145 int optBins
= 0, optSub
= 0;
148 int popenMap
; /* flag to tell if popen() has been used */
151 static const struct option longopts
[] = {
152 {"mapfile", required_argument
, NULL
, 'm'},
153 {"profile", required_argument
, NULL
, 'p'},
154 {"multiplier", required_argument
, NULL
, 'M'},
155 {"info", no_argument
, NULL
, 'i'},
156 {"verbose", no_argument
, NULL
, 'v'},
157 {"all", no_argument
, NULL
, 'a'},
158 {"histbin", no_argument
, NULL
, 'b'},
159 {"counters", no_argument
, NULL
, 's'},
160 {"reset", no_argument
, NULL
, 'r'},
161 {"no-auto", no_argument
, NULL
, 'n'},
162 {"version", no_argument
, NULL
, 'V'},
163 {"help", no_argument
, NULL
, 'h'},
167 #define next (current^1)
169 setlocale(LC_ALL
, "");
170 bindtextdomain(PACKAGE
, LOCALEDIR
);
172 close_stdout_atexit();
174 proFile
= defaultpro
;
175 mapFile
= defaultmap
;
177 while ((c
= getopt_long(argc
, argv
, "m:p:M:ivabsrnVh", longopts
, NULL
)) != -1) {
211 print_version(EXIT_SUCCESS
);
215 errtryhelp(EXIT_FAILURE
);
219 if (optReset
|| mult
) {
220 int multiplier
, fd
, to_write
;
222 /* When writing the multiplier, if the length of the
223 * write is not sizeof(int), the multiplier is not
226 multiplier
= strtoul(mult
, NULL
, 10);
227 to_write
= sizeof(int);
230 /* sth different from sizeof(int) */
233 /* try to become root, just in case */
234 ignore_result( setuid(0) );
235 fd
= open(defaultpro
, O_WRONLY
);
237 err(EXIT_FAILURE
, "%s", defaultpro
);
238 if (write(fd
, &multiplier
, to_write
) != to_write
)
239 err(EXIT_FAILURE
, _("error writing %s"), defaultpro
);
244 /* Use an fd for the profiling buffer, to skip stdio overhead */
245 if (((proFd
= open(proFile
, O_RDONLY
)) < 0)
246 || ((int)(len
= lseek(proFd
, 0, SEEK_END
)) < 0)
247 || (lseek(proFd
, 0, SEEK_SET
) < 0))
248 err(EXIT_FAILURE
, "%s", proFile
);
250 errx(EXIT_FAILURE
, "%s: %s", proFile
, _("input file is empty"));
254 rc
= read(proFd
, buf
, len
);
255 if (rc
< 0 || (size_t) rc
!= len
)
256 err(EXIT_FAILURE
, "%s", proFile
);
260 int entries
= len
/ sizeof(*buf
);
261 int big
= 0, small
= 0;
265 for (p
= buf
+ 1; p
< buf
+ entries
; p
++) {
266 if (*p
& ~0U << ((unsigned) sizeof(*buf
) * 4U))
268 if (*p
& ((1U << ((unsigned) sizeof(*buf
) * 4U)) - 1U))
272 warnx(_("Assuming reversed byte order. "
273 "Use -n to force native byte order."));
274 for (p
= buf
; p
< buf
+ entries
; p
++)
275 for (i
= 0; i
< sizeof(*buf
) / 2; i
++) {
276 unsigned char *b
= (unsigned char *)p
;
279 b
[i
] = b
[sizeof(*buf
) - i
- 1];
280 b
[sizeof(*buf
) - i
- 1] = tmp
;
287 printf(_("Sampling_step: %u\n"), step
);
293 map
= myopen(mapFile
, "r", &popenMap
);
294 if (map
== NULL
&& mapFile
== defaultmap
) {
295 mapFile
= boot_uname_r_str();
296 map
= myopen(mapFile
, "r", &popenMap
);
299 err(EXIT_FAILURE
, "%s", mapFile
);
301 while (fgets(mapline
, S_LEN
, map
)) {
302 if (sscanf(mapline
, "%llx %7[^\n ] %127[^\n ]", &fn_add
, mode
, fn_name
) != 3)
303 errx(EXIT_FAILURE
, _("%s(%i): wrong map line"), mapFile
,
305 /* only elf works like this */
306 if (!strcmp(fn_name
, "_stext") || !strcmp(fn_name
, "__stext")) {
314 errx(EXIT_FAILURE
, _("can't find \"_stext\" in %s"), mapFile
);
319 while (fgets(mapline
, S_LEN
, map
)) {
320 unsigned int this = 0;
323 if (sscanf(mapline
, "%llx %7[^\n ] %127[^\n ]", &next_add
, mode
, next_name
) != 3)
324 errx(EXIT_FAILURE
, _("%s(%i): wrong map line"), mapFile
,
328 /* the kernel only profiles up to _etext */
329 if (!strcmp(next_name
, "_etext") ||
330 !strcmp(next_name
, "__etext"))
333 /* ignore any LEADING (before a '[tT]' symbol
334 * is found) Absolute symbols and __init_end
335 * because some architectures place it before
337 if ((*mode
== 'A' || *mode
== '?')
338 && (total
== 0 || !strcmp(next_name
, "__init_end")))
340 if (*mode
!= 'T' && *mode
!= 't' &&
341 *mode
!= 'W' && *mode
!= 'w')
342 break; /* only text is profiled */
345 if (indx
>= len
/ sizeof(*buf
))
347 _("profile address out of range. Wrong map file?"));
349 while (indx
< (next_add
- add0
) / step
) {
350 if (optBins
&& (buf
[indx
] || optAll
)) {
351 if (!header_printed
) {
352 printf("%s:\n", fn_name
);
355 printf("\t%llx\t%u\n", (indx
- 1) * step
+ add0
,
363 if (optVerbose
|| this > 0)
364 printf(" total\t\t\t\t%u\n", this);
365 } else if ((this || optAll
) &&
366 (fn_len
= next_add
- fn_add
) != 0) {
368 printf("%016llx %-40s %6u %8.4f\n", fn_add
,
369 fn_name
, this, this / (double)fn_len
);
371 printf("%6u %-40s %8.4f\n",
372 this, fn_name
, this / (double)fn_len
);
374 unsigned long long scan
;
376 for (scan
= (fn_add
- add0
) / step
+ 1;
377 scan
< (next_add
- add0
) / step
;
379 unsigned long long addr
;
380 addr
= (scan
- 1) * step
+ add0
;
381 printf("\t%#llx\t%s+%#llx\t%u\n",
382 addr
, fn_name
, addr
- fn_add
,
389 strcpy(fn_name
, next_name
);
396 /* clock ticks, out of kernel text - probably modules */
397 printf("%6u %s\n", buf
[len
/ sizeof(*buf
) - 1], "*unknown*");
401 printf("%016x %-40s %6u %8.4f\n",
402 0, "total", total
, total
/ (double)(fn_add
- add0
));
404 printf("%6u %-40s %8.4f\n",
405 total
, _("total"), total
/ (double)(fn_add
- add0
));
407 popenMap
? pclose(map
) : fclose(map
);