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
;
85 if (uname(&uname_info
))
87 xasprintf(&s
, "%s%s", BOOT_SYSTEM_MAP
, uname_info
.release
);
91 static void __attribute__((__noreturn__
)) usage(void)
94 fputs(USAGE_HEADER
, out
);
95 fprintf(out
, _(" %s [options]\n"), program_invocation_short_name
);
97 fputs(USAGE_SEPARATOR
, out
);
98 fputs(_("Display kernel profiling information.\n"), out
);
100 fputs(USAGE_OPTIONS
, out
);
102 _(" -m, --mapfile <mapfile> (defaults: \"%s\" and\n"), defaultmap
);
104 _(" \"%s\")\n"), boot_uname_r_str());
106 _(" -p, --profile <pro-file> (default: \"%s\")\n"), defaultpro
);
107 fputs(_(" -M, --multiplier <mult> set the profiling multiplier to <mult>\n"), out
);
108 fputs(_(" -i, --info print only info about the sampling step\n"), out
);
109 fputs(_(" -v, --verbose print verbose data\n"), out
);
110 fputs(_(" -a, --all print all symbols, even if count is 0\n"), out
);
111 fputs(_(" -b, --histbin print individual histogram-bin counts\n"), out
);
112 fputs(_(" -s, --counters print individual counters within functions\n"), out
);
113 fputs(_(" -r, --reset reset all the counters (root only)\n"), out
);
114 fputs(_(" -n, --no-auto disable byte order auto-detection\n"), out
);
115 fputs(USAGE_SEPARATOR
, out
);
116 fprintf(out
, USAGE_HELP_OPTIONS(27));
117 fprintf(out
, USAGE_MAN_TAIL("readprofile(8)"));
121 int main(int argc
, char **argv
)
124 int proFd
, has_mult
= 0, multiplier
= 0;
125 char *mapFile
, *proFile
;
126 size_t len
= 0, indx
= 1;
127 unsigned long long add0
= 0;
129 unsigned int *buf
, total
, fn_len
;
130 unsigned long long fn_add
= 0, next_add
; /* current and next address */
131 char fn_name
[S_LEN
], next_name
[S_LEN
]; /* current and next name */
135 int optAll
= 0, optInfo
= 0, optReset
= 0, optVerbose
= 0, optNative
= 0;
136 int optBins
= 0, optSub
= 0;
139 int popenMap
; /* flag to tell if popen() has been used */
143 static const struct option longopts
[] = {
144 {"mapfile", required_argument
, NULL
, 'm'},
145 {"profile", required_argument
, NULL
, 'p'},
146 {"multiplier", required_argument
, NULL
, 'M'},
147 {"info", no_argument
, NULL
, 'i'},
148 {"verbose", no_argument
, NULL
, 'v'},
149 {"all", no_argument
, NULL
, 'a'},
150 {"histbin", no_argument
, NULL
, 'b'},
151 {"counters", no_argument
, NULL
, 's'},
152 {"reset", no_argument
, NULL
, 'r'},
153 {"no-auto", no_argument
, NULL
, 'n'},
154 {"version", no_argument
, NULL
, 'V'},
155 {"help", no_argument
, NULL
, 'h'},
159 #define next (current^1)
161 setlocale(LC_ALL
, "");
162 bindtextdomain(PACKAGE
, LOCALEDIR
);
164 close_stdout_atexit();
166 proFile
= defaultpro
;
167 mapFile
= defaultmap
;
169 while ((c
= getopt_long(argc
, argv
, "m:p:M:ivabsrnVh", longopts
, NULL
)) != -1) {
193 multiplier
= strtol_or_err(optarg
, _("failed to parse multiplier"));
204 print_version(EXIT_SUCCESS
);
208 errtryhelp(EXIT_FAILURE
);
212 if (optReset
|| has_mult
) {
215 /* When writing the multiplier, if the length of the
216 * write is not sizeof(int), the multiplier is not
219 to_write
= sizeof(int);
222 /* sth different from sizeof(int) */
225 /* try to become root, just in case */
226 ignore_result( setuid(0) );
227 fd
= open(defaultpro
, O_WRONLY
);
229 err(EXIT_FAILURE
, "%s", defaultpro
);
230 if (write(fd
, &multiplier
, to_write
) != to_write
)
231 err(EXIT_FAILURE
, _("error writing %s"), defaultpro
);
236 /* Use an fd for the profiling buffer, to skip stdio overhead */
237 if (((proFd
= open(proFile
, O_RDONLY
)) < 0)
238 || ((int)(len
= lseek(proFd
, 0, SEEK_END
)) < 0)
239 || (lseek(proFd
, 0, SEEK_SET
) < 0))
240 err(EXIT_FAILURE
, "%s", proFile
);
242 errx(EXIT_FAILURE
, "%s: %s", proFile
, _("input file is empty"));
246 rc
= read(proFd
, buf
, len
);
247 if (rc
< 0 || (size_t) rc
!= len
)
248 err(EXIT_FAILURE
, "%s", proFile
);
252 int entries
= len
/ sizeof(*buf
);
253 int big
= 0, small
= 0;
257 for (p
= buf
+ 1; p
< buf
+ entries
; p
++) {
258 if (*p
& ~0U << ((unsigned) sizeof(*buf
) * 4U))
260 if (*p
& ((1U << ((unsigned) sizeof(*buf
) * 4U)) - 1U))
264 warnx(_("Assuming reversed byte order. "
265 "Use -n to force native byte order."));
266 for (p
= buf
; p
< buf
+ entries
; p
++)
267 for (i
= 0; i
< sizeof(*buf
) / 2; i
++) {
268 unsigned char *b
= (unsigned char *)p
;
271 b
[i
] = b
[sizeof(*buf
) - i
- 1];
272 b
[sizeof(*buf
) - i
- 1] = tmp
;
279 printf(_("Sampling_step: %u\n"), step
);
285 map
= myopen(mapFile
, "r", &popenMap
);
286 if (map
== NULL
&& mapFile
== defaultmap
) {
287 mapFile
= boot_uname_r_str();
288 map
= myopen(mapFile
, "r", &popenMap
);
291 err(EXIT_FAILURE
, "%s", mapFile
);
293 while (fgets(mapline
, S_LEN
, map
)) {
294 if (sscanf(mapline
, "%llx %7[^\n ] %127[^\n ]", &fn_add
, mode
, fn_name
) != 3)
295 errx(EXIT_FAILURE
, _("%s(%i): wrong map line"), mapFile
,
297 /* only elf works like this */
298 if (!strcmp(fn_name
, "_stext") || !strcmp(fn_name
, "__stext")) {
306 errx(EXIT_FAILURE
, _("can't find \"_stext\" in %s"), mapFile
);
311 while (fgets(mapline
, S_LEN
, map
)) {
312 unsigned int this = 0;
315 if (sscanf(mapline
, "%llx %7[^\n ] %127[^\n ]", &next_add
, mode
, next_name
) != 3)
316 errx(EXIT_FAILURE
, _("%s(%i): wrong map line"), mapFile
,
320 /* the kernel only profiles up to _etext */
321 if (!strcmp(next_name
, "_etext") ||
322 !strcmp(next_name
, "__etext"))
325 /* ignore any LEADING (before a '[tT]' symbol
326 * is found) Absolute symbols and __init_end
327 * because some architectures place it before
329 if ((*mode
== 'A' || *mode
== '?')
330 && (total
== 0 || !strcmp(next_name
, "__init_end")))
332 if (*mode
!= 'T' && *mode
!= 't' &&
333 *mode
!= 'W' && *mode
!= 'w')
334 break; /* only text is profiled */
337 if (indx
>= len
/ sizeof(*buf
))
339 _("profile address out of range. Wrong map file?"));
341 while (step
> 0 && indx
< (next_add
- add0
) / step
) {
342 if (optBins
&& (buf
[indx
] || optAll
)) {
343 if (!header_printed
) {
344 printf("%s:\n", fn_name
);
347 printf("\t%llx\t%u\n", (indx
- 1) * step
+ add0
,
355 if (optVerbose
|| this > 0)
356 printf(" total\t\t\t\t%u\n", this);
357 } else if ((this || optAll
) &&
358 (fn_len
= next_add
- fn_add
) != 0) {
360 printf("%016llx %-40s %6u %8.4f\n", fn_add
,
361 fn_name
, this, this / (double)fn_len
);
363 printf("%6u %-40s %8.4f\n",
364 this, fn_name
, this / (double)fn_len
);
365 if (optSub
&& step
> 0) {
366 unsigned long long scan
;
368 for (scan
= (fn_add
- add0
) / step
+ 1;
369 scan
< (next_add
- add0
) / step
;
371 unsigned long long addr
;
372 addr
= (scan
- 1) * step
+ add0
;
373 printf("\t%#llx\t%s+%#llx\t%u\n",
374 addr
, fn_name
, addr
- fn_add
,
381 strcpy(fn_name
, next_name
);
388 /* clock ticks, out of kernel text - probably modules */
389 printf("%6u %s\n", buf
[len
/ sizeof(*buf
) - 1], "*unknown*");
392 rep
= total
/ (double)(fn_add
- add0
);
396 printf("%016x %-40s %6u %8.4f\n",
397 0, "total", total
, rep
);
399 printf("%6u %-40s %8.4f\n",
400 total
, _("total"), rep
);
402 popenMap
? pclose(map
) : fclose(map
);