]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/readprofile.c
e810ea5e1b6063e450a3e1a9dd2375bdd72d0326
[thirdparty/util-linux.git] / sys-utils / readprofile.c
1 /*
2 * readprofile.c - used to read /proc/profile
3 *
4 * Copyright (C) 1994,1996 Alessandro Rubini (rubini@ipvvis.unipv.it)
5 *
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.
10 *
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.
15 *
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.
19 */
20
21 /*
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>
25 * - 64bit clean patch
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"
41 */
42
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <getopt.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <sys/stat.h>
50 #include <sys/types.h>
51 #include <sys/utsname.h>
52 #include <unistd.h>
53
54 #include "c.h"
55 #include "strutils.h"
56 #include "nls.h"
57 #include "xalloc.h"
58 #include "closestream.h"
59
60 #define S_LEN 128
61
62 /* These are the defaults */
63 static char defaultmap[]="/boot/System.map";
64 static char defaultpro[]="/proc/profile";
65
66 static FILE *myopen(char *name, char *mode, int *flag)
67 {
68 int len = strlen(name);
69
70 if (!strcmp(name + len - 3, ".gz")) {
71 FILE *res;
72 char *cmdline = xmalloc(len + 6);
73 snprintf(cmdline, len + 6, "zcat %s", name);
74 res = popen(cmdline, mode);
75 free(cmdline);
76 *flag = 1;
77 return res;
78 }
79 *flag = 0;
80 return fopen(name, mode);
81 }
82
83 #ifndef BOOT_SYSTEM_MAP
84 #define BOOT_SYSTEM_MAP "/boot/System.map-"
85 #endif
86
87 static char *boot_uname_r_str(void)
88 {
89 struct utsname uname_info;
90 char *s;
91 size_t len;
92
93 if (uname(&uname_info))
94 return "";
95 len = strlen(BOOT_SYSTEM_MAP) + strlen(uname_info.release) + 1;
96 s = xmalloc(len);
97 strcpy(s, BOOT_SYSTEM_MAP);
98 strcat(s, uname_info.release);
99 return s;
100 }
101
102 static void __attribute__((__noreturn__)) usage(void)
103 {
104 FILE *out = stdout;
105 fputs(USAGE_HEADER, out);
106 fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
107
108 fputs(USAGE_SEPARATOR, out);
109 fputs(_("Display kernel profiling information.\n"), out);
110
111 fputs(USAGE_OPTIONS, out);
112 fprintf(out,
113 _(" -m, --mapfile <mapfile> (defaults: \"%s\" and\n"), defaultmap);
114 fprintf(out,
115 _(" \"%s\")\n"), boot_uname_r_str());
116 fprintf(out,
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)"));
129 exit(EXIT_SUCCESS);
130 }
131
132 int main(int argc, char **argv)
133 {
134 FILE *map;
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;
139 unsigned int step;
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 */
143 char mode[8];
144 int c;
145 ssize_t rc;
146 int optAll = 0, optInfo = 0, optReset = 0, optVerbose = 0, optNative = 0;
147 int optBins = 0, optSub = 0;
148 char mapline[S_LEN];
149 int maplineno = 1;
150 int popenMap; /* flag to tell if popen() has been used */
151 int header_printed;
152 double rep = 0;
153
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'},
167 {NULL, 0, NULL, 0}
168 };
169
170 #define next (current^1)
171
172 setlocale(LC_ALL, "");
173 bindtextdomain(PACKAGE, LOCALEDIR);
174 textdomain(PACKAGE);
175 close_stdout_atexit();
176
177 proFile = defaultpro;
178 mapFile = defaultmap;
179
180 while ((c = getopt_long(argc, argv, "m:p:M:ivabsrnVh", longopts, NULL)) != -1) {
181 switch (c) {
182 case 'm':
183 mapFile = optarg;
184 break;
185 case 'n':
186 optNative++;
187 break;
188 case 'p':
189 proFile = optarg;
190 break;
191 case 'a':
192 optAll++;
193 break;
194 case 'b':
195 optBins++;
196 break;
197 case 's':
198 optSub++;
199 break;
200 case 'i':
201 optInfo++;
202 break;
203 case 'M':
204 multiplier = strtol_or_err(optarg, _("failed to parse multiplier"));
205 has_mult = 1;
206 break;
207 case 'r':
208 optReset++;
209 break;
210 case 'v':
211 optVerbose++;
212 break;
213
214 case 'V':
215 print_version(EXIT_SUCCESS);
216 case 'h':
217 usage();
218 default:
219 errtryhelp(EXIT_FAILURE);
220 }
221 }
222
223 if (optReset || has_mult) {
224 int fd, to_write;
225
226 /* When writing the multiplier, if the length of the
227 * write is not sizeof(int), the multiplier is not
228 * changed. */
229 if (has_mult) {
230 to_write = sizeof(int);
231 } else {
232 multiplier = 0;
233 /* sth different from sizeof(int) */
234 to_write = 1;
235 }
236 /* try to become root, just in case */
237 ignore_result( setuid(0) );
238 fd = open(defaultpro, O_WRONLY);
239 if (fd < 0)
240 err(EXIT_FAILURE, "%s", defaultpro);
241 if (write(fd, &multiplier, to_write) != to_write)
242 err(EXIT_FAILURE, _("error writing %s"), defaultpro);
243 close(fd);
244 exit(EXIT_SUCCESS);
245 }
246
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);
252 if (!len)
253 errx(EXIT_FAILURE, "%s: %s", proFile, _("input file is empty"));
254
255 buf = xmalloc(len);
256
257 rc = read(proFd, buf, len);
258 if (rc < 0 || (size_t) rc != len)
259 err(EXIT_FAILURE, "%s", proFile);
260 close(proFd);
261
262 if (!optNative) {
263 int entries = len / sizeof(*buf);
264 int big = 0, small = 0;
265 unsigned *p;
266 size_t i;
267
268 for (p = buf + 1; p < buf + entries; p++) {
269 if (*p & ~0U << ((unsigned) sizeof(*buf) * 4U))
270 big++;
271 if (*p & ((1U << ((unsigned) sizeof(*buf) * 4U)) - 1U))
272 small++;
273 }
274 if (big > small) {
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;
280 unsigned char tmp;
281 tmp = b[i];
282 b[i] = b[sizeof(*buf) - i - 1];
283 b[sizeof(*buf) - i - 1] = tmp;
284 }
285 }
286 }
287
288 step = buf[0];
289 if (optInfo) {
290 printf(_("Sampling_step: %u\n"), step);
291 exit(EXIT_SUCCESS);
292 }
293
294 total = 0;
295
296 map = myopen(mapFile, "r", &popenMap);
297 if (map == NULL && mapFile == defaultmap) {
298 mapFile = boot_uname_r_str();
299 map = myopen(mapFile, "r", &popenMap);
300 }
301 if (map == NULL)
302 err(EXIT_FAILURE, "%s", mapFile);
303
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,
307 maplineno);
308 /* only elf works like this */
309 if (!strcmp(fn_name, "_stext") || !strcmp(fn_name, "__stext")) {
310 add0 = fn_add;
311 break;
312 }
313 maplineno++;
314 }
315
316 if (!add0)
317 errx(EXIT_FAILURE, _("can't find \"_stext\" in %s"), mapFile);
318
319 /*
320 * Main loop.
321 */
322 while (fgets(mapline, S_LEN, map)) {
323 unsigned int this = 0;
324 int done = 0;
325
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,
328 maplineno);
329 header_printed = 0;
330
331 /* the kernel only profiles up to _etext */
332 if (!strcmp(next_name, "_etext") ||
333 !strcmp(next_name, "__etext"))
334 done = 1;
335 else {
336 /* ignore any LEADING (before a '[tT]' symbol
337 * is found) Absolute symbols and __init_end
338 * because some architectures place it before
339 * .text section */
340 if ((*mode == 'A' || *mode == '?')
341 && (total == 0 || !strcmp(next_name, "__init_end")))
342 continue;
343 if (*mode != 'T' && *mode != 't' &&
344 *mode != 'W' && *mode != 'w')
345 break; /* only text is profiled */
346 }
347
348 if (indx >= len / sizeof(*buf))
349 errx(EXIT_FAILURE,
350 _("profile address out of range. Wrong map file?"));
351
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);
356 header_printed = 1;
357 }
358 printf("\t%llx\t%u\n", (indx - 1) * step + add0,
359 buf[indx]);
360 }
361 this += buf[indx++];
362 }
363 total += this;
364
365 if (optBins) {
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) {
370 if (optVerbose)
371 printf("%016llx %-40s %6u %8.4f\n", fn_add,
372 fn_name, this, this / (double)fn_len);
373 else
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;
378
379 for (scan = (fn_add - add0) / step + 1;
380 scan < (next_add - add0) / step;
381 scan++) {
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,
386 buf[scan]);
387 }
388 }
389 }
390
391 fn_add = next_add;
392 strcpy(fn_name, next_name);
393
394 maplineno++;
395 if (done)
396 break;
397 }
398
399 /* clock ticks, out of kernel text - probably modules */
400 printf("%6u %s\n", buf[len / sizeof(*buf) - 1], "*unknown*");
401
402 if (fn_add > add0)
403 rep = total / (double)(fn_add - add0);
404
405 /* trailer */
406 if (optVerbose)
407 printf("%016x %-40s %6u %8.4f\n",
408 0, "total", total, rep);
409 else
410 printf("%6u %-40s %8.4f\n",
411 total, _("total"), rep);
412
413 popenMap ? pclose(map) : fclose(map);
414 exit(EXIT_SUCCESS);
415 }