Annotation of src/usr.bin/gprof/gprof.c, Revision 1.23
1.23 ! pascal 1: /* $OpenBSD: gprof.c,v 1.22 2015/08/20 22:32:41 deraadt Exp $ */
1.1 deraadt 2: /* $NetBSD: gprof.c,v 1.8 1995/04/19 07:15:59 cgd Exp $ */
3:
4: /*
5: * Copyright (c) 1983, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
1.12 millert 16: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 17: * may be used to endorse or promote products derived from this software
18: * without specific prior written permission.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30: * SUCH DAMAGE.
31: */
32:
33: #include "gprof.h"
34:
1.11 art 35: int valcmp(const void *, const void *);
1.1 deraadt 36:
37: static struct gmonhdr gmonhdr;
1.8 mickey 38: extern char *__progname;
1.1 deraadt 39:
1.7 mickey 40: int
1.13 deraadt 41: main(int argc, char *argv[])
1.1 deraadt 42: {
43: char **sp;
44: nltype **timesortnlp;
1.11 art 45: char **defaultEs;
1.1 deraadt 46:
1.23 ! pascal 47: if (pledge("stdio rpath wpath cpath", NULL) == -1)
! 48: err(1, NULL);
! 49:
1.1 deraadt 50: --argc;
51: argv++;
52: debug = 0;
53: bflag = TRUE;
54: while ( *argv != 0 && **argv == '-' ) {
55: (*argv)++;
56: switch ( **argv ) {
57: case 'a':
58: aflag = TRUE;
59: break;
60: case 'b':
61: bflag = FALSE;
62: break;
63: case 'C':
64: Cflag = TRUE;
65: cyclethreshold = atoi( *++argv );
66: break;
67: case 'c':
1.21 tedu 68: #if defined(__i386__) || defined(__vax__) || \
1.18 deraadt 69: defined(__sparc__) || defined(__sparc64__)
1.1 deraadt 70: cflag = TRUE;
71: #else
1.7 mickey 72: fprintf(stderr, "%s: -c isn't supported on this architecture yet\n", __progname);
1.1 deraadt 73: exit(1);
74: #endif
75: break;
76: case 'd':
77: dflag = TRUE;
1.20 millert 78: setvbuf(stdout, NULL, _IOLBF, 0);
1.1 deraadt 79: debug |= atoi( *++argv );
80: debug |= ANYDEBUG;
81: # ifdef DEBUG
82: printf("[main] debug = %d\n", debug);
1.10 danh 83: # else /* not DEBUG */
1.7 mickey 84: warnx("-d ignored");
1.10 danh 85: # endif /* DEBUG */
1.1 deraadt 86: break;
87: case 'E':
88: ++argv;
89: addlist( Elist , *argv );
90: Eflag = TRUE;
91: addlist( elist , *argv );
92: eflag = TRUE;
93: break;
94: case 'e':
95: addlist( elist , *++argv );
96: eflag = TRUE;
97: break;
98: case 'F':
99: ++argv;
100: addlist( Flist , *argv );
101: Fflag = TRUE;
102: addlist( flist , *argv );
103: fflag = TRUE;
104: break;
105: case 'f':
106: addlist( flist , *++argv );
107: fflag = TRUE;
108: break;
109: case 'k':
110: addlist( kfromlist , *++argv );
111: addlist( ktolist , *++argv );
112: kflag = TRUE;
113: break;
114: case 's':
115: sflag = TRUE;
116: break;
117: case 'z':
118: zflag = TRUE;
119: break;
120: }
121: argv++;
122: }
123: if ( *argv != 0 ) {
124: a_outname = *argv;
125: argv++;
126: } else {
127: a_outname = A_OUTNAME;
128: }
129: if ( *argv != 0 ) {
130: gmonname = *argv;
131: argv++;
132: } else {
133: gmonname = GMONNAME;
1.23 ! pascal 134: }
! 135: if ( sflag == FALSE ) {
! 136: if (pledge("stdio rpath", NULL) == -1)
! 137: err(1, "pledge");
1.1 deraadt 138: }
139: /*
1.11 art 140: * get information about a.out file.
141: */
142: if (getnfile(a_outname, &defaultEs) == -1)
143: errx(1, "%s: bad format", a_outname);
144: /*
145: * sort symbol table.
146: */
147: qsort(nl, nname, sizeof(nltype), valcmp);
148: /*
1.1 deraadt 149: * turn off default functions
150: */
151: for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
152: Eflag = TRUE;
153: addlist( Elist , *sp );
154: eflag = TRUE;
155: addlist( elist , *sp );
156: }
157: /*
158: * get information about mon.out file(s).
159: */
160: do {
161: getpfile( gmonname );
162: if ( *argv != 0 ) {
163: gmonname = *argv;
164: }
165: } while ( *argv++ != 0 );
166: /*
167: * how many ticks per second?
168: * if we can't tell, report time in ticks.
169: */
170: if (hz == 0) {
171: hz = 1;
1.7 mickey 172: warnx("time is in ticks, not seconds");
1.1 deraadt 173: }
174: /*
175: * dump out a gmon.sum file if requested
176: */
177: if ( sflag ) {
178: dumpsum( GMONSUM );
179: }
180: /*
181: * assign samples to procedures
182: */
183: asgnsamples();
184: /*
185: * assemble the dynamic profile
186: */
187: timesortnlp = doarcs();
188: /*
189: * print the dynamic profile
190: */
191: printgprof( timesortnlp );
192: /*
193: * print the flat profile
194: */
195: printprof();
196: /*
197: * print the index
198: */
199: printindex();
1.7 mickey 200:
201: return (0);
1.1 deraadt 202: }
203:
204: /*
205: * information from a gmon.out file is in two parts:
206: * an array of sampling hits within pc ranges,
207: * and the arcs.
208: */
1.7 mickey 209: void
1.17 espie 210: getpfile(const char *filename)
1.1 deraadt 211: {
212: FILE *pfile;
213: struct rawarc arc;
214:
215: pfile = openpfile(filename);
216: readsamples(pfile);
217: /*
218: * the rest of the file consists of
219: * a bunch of <from,self,count> tuples.
220: */
221: while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
222: # ifdef DEBUG
223: if ( debug & SAMPLEDEBUG ) {
1.14 art 224: printf( "[getpfile] frompc 0x%lx selfpc 0x%lx count %ld\n" ,
1.1 deraadt 225: arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
226: }
1.10 danh 227: # endif /* DEBUG */
1.1 deraadt 228: /*
229: * add this arc
230: */
231: tally( &arc );
232: }
233: fclose(pfile);
234: }
235:
236: FILE *
1.17 espie 237: openpfile(const char *filename)
1.1 deraadt 238: {
239: struct gmonhdr tmp;
240: FILE *pfile;
241: int size;
242: int rate;
243:
1.7 mickey 244: if((pfile = fopen(filename, "r")) == NULL)
245: err(1, "fopen: %s", filename);
1.16 millert 246: if (fread(&tmp, sizeof(struct gmonhdr), 1, pfile) != 1)
247: errx(1, "%s: bad gmon header", filename);
1.1 deraadt 248: if ( s_highpc != 0 && ( tmp.lpc != gmonhdr.lpc ||
1.7 mickey 249: tmp.hpc != gmonhdr.hpc || tmp.ncnt != gmonhdr.ncnt))
250: errx(1, "%s: incompatible with first gmon file", filename);
1.1 deraadt 251: gmonhdr = tmp;
252: if ( gmonhdr.version == GMONVERSION ) {
253: rate = gmonhdr.profrate;
254: size = sizeof(struct gmonhdr);
255: } else {
256: fseek(pfile, sizeof(struct ophdr), SEEK_SET);
257: size = sizeof(struct ophdr);
258: gmonhdr.profrate = rate = hertz();
259: gmonhdr.version = GMONVERSION;
260: }
261: if (hz == 0) {
262: hz = rate;
1.7 mickey 263: } else if (hz != rate)
264: errx(1, "%s: profile clock rate (%d) incompatible with clock rate "
265: "(%ld) in first gmon file", filename, rate, hz);
1.1 deraadt 266: s_lowpc = (unsigned long) gmonhdr.lpc;
267: s_highpc = (unsigned long) gmonhdr.hpc;
268: lowpc = (unsigned long)gmonhdr.lpc / sizeof(UNIT);
269: highpc = (unsigned long)gmonhdr.hpc / sizeof(UNIT);
270: sampbytes = gmonhdr.ncnt - size;
271: nsamples = sampbytes / sizeof (UNIT);
272: # ifdef DEBUG
273: if ( debug & SAMPLEDEBUG ) {
1.14 art 274: printf( "[openpfile] hdr.lpc 0x%lx hdr.hpc 0x%lx hdr.ncnt %d\n",
1.1 deraadt 275: gmonhdr.lpc , gmonhdr.hpc , gmonhdr.ncnt );
1.14 art 276: printf( "[openpfile] s_lowpc 0x%lx s_highpc 0x%lx\n" ,
1.1 deraadt 277: s_lowpc , s_highpc );
1.14 art 278: printf( "[openpfile] lowpc 0x%lx highpc 0x%lx\n" ,
1.1 deraadt 279: lowpc , highpc );
280: printf( "[openpfile] sampbytes %d nsamples %d\n" ,
281: sampbytes , nsamples );
1.14 art 282: printf( "[openpfile] sample rate %ld\n" , hz );
1.1 deraadt 283: }
1.10 danh 284: # endif /* DEBUG */
1.1 deraadt 285: return(pfile);
286: }
287:
1.7 mickey 288: void
1.13 deraadt 289: tally(struct rawarc *rawp)
1.1 deraadt 290: {
291: nltype *parentp;
292: nltype *childp;
293:
294: parentp = nllookup( rawp -> raw_frompc );
295: childp = nllookup( rawp -> raw_selfpc );
296: if ( parentp == 0 || childp == 0 )
297: return;
298: if ( kflag
299: && onlist( kfromlist , parentp -> name )
300: && onlist( ktolist , childp -> name ) ) {
301: return;
302: }
303: childp -> ncall += rawp -> raw_count;
304: # ifdef DEBUG
305: if ( debug & TALLYDEBUG ) {
1.14 art 306: printf( "[tally] arc from %s to %s traversed %ld times\n" ,
1.1 deraadt 307: parentp -> name , childp -> name , rawp -> raw_count );
308: }
1.10 danh 309: # endif /* DEBUG */
1.1 deraadt 310: addarc( parentp , childp , rawp -> raw_count );
311: }
312:
313: /*
314: * dump out the gmon.sum file
315: */
1.7 mickey 316: void
1.17 espie 317: dumpsum(const char *sumfile)
1.1 deraadt 318: {
1.9 mpech 319: nltype *nlp;
320: arctype *arcp;
1.1 deraadt 321: struct rawarc arc;
322: FILE *sfile;
323:
1.7 mickey 324: if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL )
325: err(1, "fopen: %s", sumfile);
1.1 deraadt 326: /*
327: * dump the header; use the last header read in
328: */
1.7 mickey 329: if ( fwrite( &gmonhdr , sizeof gmonhdr , 1 , sfile ) != 1 )
330: err(1, "fwrite: %s", sumfile);
1.1 deraadt 331: /*
332: * dump the samples
333: */
1.7 mickey 334: if (fwrite(samples, sizeof (UNIT), nsamples, sfile) != nsamples)
335: err(1, "fwrite: %s", sumfile);
1.1 deraadt 336: /*
337: * dump the normalized raw arc information
338: */
339: for ( nlp = nl ; nlp < npe ; nlp++ ) {
340: for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
341: arc.raw_frompc = arcp -> arc_parentp -> value;
342: arc.raw_selfpc = arcp -> arc_childp -> value;
343: arc.raw_count = arcp -> arc_count;
1.7 mickey 344: if (fwrite ( &arc , sizeof arc , 1 , sfile ) != 1)
345: err(1, "fwrite: %s", sumfile);
1.1 deraadt 346: # ifdef DEBUG
347: if ( debug & SAMPLEDEBUG ) {
1.14 art 348: printf( "[dumpsum] frompc 0x%lx selfpc 0x%lx count %ld\n" ,
1.1 deraadt 349: arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
350: }
1.10 danh 351: # endif /* DEBUG */
1.1 deraadt 352: }
353: }
354: fclose( sfile );
355: }
356:
1.7 mickey 357: int
1.11 art 358: valcmp(const void *vp1, const void *vp2)
1.1 deraadt 359: {
1.11 art 360: const nltype *p1 = vp1;
361: const nltype *p2 = vp2;
362:
1.1 deraadt 363: if ( p1 -> value < p2 -> value ) {
364: return LESSTHAN;
365: }
366: if ( p1 -> value > p2 -> value ) {
367: return GREATERTHAN;
368: }
369: return EQUALTO;
370: }
371:
1.7 mickey 372: void
1.13 deraadt 373: readsamples(FILE *pfile)
1.1 deraadt 374: {
375: UNIT sample;
1.9 mpech 376: int i;
1.1 deraadt 377:
378: if (samples == 0) {
1.22 deraadt 379: samples = calloc(sampbytes, sizeof (UNIT));
1.7 mickey 380: if (samples == 0)
1.14 art 381: errx(1, "No room for %ld sample pc's", sampbytes / sizeof (UNIT));
1.1 deraadt 382: }
383: for (i = 0; i < nsamples; i++) {
384: fread(&sample, sizeof (UNIT), 1, pfile);
385: if (feof(pfile))
386: break;
387: samples[i] += sample;
388: }
1.7 mickey 389: if (i != nsamples)
390: errx(1, "unexpected EOF after reading %d/%d samples", i, nsamples );
1.1 deraadt 391: }
392:
393: /*
394: * Assign samples to the procedures to which they belong.
395: *
396: * There are three cases as to where pcl and pch can be
397: * with respect to the routine entry addresses svalue0 and svalue1
398: * as shown in the following diagram. overlap computes the
399: * distance between the arrows, the fraction of the sample
400: * that is to be credited to the routine which starts at svalue0.
401: *
402: * svalue0 svalue1
403: * | |
404: * v v
405: *
406: * +-----------------------------------------------+
407: * | |
408: * | ->| |<- ->| |<- ->| |<- |
409: * | | | | | |
410: * +---------+ +---------+ +---------+
411: *
412: * ^ ^ ^ ^ ^ ^
413: * | | | | | |
414: * pcl pch pcl pch pcl pch
415: *
416: * For the vax we assert that samples will never fall in the first
417: * two bytes of any routine, since that is the entry mask,
418: * thus we give call alignentries() to adjust the entry points if
419: * the entry mask falls in one bucket but the code for the routine
420: * doesn't start until the next bucket. In conjunction with the
421: * alignment of routine addresses, this should allow us to have
422: * only one sample for every four bytes of text space and never
423: * have any overlap (the two end cases, above).
424: */
1.7 mickey 425: void
1.13 deraadt 426: asgnsamples(void)
1.1 deraadt 427: {
1.9 mpech 428: int j;
1.1 deraadt 429: UNIT ccnt;
430: double time;
431: unsigned long pcl, pch;
1.15 art 432: unsigned long i;
1.1 deraadt 433: unsigned long overlap;
434: unsigned long svalue0, svalue1;
435:
436: /* read samples and assign to namelist symbols */
437: scale = highpc - lowpc;
438: scale /= nsamples;
439: alignentries();
440: for (i = 0, j = 1; i < nsamples; i++) {
441: ccnt = samples[i];
442: if (ccnt == 0)
443: continue;
1.15 art 444: pcl = lowpc + (unsigned long)(scale * i);
445: pch = lowpc + (unsigned long)(scale * (i + 1));
1.1 deraadt 446: time = ccnt;
447: # ifdef DEBUG
448: if ( debug & SAMPLEDEBUG ) {
1.14 art 449: printf( "[asgnsamples] pcl 0x%lx pch 0x%lx ccnt %d\n" ,
1.1 deraadt 450: pcl , pch , ccnt );
451: }
1.10 danh 452: # endif /* DEBUG */
1.1 deraadt 453: totime += time;
454: for (j = j - 1; j < nname; j++) {
455: svalue0 = nl[j].svalue;
456: svalue1 = nl[j+1].svalue;
457: /*
458: * if high end of tick is below entry address,
459: * go for next tick.
460: */
461: if (pch < svalue0)
462: break;
463: /*
464: * if low end of tick into next routine,
465: * go for next routine.
466: */
467: if (pcl >= svalue1)
468: continue;
469: overlap = min(pch, svalue1) - max(pcl, svalue0);
470: if (overlap > 0) {
471: # ifdef DEBUG
472: if (debug & SAMPLEDEBUG) {
1.14 art 473: printf("[asgnsamples] (0x%lx->0x%lx-0x%lx) %s gets %f ticks %ld overlap\n",
1.1 deraadt 474: nl[j].value/sizeof(UNIT), svalue0, svalue1,
475: nl[j].name,
476: overlap * time / scale, overlap);
477: }
1.10 danh 478: # endif /* DEBUG */
1.1 deraadt 479: nl[j].time += overlap * time / scale;
480: }
481: }
482: }
483: # ifdef DEBUG
484: if (debug & SAMPLEDEBUG) {
485: printf("[asgnsamples] totime %f\n", totime);
486: }
1.10 danh 487: # endif /* DEBUG */
1.1 deraadt 488: }
489:
490:
491: unsigned long
1.13 deraadt 492: min(unsigned long a, unsigned long b)
1.1 deraadt 493: {
494: if (a<b)
495: return(a);
496: return(b);
497: }
498:
499: unsigned long
1.13 deraadt 500: max(unsigned long a, unsigned long b)
1.1 deraadt 501: {
502: if (a>b)
503: return(a);
504: return(b);
505: }
506:
507: /*
508: * calculate scaled entry point addresses (to save time in asgnsamples),
509: * and possibly push the scaled entry points over the entry mask,
510: * if it turns out that the entry point is in one bucket and the code
511: * for a routine is in the next bucket.
512: */
1.7 mickey 513: void
1.13 deraadt 514: alignentries(void)
1.1 deraadt 515: {
1.9 mpech 516: struct nl *nlp;
1.1 deraadt 517: unsigned long bucket_of_entry;
518: unsigned long bucket_of_code;
519:
520: for (nlp = nl; nlp < npe; nlp++) {
521: nlp -> svalue = nlp -> value / sizeof(UNIT);
522: bucket_of_entry = (nlp->svalue - lowpc) / scale;
523: bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
524: if (bucket_of_entry < bucket_of_code) {
525: # ifdef DEBUG
526: if (debug & SAMPLEDEBUG) {
1.14 art 527: printf("[alignentries] pushing svalue 0x%lx to 0x%lx\n",
1.1 deraadt 528: nlp->svalue, nlp->svalue + UNITS_TO_CODE);
529: }
1.10 danh 530: # endif /* DEBUG */
1.1 deraadt 531: nlp->svalue += UNITS_TO_CODE;
532: }
533: }
534: }