2 * SPDX-License-Identifier: GPL-2.0-or-later
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * Copyright (C) 1994,1996 Alessandro Rubini (rubini@ipvvis.unipv.it)
11 * readprofile.c - used to read /proc/profile
15 * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL>
16 * - added Native Language Support
17 * 1999-09-01 Stephane Eranian <eranian@cello.hpl.hp.com>
19 * 3Feb2001 Andrew Morton <andrewm@uow.edu.au>
20 * - -M option to write profile multiplier.
21 * 2001-11-07 Werner Almesberger <wa@almesberger.net>
22 * - byte order auto-detection and -n option
23 * 2001-11-09 Werner Almesberger <wa@almesberger.net>
24 * - skip step size (index 0)
25 * 2002-03-09 John Levon <moz@compsoc.man.ac.uk>
26 * - make maplineno do something
27 * 2002-11-28 Mads Martin Joergensen +
28 * - also try /boot/System.map-`uname -r`
29 * 2003-04-09 Werner Almesberger <wa@almesberger.net>
30 * - fixed off-by eight error and improved heuristics in byte order detection
31 * 2003-08-12 Nikita Danilov <Nikita@Namesys.COM>
32 * - added -s option; example of use:
33 * "readprofile -s -m /boot/System.map-test | grep __d_lookup | sort -n -k3"
43 #include <sys/types.h>
44 #include <sys/utsname.h>
51 #include "closestream.h"
55 /* These are the defaults */
56 static char defaultmap
[]="/boot/System.map";
57 static char defaultpro
[]="/proc/profile";
59 static FILE *myopen(char *name
, char *mode
, int *flag
)
61 int len
= strlen(name
);
63 if (!strcmp(name
+ len
- 3, ".gz")) {
65 char *cmdline
= xmalloc(len
+ 6);
66 snprintf(cmdline
, len
+ 6, "zcat %s", name
);
67 res
= popen(cmdline
, mode
);
73 return fopen(name
, mode
);
76 #ifndef BOOT_SYSTEM_MAP
77 #define BOOT_SYSTEM_MAP "/boot/System.map-"
80 static char *boot_uname_r_str(void)
82 struct utsname uname_info
;
86 if (uname(&uname_info
))
88 len
= strlen(BOOT_SYSTEM_MAP
) + strlen(uname_info
.release
) + 1;
90 strcpy(s
, BOOT_SYSTEM_MAP
);
91 strcat(s
, uname_info
.release
);
95 static void __attribute__((__noreturn__
)) usage(void)
98 fputs(USAGE_HEADER
, out
);
99 fprintf(out
, _(" %s [options]\n"), program_invocation_short_name
);
101 fputs(USAGE_SEPARATOR
, out
);
102 fputs(_("Display kernel profiling information.\n"), out
);
104 fputs(USAGE_OPTIONS
, out
);
106 _(" -m, --mapfile <mapfile> (defaults: \"%s\" and\n"), defaultmap
);
108 _(" \"%s\")\n"), boot_uname_r_str());
110 _(" -p, --profile <pro-file> (default: \"%s\")\n"), defaultpro
);
111 fputs(_(" -M, --multiplier <mult> set the profiling multiplier to <mult>\n"), out
);
112 fputs(_(" -i, --info print only info about the sampling step\n"), out
);
113 fputs(_(" -v, --verbose print verbose data\n"), out
);
114 fputs(_(" -a, --all print all symbols, even if count is 0\n"), out
);
115 fputs(_(" -b, --histbin print individual histogram-bin counts\n"), out
);
116 fputs(_(" -s, --counters print individual counters within functions\n"), out
);
117 fputs(_(" -r, --reset reset all the counters (root only)\n"), out
);
118 fputs(_(" -n, --no-auto disable byte order auto-detection\n"), out
);
119 fputs(USAGE_SEPARATOR
, out
);
120 fprintf(out
, USAGE_HELP_OPTIONS(27));
121 fprintf(out
, USAGE_MAN_TAIL("readprofile(8)"));
125 int main(int argc
, char **argv
)
128 int proFd
, has_mult
= 0, multiplier
= 0;
129 char *mapFile
, *proFile
;
130 size_t len
= 0, indx
= 1;
131 unsigned long long add0
= 0;
133 unsigned int *buf
, total
, fn_len
;
134 unsigned long long fn_add
= 0, next_add
; /* current and next address */
135 char fn_name
[S_LEN
], next_name
[S_LEN
]; /* current and next name */
139 int optAll
= 0, optInfo
= 0, optReset
= 0, optVerbose
= 0, optNative
= 0;
140 int optBins
= 0, optSub
= 0;
143 int popenMap
; /* flag to tell if popen() has been used */
147 static const struct option longopts
[] = {
148 {"mapfile", required_argument
, NULL
, 'm'},
149 {"profile", required_argument
, NULL
, 'p'},
150 {"multiplier", required_argument
, NULL
, 'M'},
151 {"info", no_argument
, NULL
, 'i'},
152 {"verbose", no_argument
, NULL
, 'v'},
153 {"all", no_argument
, NULL
, 'a'},
154 {"histbin", no_argument
, NULL
, 'b'},
155 {"counters", no_argument
, NULL
, 's'},
156 {"reset", no_argument
, NULL
, 'r'},
157 {"no-auto", no_argument
, NULL
, 'n'},
158 {"version", no_argument
, NULL
, 'V'},
159 {"help", no_argument
, NULL
, 'h'},
163 #define next (current^1)
165 setlocale(LC_ALL
, "");
166 bindtextdomain(PACKAGE
, LOCALEDIR
);
168 close_stdout_atexit();
170 proFile
= defaultpro
;
171 mapFile
= defaultmap
;
173 while ((c
= getopt_long(argc
, argv
, "m:p:M:ivabsrnVh", longopts
, NULL
)) != -1) {
197 multiplier
= strtol_or_err(optarg
, _("failed to parse multiplier"));
208 print_version(EXIT_SUCCESS
);
212 errtryhelp(EXIT_FAILURE
);
216 if (optReset
|| has_mult
) {
219 /* When writing the multiplier, if the length of the
220 * write is not sizeof(int), the multiplier is not
223 to_write
= sizeof(int);
226 /* sth different from sizeof(int) */
229 /* try to become root, just in case */
230 ignore_result( setuid(0) );
231 fd
= open(defaultpro
, O_WRONLY
);
233 err(EXIT_FAILURE
, "%s", defaultpro
);
234 if (write(fd
, &multiplier
, to_write
) != to_write
)
235 err(EXIT_FAILURE
, _("error writing %s"), defaultpro
);
240 /* Use an fd for the profiling buffer, to skip stdio overhead */
241 if (((proFd
= open(proFile
, O_RDONLY
)) < 0)
242 || ((int)(len
= lseek(proFd
, 0, SEEK_END
)) < 0)
243 || (lseek(proFd
, 0, SEEK_SET
) < 0))
244 err(EXIT_FAILURE
, "%s", proFile
);
246 errx(EXIT_FAILURE
, "%s: %s", proFile
, _("input file is empty"));
250 rc
= read(proFd
, buf
, len
);
251 if (rc
< 0 || (size_t) rc
!= len
)
252 err(EXIT_FAILURE
, "%s", proFile
);
256 int entries
= len
/ sizeof(*buf
);
257 int big
= 0, small
= 0;
261 for (p
= buf
+ 1; p
< buf
+ entries
; p
++) {
262 if (*p
& ~0U << ((unsigned) sizeof(*buf
) * 4U))
264 if (*p
& ((1U << ((unsigned) sizeof(*buf
) * 4U)) - 1U))
268 warnx(_("Assuming reversed byte order. "
269 "Use -n to force native byte order."));
270 for (p
= buf
; p
< buf
+ entries
; p
++)
271 for (i
= 0; i
< sizeof(*buf
) / 2; i
++) {
272 unsigned char *b
= (unsigned char *)p
;
275 b
[i
] = b
[sizeof(*buf
) - i
- 1];
276 b
[sizeof(*buf
) - i
- 1] = tmp
;
283 printf(_("Sampling_step: %u\n"), step
);
289 map
= myopen(mapFile
, "r", &popenMap
);
290 if (map
== NULL
&& mapFile
== defaultmap
) {
291 mapFile
= boot_uname_r_str();
292 map
= myopen(mapFile
, "r", &popenMap
);
295 err(EXIT_FAILURE
, "%s", mapFile
);
297 while (fgets(mapline
, S_LEN
, map
)) {
298 if (sscanf(mapline
, "%llx %7[^\n ] %127[^\n ]", &fn_add
, mode
, fn_name
) != 3)
299 errx(EXIT_FAILURE
, _("%s(%i): wrong map line"), mapFile
,
301 /* only elf works like this */
302 if (!strcmp(fn_name
, "_stext") || !strcmp(fn_name
, "__stext")) {
310 errx(EXIT_FAILURE
, _("can't find \"_stext\" in %s"), mapFile
);
315 while (fgets(mapline
, S_LEN
, map
)) {
316 unsigned int this = 0;
319 if (sscanf(mapline
, "%llx %7[^\n ] %127[^\n ]", &next_add
, mode
, next_name
) != 3)
320 errx(EXIT_FAILURE
, _("%s(%i): wrong map line"), mapFile
,
324 /* the kernel only profiles up to _etext */
325 if (!strcmp(next_name
, "_etext") ||
326 !strcmp(next_name
, "__etext"))
329 /* ignore any LEADING (before a '[tT]' symbol
330 * is found) Absolute symbols and __init_end
331 * because some architectures place it before
333 if ((*mode
== 'A' || *mode
== '?')
334 && (total
== 0 || !strcmp(next_name
, "__init_end")))
336 if (*mode
!= 'T' && *mode
!= 't' &&
337 *mode
!= 'W' && *mode
!= 'w')
338 break; /* only text is profiled */
341 if (indx
>= len
/ sizeof(*buf
))
343 _("profile address out of range. Wrong map file?"));
345 while (step
> 0 && indx
< (next_add
- add0
) / step
) {
346 if (optBins
&& (buf
[indx
] || optAll
)) {
347 if (!header_printed
) {
348 printf("%s:\n", fn_name
);
351 printf("\t%llx\t%u\n", (indx
- 1) * step
+ add0
,
359 if (optVerbose
|| this > 0)
360 printf(" total\t\t\t\t%u\n", this);
361 } else if ((this || optAll
) &&
362 (fn_len
= next_add
- fn_add
) != 0) {
364 printf("%016llx %-40s %6u %8.4f\n", fn_add
,
365 fn_name
, this, this / (double)fn_len
);
367 printf("%6u %-40s %8.4f\n",
368 this, fn_name
, this / (double)fn_len
);
369 if (optSub
&& step
> 0) {
370 unsigned long long scan
;
372 for (scan
= (fn_add
- add0
) / step
+ 1;
373 scan
< (next_add
- add0
) / step
;
375 unsigned long long addr
;
376 addr
= (scan
- 1) * step
+ add0
;
377 printf("\t%#llx\t%s+%#llx\t%u\n",
378 addr
, fn_name
, addr
- fn_add
,
385 strcpy(fn_name
, next_name
);
392 /* clock ticks, out of kernel text - probably modules */
393 printf("%6u %s\n", buf
[len
/ sizeof(*buf
) - 1], "*unknown*");
396 rep
= total
/ (double)(fn_add
- add0
);
400 printf("%016x %-40s %6u %8.4f\n",
401 0, "total", total
, rep
);
403 printf("%6u %-40s %8.4f\n",
404 total
, _("total"), rep
);
406 popenMap
? pclose(map
) : fclose(map
);