Upload Tizen:Base source
[framework/base/util-linux-ng.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
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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 <stdio.h>
45 #include <fcntl.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <sys/utsname.h>
52 #include "nls.h"
53
54 #define S_LEN 128
55
56 static char *prgname;
57
58 /* These are the defaults */
59 static char defaultmap[]="/boot/System.map";
60 static char defaultpro[]="/proc/profile";
61 static char optstring[]="M:m:np:itvarVbs";
62
63 static void *
64 xmalloc (size_t size) {
65         void *t;
66
67         if (size == 0)
68                 return NULL;
69
70         t = malloc (size);
71         if (t == NULL) {
72                 fprintf(stderr, _("out of memory"));
73                 exit(1);
74         }
75
76         return t;
77 }
78
79 static FILE *
80 myopen(char *name, char *mode, int *flag) {
81         int len = strlen(name);
82
83         if (!strcmp(name+len-3,".gz")) {
84                 FILE *res;
85                 char *cmdline = xmalloc(len+6);
86                 sprintf(cmdline, "zcat %s", name);
87                 res = popen(cmdline,mode);
88                 free(cmdline);
89                 *flag = 1;
90                 return res;
91         }
92         *flag = 0;
93         return fopen(name,mode);
94 }
95
96 #ifndef BOOT_SYSTEM_MAP
97 #define BOOT_SYSTEM_MAP "/boot/System.map-"
98 #endif
99
100 static char *
101 boot_uname_r_str(void) {
102         struct utsname uname_info;
103         char *s;
104         size_t len;
105
106         if (uname(&uname_info))
107                 return "";
108         len = strlen(BOOT_SYSTEM_MAP) + strlen(uname_info.release) + 1;
109         s = xmalloc(len);
110         strcpy(s, BOOT_SYSTEM_MAP);
111         strcat(s, uname_info.release);
112         return s;
113 }
114
115 static void
116 usage(void) {
117         fprintf(stderr, _(
118                 "%s: Usage: \"%s [options]\n"
119                 "\t -m <mapfile>  (defaults: \"%s\" and\n\t\t\t\t  \"%s\")\n"
120                 "\t -p <pro-file> (default: \"%s\")\n"
121                 "\t -M <mult>     set the profiling multiplier to <mult>\n"
122                 "\t -i            print only info about the sampling step\n"
123                 "\t -v            print verbose data\n"
124                 "\t -a            print all symbols, even if count is 0\n"
125                 "\t -b            print individual histogram-bin counts\n"
126                 "\t -s            print individual counters within functions\n"
127                 "\t -r            reset all the counters (root only)\n"
128                 "\t -n            disable byte order auto-detection\n"
129                 "\t -V            print version and exit\n"),
130                 prgname, prgname, defaultmap, boot_uname_r_str(), defaultpro);
131         exit(1);
132 }
133
134 int
135 main(int argc, char **argv) {
136         FILE *map;
137         int proFd;
138         char *mapFile, *proFile, *mult=0;
139         unsigned long len=0, indx=1;
140         unsigned long long add0=0;
141         unsigned int step;
142         unsigned int *buf, total, fn_len;
143         unsigned long long fn_add, next_add;          /* current and next address */
144         char fn_name[S_LEN], next_name[S_LEN];   /* current and next name */
145         char mode[8];
146         int c;
147         int optAll=0, optInfo=0, optReset=0, optVerbose=0, optNative=0;
148         int optBins=0, optSub=0;
149         char mapline[S_LEN];
150         int maplineno=1;
151         int popenMap;   /* flag to tell if popen() has been used */
152         int header_printed;
153
154 #define next (current^1)
155
156         setlocale(LC_ALL, "");
157         bindtextdomain(PACKAGE, LOCALEDIR);
158         textdomain(PACKAGE);
159
160         prgname = argv[0];
161         proFile = defaultpro;
162         mapFile = defaultmap;
163
164         while ((c = getopt(argc, argv, optstring)) != -1) {
165                 switch(c) {
166                 case 'm':
167                         mapFile = optarg;
168                         break;
169                 case 'n':
170                         optNative++;
171                         break;
172                 case 'p':
173                         proFile = optarg;
174                         break;
175                 case 'a':
176                         optAll++;
177                         break;
178                 case 'b':
179                         optBins++;
180                         break;
181                 case 's':
182                         optSub++;
183                         break;
184                 case 'i':
185                         optInfo++;
186                         break;
187                 case 'M':
188                         mult = optarg;
189                         break;
190                 case 'r':
191                         optReset++;
192                         break;
193                 case 'v':
194                         optVerbose++;
195                         break;
196                 case 'V':
197                         printf(_("%s (%s)\n"), prgname,
198                                PACKAGE_STRING);
199                         exit(0);
200                 default:
201                         usage();
202                 }
203         }
204
205         if (optReset || mult) {
206                 int multiplier, fd, to_write;
207
208                 /*
209                  * When writing the multiplier, if the length of the write is
210                  * not sizeof(int), the multiplier is not changed
211                  */
212                 if (mult) {
213                         multiplier = strtoul(mult, 0, 10);
214                         to_write = sizeof(int);
215                 } else {
216                         multiplier = 0;
217                         to_write = 1;   /* sth different from sizeof(int) */
218                 }
219                 /* try to become root, just in case */
220                 setuid(0);
221                 fd = open(defaultpro,O_WRONLY);
222                 if (fd < 0) {
223                         perror(defaultpro);
224                         exit(1);
225                 }
226                 if (write(fd, &multiplier, to_write) != to_write) {
227                         fprintf(stderr, _("readprofile: error writing %s: %s\n"),
228                                 defaultpro, strerror(errno));
229                         exit(1);
230                 }
231                 close(fd);
232                 exit(0);
233         }
234
235         /*
236          * Use an fd for the profiling buffer, to skip stdio overhead
237          */
238         if (((proFd=open(proFile,O_RDONLY)) < 0)
239             || ((int)(len=lseek(proFd,0,SEEK_END)) < 0)
240             || (lseek(proFd,0,SEEK_SET) < 0)) {
241                 fprintf(stderr,"%s: %s: %s\n",prgname,proFile,strerror(errno));
242                 exit(1);
243         }
244
245         if (!(buf=malloc(len))) {
246                 fprintf(stderr,"%s: malloc(): %s\n", prgname, strerror(errno));
247                 exit(1);
248         }
249
250         if (read(proFd,buf,len) != len) {
251                 fprintf(stderr,"%s: %s: %s\n",prgname,proFile,strerror(errno));
252                 exit(1);
253         }
254         close(proFd);
255
256         if (!optNative) {
257                 int entries = len/sizeof(*buf);
258                 int big = 0,small = 0,i;
259                 unsigned *p;
260
261                 for (p = buf+1; p < buf+entries; p++) {
262                         if (*p & ~0U << (sizeof(*buf)*4))
263                                 big++;
264                         if (*p & ((1 << (sizeof(*buf)*4))-1))
265                                 small++;
266                 }
267                 if (big > small) {
268                         fprintf(stderr,_("Assuming reversed byte order. "
269                                 "Use -n to force native byte order.\n"));
270                         for (p = buf; p < buf+entries; p++)
271                                 for (i = 0; i < sizeof(*buf)/2; i++) {
272                                         unsigned char *b = (unsigned char *) p;
273                                         unsigned char tmp;
274
275                                         tmp = b[i];
276                                         b[i] = b[sizeof(*buf)-i-1];
277                                         b[sizeof(*buf)-i-1] = tmp;
278                                 }
279                 }
280         }
281
282         step = buf[0];
283         if (optInfo) {
284                 printf(_("Sampling_step: %i\n"), step);
285                 exit(0);
286         } 
287
288         total = 0;
289
290         map = myopen(mapFile, "r", &popenMap);
291         if (map == NULL && mapFile == defaultmap) {
292                 mapFile = boot_uname_r_str();
293                 map = myopen(mapFile, "r", &popenMap);
294         }
295         if (map == NULL) {
296                 int errsv = errno;
297                 fprintf(stderr, "%s: ", prgname);
298                 errno = errsv;
299                 perror(mapFile);
300                 exit(1);
301         }
302
303         while (fgets(mapline,S_LEN,map)) {
304                 if (sscanf(mapline,"%llx %s %s",&fn_add,mode,fn_name) != 3) {
305                         fprintf(stderr,_("%s: %s(%i): wrong map line\n"),
306                                 prgname, mapFile, maplineno);
307                         exit(1);
308                 }
309                 /* only elf works like this */
310                 if (!strcmp(fn_name,"_stext") || !strcmp(fn_name,"__stext")) {
311                         add0 = fn_add;
312                         break;
313                 }
314                 maplineno++;
315         }
316
317         if (!add0) {
318                 fprintf(stderr,_("%s: can't find \"_stext\" in %s\n"),
319                         prgname, mapFile);
320                 exit(1);
321         }
322
323         /*
324          * Main loop.
325          */
326         while (fgets(mapline,S_LEN,map)) {
327                 unsigned int this=0;
328                 int done = 0;
329
330                 if (sscanf(mapline,"%llx %s %s",&next_add,mode,next_name)!=3) {
331                         fprintf(stderr,_("%s: %s(%i): wrong map line\n"),
332                                 prgname,mapFile, maplineno);
333                         exit(1);
334                 }
335                 header_printed = 0;
336
337                 /* the kernel only profiles up to _etext */
338                 if (!strcmp(next_name, "_etext") ||
339                     !strcmp(next_name, "__etext"))
340                         done = 1;
341                 else {
342                         /* ignore any LEADING (before a '[tT]' symbol is found)
343                            Absolute symbols */
344                         if ((*mode == 'A' || *mode == '?') && total == 0)
345                                 continue;
346                         if (*mode != 'T' && *mode != 't' &&
347                             *mode != 'W' && *mode != 'w')
348                                 break;  /* only text is profiled */
349                 }
350
351                 if (indx >= len / sizeof(*buf)) {
352                         fprintf(stderr, _("%s: profile address out of range. "
353                                           "Wrong map file?\n"), prgname);
354                         exit(1);
355                 }
356
357                 while (indx < (next_add-add0)/step) {
358                         if (optBins && (buf[indx] || optAll)) {
359                                 if (!header_printed) {
360                                         printf ("%s:\n", fn_name);
361                                         header_printed = 1;
362                                 }
363                                 printf ("\t%llx\t%u\n", (indx - 1)*step + add0, buf[indx]);
364                         }
365                         this += buf[indx++];
366                 }
367                 total += this;
368
369                 if (optBins) {
370                         if (optVerbose || this > 0)
371                                 printf ("  total\t\t\t\t%u\n", this);
372                 } else if ((this || optAll) &&
373                            (fn_len = next_add-fn_add) != 0) {
374                         if (optVerbose)
375                                 printf("%016llx %-40s %6i %8.4f\n", fn_add,
376                                        fn_name,this,this/(double)fn_len);
377                         else
378                                 printf("%6i %-40s %8.4f\n",
379                                        this,fn_name,this/(double)fn_len);
380                         if (optSub) {
381                                 unsigned long long scan;
382
383                                 for (scan = (fn_add-add0)/step + 1;
384                                      scan < (next_add-add0)/step; scan++) {
385                                         unsigned long long addr;
386
387                                         addr = (scan - 1)*step + add0;
388                                         printf("\t%#llx\t%s+%#llx\t%u\n",
389                                                addr, fn_name, addr - fn_add,
390                                                buf[scan]);
391                                 }
392                         }
393                 }
394
395                 fn_add = next_add;
396                 strcpy(fn_name,next_name);
397
398                 maplineno++;
399                 if (done)
400                         break;
401         }
402
403         /* clock ticks, out of kernel text - probably modules */
404         printf("%6i %s\n", buf[len/sizeof(*buf)-1], "*unknown*");
405
406         /* trailer */
407         if (optVerbose)
408                 printf("%016x %-40s %6i %8.4f\n",
409                        0,"total",total,total/(double)(fn_add-add0));
410         else
411                 printf("%6i %-40s %8.4f\n",
412                        total,_("total"),total/(double)(fn_add-add0));
413         
414         popenMap ? pclose(map) : fclose(map);
415         exit(0);
416 }