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