(root)/
util-linux-2.39/
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  	printf(USAGE_HELP_OPTIONS(27));
     128  	printf(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  }