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>
58 #include "closestream.h"
62 /* These are the defaults */
63 static char defaultmap
[]="/boot/System.map";
64 static char defaultpro
[]="/proc/profile";
66 static FILE *myopen(char *name
, char *mode
, int *flag
)
68 int len
= strlen(name
);
70 if (!strcmp(name
+ len
- 3, ".gz")) {
72 char *cmdline
= xmalloc(len
+ 6);
73 snprintf(cmdline
, len
+ 6, "zcat %s", name
);
74 res
= popen(cmdline
, mode
);
80 return fopen(name
, mode
);
83 #ifndef BOOT_SYSTEM_MAP
84 #define BOOT_SYSTEM_MAP "/boot/System.map-"
87 static char *boot_uname_r_str(void)
89 struct utsname uname_info
;
93 if (uname(&uname_info
))
95 len
= strlen(BOOT_SYSTEM_MAP
) + strlen(uname_info
.release
) + 1;
97 strcpy(s
, BOOT_SYSTEM_MAP
);
98 strcat(s
, uname_info
.release
);
102 static void __attribute__((__noreturn__
)) usage(void)
105 fputs(USAGE_HEADER
, out
);
106 fprintf(out
, _(" %s [options]\n"), program_invocation_short_name
);
108 fputs(USAGE_SEPARATOR
, out
);
109 fputs(_("Display kernel profiling information.\n"), out
);
111 fputs(USAGE_OPTIONS
, out
);
113 _(" -m, --mapfile <mapfile> (defaults: \"%s\" and\n"), defaultmap
);
115 _(" \"%s\")\n"), boot_uname_r_str());
117 _(" -p, --profile <pro-file> (default: \"%s\")\n"), defaultpro
);
118 fputs(_(" -M, --multiplier <mult> set the profiling multiplier to <mult>\n"), out
);
119 fputs(_(" -i, --info print only info about the sampling step\n"), out
);
120 fputs(_(" -v, --verbose print verbose data\n"), out
);
121 fputs(_(" -a, --all print all symbols, even if count is 0\n"), out
);
122 fputs(_(" -b, --histbin print individual histogram-bin counts\n"), out
);
123 fputs(_(" -s, --counters print individual counters within functions\n"), out
);
124 fputs(_(" -r, --reset reset all the counters (root only)\n"), out
);
125 fputs(_(" -n, --no-auto disable byte order auto-detection\n"), out
);
126 fputs(USAGE_SEPARATOR
, out
);
127 fprintf(out
, USAGE_HELP_OPTIONS(27));
128 fprintf(out
, USAGE_MAN_TAIL("readprofile(8)"));
132 int main(int argc
, char **argv
)
135 int proFd
, has_mult
= 0, multiplier
= 0;
136 char *mapFile
, *proFile
;
137 size_t len
= 0, indx
= 1;
138 unsigned long long add0
= 0;
140 unsigned int *buf
, total
, fn_len
;
141 unsigned long long fn_add
= 0, next_add
; /* current and next address */
142 char fn_name
[S_LEN
], next_name
[S_LEN
]; /* current and next name */
146 int optAll
= 0, optInfo
= 0, optReset
= 0, optVerbose
= 0, optNative
= 0;
147 int optBins
= 0, optSub
= 0;
150 int popenMap
; /* flag to tell if popen() has been used */
154 static const struct option longopts
[] = {
155 {"mapfile", required_argument
, NULL
, 'm'},
156 {"profile", required_argument
, NULL
, 'p'},
157 {"multiplier", required_argument
, NULL
, 'M'},
158 {"info", no_argument
, NULL
, 'i'},
159 {"verbose", no_argument
, NULL
, 'v'},
160 {"all", no_argument
, NULL
, 'a'},
161 {"histbin", no_argument
, NULL
, 'b'},
162 {"counters", no_argument
, NULL
, 's'},
163 {"reset", no_argument
, NULL
, 'r'},
164 {"no-auto", no_argument
, NULL
, 'n'},
165 {"version", no_argument
, NULL
, 'V'},
166 {"help", no_argument
, NULL
, 'h'},
170 #define next (current^1)
172 setlocale(LC_ALL
, "");
173 bindtextdomain(PACKAGE
, LOCALEDIR
);
175 close_stdout_atexit();
177 proFile
= defaultpro
;
178 mapFile
= defaultmap
;
180 while ((c
= getopt_long(argc
, argv
, "m:p:M:ivabsrnVh", longopts
, NULL
)) != -1) {
204 multiplier
= strtol_or_err(optarg
, _("failed to parse multiplier"));
215 print_version(EXIT_SUCCESS
);
219 errtryhelp(EXIT_FAILURE
);
223 if (optReset
|| has_mult
) {
226 /* When writing the multiplier, if the length of the
227 * write is not sizeof(int), the multiplier is not
230 to_write
= sizeof(int);
233 /* sth different from sizeof(int) */
236 /* try to become root, just in case */
237 ignore_result( setuid(0) );
238 fd
= open(defaultpro
, O_WRONLY
);
240 err(EXIT_FAILURE
, "%s", defaultpro
);
241 if (write(fd
, &multiplier
, to_write
) != to_write
)
242 err(EXIT_FAILURE
, _("error writing %s"), defaultpro
);
247 /* Use an fd for the profiling buffer, to skip stdio overhead */
248 if (((proFd
= open(proFile
, O_RDONLY
)) < 0)
249 || ((int)(len
= lseek(proFd
, 0, SEEK_END
)) < 0)
250 || (lseek(proFd
, 0, SEEK_SET
) < 0))
251 err(EXIT_FAILURE
, "%s", proFile
);
253 errx(EXIT_FAILURE
, "%s: %s", proFile
, _("input file is empty"));
257 rc
= read(proFd
, buf
, len
);
258 if (rc
< 0 || (size_t) rc
!= len
)
259 err(EXIT_FAILURE
, "%s", proFile
);
263 int entries
= len
/ sizeof(*buf
);
264 int big
= 0, small
= 0;
268 for (p
= buf
+ 1; p
< buf
+ entries
; p
++) {
269 if (*p
& ~0U << ((unsigned) sizeof(*buf
) * 4U))
271 if (*p
& ((1U << ((unsigned) sizeof(*buf
) * 4U)) - 1U))
275 warnx(_("Assuming reversed byte order. "
276 "Use -n to force native byte order."));
277 for (p
= buf
; p
< buf
+ entries
; p
++)
278 for (i
= 0; i
< sizeof(*buf
) / 2; i
++) {
279 unsigned char *b
= (unsigned char *)p
;
282 b
[i
] = b
[sizeof(*buf
) - i
- 1];
283 b
[sizeof(*buf
) - i
- 1] = tmp
;
290 printf(_("Sampling_step: %u\n"), step
);
296 map
= myopen(mapFile
, "r", &popenMap
);
297 if (map
== NULL
&& mapFile
== defaultmap
) {
298 mapFile
= boot_uname_r_str();
299 map
= myopen(mapFile
, "r", &popenMap
);
302 err(EXIT_FAILURE
, "%s", mapFile
);
304 while (fgets(mapline
, S_LEN
, map
)) {
305 if (sscanf(mapline
, "%llx %7[^\n ] %127[^\n ]", &fn_add
, mode
, fn_name
) != 3)
306 errx(EXIT_FAILURE
, _("%s(%i): wrong map line"), mapFile
,
308 /* only elf works like this */
309 if (!strcmp(fn_name
, "_stext") || !strcmp(fn_name
, "__stext")) {
317 errx(EXIT_FAILURE
, _("can't find \"_stext\" in %s"), mapFile
);
322 while (fgets(mapline
, S_LEN
, map
)) {
323 unsigned int this = 0;
326 if (sscanf(mapline
, "%llx %7[^\n ] %127[^\n ]", &next_add
, mode
, next_name
) != 3)
327 errx(EXIT_FAILURE
, _("%s(%i): wrong map line"), mapFile
,
331 /* the kernel only profiles up to _etext */
332 if (!strcmp(next_name
, "_etext") ||
333 !strcmp(next_name
, "__etext"))
336 /* ignore any LEADING (before a '[tT]' symbol
337 * is found) Absolute symbols and __init_end
338 * because some architectures place it before
340 if ((*mode
== 'A' || *mode
== '?')
341 && (total
== 0 || !strcmp(next_name
, "__init_end")))
343 if (*mode
!= 'T' && *mode
!= 't' &&
344 *mode
!= 'W' && *mode
!= 'w')
345 break; /* only text is profiled */
348 if (indx
>= len
/ sizeof(*buf
))
350 _("profile address out of range. Wrong map file?"));
352 while (step
> 0 && indx
< (next_add
- add0
) / step
) {
353 if (optBins
&& (buf
[indx
] || optAll
)) {
354 if (!header_printed
) {
355 printf("%s:\n", fn_name
);
358 printf("\t%llx\t%u\n", (indx
- 1) * step
+ add0
,
366 if (optVerbose
|| this > 0)
367 printf(" total\t\t\t\t%u\n", this);
368 } else if ((this || optAll
) &&
369 (fn_len
= next_add
- fn_add
) != 0) {
371 printf("%016llx %-40s %6u %8.4f\n", fn_add
,
372 fn_name
, this, this / (double)fn_len
);
374 printf("%6u %-40s %8.4f\n",
375 this, fn_name
, this / (double)fn_len
);
376 if (optSub
&& step
> 0) {
377 unsigned long long scan
;
379 for (scan
= (fn_add
- add0
) / step
+ 1;
380 scan
< (next_add
- add0
) / step
;
382 unsigned long long addr
;
383 addr
= (scan
- 1) * step
+ add0
;
384 printf("\t%#llx\t%s+%#llx\t%u\n",
385 addr
, fn_name
, addr
- fn_add
,
392 strcpy(fn_name
, next_name
);
399 /* clock ticks, out of kernel text - probably modules */
400 printf("%6u %s\n", buf
[len
/ sizeof(*buf
) - 1], "*unknown*");
403 rep
= total
/ (double)(fn_add
- add0
);
407 printf("%016x %-40s %6u %8.4f\n",
408 0, "total", total
, rep
);
410 printf("%6u %-40s %8.4f\n",
411 total
, _("total"), rep
);
413 popenMap
? pclose(map
) : fclose(map
);