|
|
1.1 root 1: /*
2: * Copyright (c) 1983 Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms are permitted
6: * provided that: (1) source distributions retain this entire copyright
7: * notice and comment, and (2) distributions including binaries display
8: * the following acknowledgement: ``This product includes software
9: * developed by the University of California, Berkeley and its contributors''
10: * in the documentation or other materials provided with the distribution
11: * and in all advertising materials mentioning features or use of this
12: * software. Neither the name of the University nor the names of its
13: * contributors may be used to endorse or promote products derived
14: * from this software without specific prior written permission.
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18: */
19:
20: #ifndef lint
21: char copyright[] =
22: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
23: All rights reserved.\n";
24: #endif /* not lint */
25:
26: #ifndef lint
27: static char sccsid[] = "@(#)gprof.c 5.6 (Berkeley) 6/1/90";
28: #endif /* not lint */
29:
30: #include "gprof.h"
31:
32: char *whoami = "gprof";
33:
34: /*
35: * things which get -E excluded by default.
36: */
37: char *defaultEs[] = { "mcount" , "__mcleanup" , 0 };
38:
39: main(argc, argv)
40: int argc;
41: char **argv;
42: {
43: char **sp;
44: nltype **timesortnlp;
45:
46: --argc;
47: argv++;
48: debug = 0;
49: bflag = TRUE;
50: while ( *argv != 0 && **argv == '-' ) {
51: (*argv)++;
52: switch ( **argv ) {
53: case 'a':
54: aflag = TRUE;
55: break;
56: case 'b':
57: bflag = FALSE;
58: break;
59: case 'c':
60: cflag = TRUE;
61: break;
62: case 'd':
63: dflag = TRUE;
64: (*argv)++;
65: debug |= atoi( *argv );
66: debug |= ANYDEBUG;
67: # ifdef DEBUG
68: printf("[main] debug = %d\n", debug);
69: # else not DEBUG
70: printf("%s: -d ignored\n", whoami);
71: # endif DEBUG
72: break;
73: case 'E':
74: ++argv;
75: addlist( Elist , *argv );
76: Eflag = TRUE;
77: addlist( elist , *argv );
78: eflag = TRUE;
79: break;
80: case 'e':
81: addlist( elist , *++argv );
82: eflag = TRUE;
83: break;
84: case 'F':
85: ++argv;
86: addlist( Flist , *argv );
87: Fflag = TRUE;
88: addlist( flist , *argv );
89: fflag = TRUE;
90: break;
91: case 'f':
92: addlist( flist , *++argv );
93: fflag = TRUE;
94: break;
95: case 'k':
96: addlist( kfromlist , *++argv );
97: addlist( ktolist , *++argv );
98: kflag = TRUE;
99: break;
100: case 's':
101: sflag = TRUE;
102: break;
103: case 'z':
104: zflag = TRUE;
105: break;
106: }
107: argv++;
108: }
109: if ( *argv != 0 ) {
110: a_outname = *argv;
111: argv++;
112: } else {
113: a_outname = A_OUTNAME;
114: }
115: if ( *argv != 0 ) {
116: gmonname = *argv;
117: argv++;
118: } else {
119: gmonname = GMONNAME;
120: }
121: /*
122: * turn off default functions
123: */
124: for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
125: Eflag = TRUE;
126: addlist( Elist , *sp );
127: eflag = TRUE;
128: addlist( elist , *sp );
129: }
130: /*
131: * how many ticks per second?
132: * if we can't tell, report time in ticks.
133: */
134: hz = hertz();
135: if (hz == 0) {
136: hz = 1;
137: fprintf(stderr, "time is in ticks, not seconds\n");
138: }
139: /*
140: * get information about a.out file.
141: */
142: getnfile();
143: /*
144: * get information about mon.out file(s).
145: */
146: do {
147: getpfile( gmonname );
148: if ( *argv != 0 ) {
149: gmonname = *argv;
150: }
151: } while ( *argv++ != 0 );
152: /*
153: * dump out a gmon.sum file if requested
154: */
155: if ( sflag ) {
156: dumpsum( GMONSUM );
157: }
158: /*
159: * assign samples to procedures
160: */
161: asgnsamples();
162: /*
163: * assemble the dynamic profile
164: */
165: timesortnlp = doarcs();
166: /*
167: * print the dynamic profile
168: */
169: printgprof( timesortnlp );
170: /*
171: * print the flat profile
172: */
173: printprof();
174: /*
175: * print the index
176: */
177: printindex();
178: done();
179: }
180:
181: /*
182: * Set up string and symbol tables from a.out.
183: * and optionally the text space.
184: * On return symbol table is sorted by value.
185: */
186: getnfile()
187: {
188: FILE *nfile;
189: int valcmp();
190:
191: nfile = fopen( a_outname ,"r");
192: if (nfile == NULL) {
193: perror( a_outname );
194: done();
195: }
196: fread(&xbuf, 1, sizeof(xbuf), nfile);
197: if (N_BADMAG(xbuf)) {
198: fprintf(stderr, "%s: %s: bad format\n", whoami , a_outname );
199: done();
200: }
201: getstrtab(nfile);
202: getsymtab(nfile);
203: gettextspace( nfile );
204: qsort(nl, nname, sizeof(nltype), valcmp);
205: fclose(nfile);
206: # ifdef DEBUG
207: if ( debug & AOUTDEBUG ) {
208: register int j;
209:
210: for (j = 0; j < nname; j++){
211: printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name);
212: }
213: }
214: # endif DEBUG
215: }
216:
217: getstrtab(nfile)
218: FILE *nfile;
219: {
220:
221: fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
222: if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
223: fprintf(stderr, "%s: %s: no string table (old format?)\n" ,
224: whoami , a_outname );
225: done();
226: }
227: strtab = (char *)calloc(ssiz, 1);
228: if (strtab == NULL) {
229: fprintf(stderr, "%s: %s: no room for %d bytes of string table",
230: whoami , a_outname , ssiz);
231: done();
232: }
233: if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
234: fprintf(stderr, "%s: %s: error reading string table\n",
235: whoami , a_outname );
236: done();
237: }
238: }
239:
240: /*
241: * Read in symbol table
242: */
243: getsymtab(nfile)
244: FILE *nfile;
245: {
246: register long i;
247: int askfor;
248: struct nlist nbuf;
249:
250: /* pass1 - count symbols */
251: fseek(nfile, (long)N_SYMOFF(xbuf), 0);
252: nname = 0;
253: for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
254: fread(&nbuf, sizeof(nbuf), 1, nfile);
255: if ( ! funcsymbol( &nbuf ) ) {
256: continue;
257: }
258: nname++;
259: }
260: if (nname == 0) {
261: fprintf(stderr, "%s: %s: no symbols\n", whoami , a_outname );
262: done();
263: }
264: askfor = nname + 1;
265: nl = (nltype *) calloc( askfor , sizeof(nltype) );
266: if (nl == 0) {
267: fprintf(stderr, "%s: No room for %d bytes of symbol table\n",
268: whoami, askfor * sizeof(nltype) );
269: done();
270: }
271:
272: /* pass2 - read symbols */
273: fseek(nfile, (long)N_SYMOFF(xbuf), 0);
274: npe = nl;
275: nname = 0;
276: for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
277: fread(&nbuf, sizeof(nbuf), 1, nfile);
278: if ( ! funcsymbol( &nbuf ) ) {
279: # ifdef DEBUG
280: if ( debug & AOUTDEBUG ) {
281: printf( "[getsymtab] rejecting: 0x%x %s\n" ,
282: nbuf.n_type , strtab + nbuf.n_un.n_strx );
283: }
284: # endif DEBUG
285: continue;
286: }
287: npe->value = nbuf.n_value;
288: npe->name = strtab+nbuf.n_un.n_strx;
289: # ifdef DEBUG
290: if ( debug & AOUTDEBUG ) {
291: printf( "[getsymtab] %d %s 0x%08x\n" ,
292: nname , npe -> name , npe -> value );
293: }
294: # endif DEBUG
295: npe++;
296: nname++;
297: }
298: npe->value = -1;
299: }
300:
301: /*
302: * read in the text space of an a.out file
303: */
304: gettextspace( nfile )
305: FILE *nfile;
306: {
307: char *malloc();
308:
309: if ( cflag == 0 ) {
310: return;
311: }
312: textspace = (u_char *) malloc( xbuf.a_text );
313: if ( textspace == 0 ) {
314: fprintf( stderr , "%s: ran out room for %d bytes of text space: " ,
315: whoami , xbuf.a_text );
316: fprintf( stderr , "can't do -c\n" );
317: return;
318: }
319: (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 );
320: if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) {
321: fprintf( stderr , "%s: couldn't read text space: " , whoami );
322: fprintf( stderr , "can't do -c\n" );
323: free( textspace );
324: textspace = 0;
325: return;
326: }
327: }
328: /*
329: * information from a gmon.out file is in two parts:
330: * an array of sampling hits within pc ranges,
331: * and the arcs.
332: */
333: getpfile(filename)
334: char *filename;
335: {
336: FILE *pfile;
337: FILE *openpfile();
338: struct rawarc arc;
339:
340: pfile = openpfile(filename);
341: readsamples(pfile);
342: /*
343: * the rest of the file consists of
344: * a bunch of <from,self,count> tuples.
345: */
346: while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
347: # ifdef DEBUG
348: if ( debug & SAMPLEDEBUG ) {
349: printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" ,
350: arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
351: }
352: # endif DEBUG
353: /*
354: * add this arc
355: */
356: tally( &arc );
357: }
358: fclose(pfile);
359: }
360:
361: FILE *
362: openpfile(filename)
363: char *filename;
364: {
365: struct hdr tmp;
366: FILE *pfile;
367:
368: if((pfile = fopen(filename, "r")) == NULL) {
369: perror(filename);
370: done();
371: }
372: fread(&tmp, sizeof(struct hdr), 1, pfile);
373: if ( s_highpc != 0 && ( tmp.lowpc != h.lowpc ||
374: tmp.highpc != h.highpc || tmp.ncnt != h.ncnt ) ) {
375: fprintf(stderr, "%s: incompatible with first gmon file\n", filename);
376: done();
377: }
378: h = tmp;
379: s_lowpc = (unsigned long) h.lowpc;
380: s_highpc = (unsigned long) h.highpc;
381: lowpc = (unsigned long)h.lowpc / sizeof(UNIT);
382: highpc = (unsigned long)h.highpc / sizeof(UNIT);
383: sampbytes = h.ncnt - sizeof(struct hdr);
384: nsamples = sampbytes / sizeof (UNIT);
385: # ifdef DEBUG
386: if ( debug & SAMPLEDEBUG ) {
387: printf( "[openpfile] hdr.lowpc 0x%x hdr.highpc 0x%x hdr.ncnt %d\n",
388: h.lowpc , h.highpc , h.ncnt );
389: printf( "[openpfile] s_lowpc 0x%x s_highpc 0x%x\n" ,
390: s_lowpc , s_highpc );
391: printf( "[openpfile] lowpc 0x%x highpc 0x%x\n" ,
392: lowpc , highpc );
393: printf( "[openpfile] sampbytes %d nsamples %d\n" ,
394: sampbytes , nsamples );
395: }
396: # endif DEBUG
397: return(pfile);
398: }
399:
400: tally( rawp )
401: struct rawarc *rawp;
402: {
403: nltype *parentp;
404: nltype *childp;
405:
406: parentp = nllookup( rawp -> raw_frompc );
407: childp = nllookup( rawp -> raw_selfpc );
408: if ( kflag
409: && onlist( kfromlist , parentp -> name )
410: && onlist( ktolist , childp -> name ) ) {
411: return;
412: }
413: childp -> ncall += rawp -> raw_count;
414: # ifdef DEBUG
415: if ( debug & TALLYDEBUG ) {
416: printf( "[tally] arc from %s to %s traversed %d times\n" ,
417: parentp -> name , childp -> name , rawp -> raw_count );
418: }
419: # endif DEBUG
420: addarc( parentp , childp , rawp -> raw_count );
421: }
422:
423: /*
424: * dump out the gmon.sum file
425: */
426: dumpsum( sumfile )
427: char *sumfile;
428: {
429: register nltype *nlp;
430: register arctype *arcp;
431: struct rawarc arc;
432: FILE *sfile;
433:
434: if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) {
435: perror( sumfile );
436: done();
437: }
438: /*
439: * dump the header; use the last header read in
440: */
441: if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) {
442: perror( sumfile );
443: done();
444: }
445: /*
446: * dump the samples
447: */
448: if (fwrite(samples, sizeof (UNIT), nsamples, sfile) != nsamples) {
449: perror( sumfile );
450: done();
451: }
452: /*
453: * dump the normalized raw arc information
454: */
455: for ( nlp = nl ; nlp < npe ; nlp++ ) {
456: for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
457: arc.raw_frompc = arcp -> arc_parentp -> value;
458: arc.raw_selfpc = arcp -> arc_childp -> value;
459: arc.raw_count = arcp -> arc_count;
460: if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) {
461: perror( sumfile );
462: done();
463: }
464: # ifdef DEBUG
465: if ( debug & SAMPLEDEBUG ) {
466: printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" ,
467: arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
468: }
469: # endif DEBUG
470: }
471: }
472: fclose( sfile );
473: }
474:
475: valcmp(p1, p2)
476: nltype *p1, *p2;
477: {
478: if ( p1 -> value < p2 -> value ) {
479: return LESSTHAN;
480: }
481: if ( p1 -> value > p2 -> value ) {
482: return GREATERTHAN;
483: }
484: return EQUALTO;
485: }
486:
487: readsamples(pfile)
488: FILE *pfile;
489: {
490: register i;
491: UNIT sample;
492:
493: if (samples == 0) {
494: samples = (UNIT *) calloc(sampbytes, sizeof (UNIT));
495: if (samples == 0) {
496: fprintf( stderr , "%s: No room for %d sample pc's\n",
497: whoami , sampbytes / sizeof (UNIT));
498: done();
499: }
500: }
501: for (i = 0; i < nsamples; i++) {
502: fread(&sample, sizeof (UNIT), 1, pfile);
503: if (feof(pfile))
504: break;
505: samples[i] += sample;
506: }
507: if (i != nsamples) {
508: fprintf(stderr,
509: "%s: unexpected EOF after reading %d/%d samples\n",
510: whoami , --i , nsamples );
511: done();
512: }
513: }
514:
515: /*
516: * Assign samples to the procedures to which they belong.
517: *
518: * There are three cases as to where pcl and pch can be
519: * with respect to the routine entry addresses svalue0 and svalue1
520: * as shown in the following diagram. overlap computes the
521: * distance between the arrows, the fraction of the sample
522: * that is to be credited to the routine which starts at svalue0.
523: *
524: * svalue0 svalue1
525: * | |
526: * v v
527: *
528: * +-----------------------------------------------+
529: * | |
530: * | ->| |<- ->| |<- ->| |<- |
531: * | | | | | |
532: * +---------+ +---------+ +---------+
533: *
534: * ^ ^ ^ ^ ^ ^
535: * | | | | | |
536: * pcl pch pcl pch pcl pch
537: *
538: * For the vax we assert that samples will never fall in the first
539: * two bytes of any routine, since that is the entry mask,
540: * thus we give call alignentries() to adjust the entry points if
541: * the entry mask falls in one bucket but the code for the routine
542: * doesn't start until the next bucket. In conjunction with the
543: * alignment of routine addresses, this should allow us to have
544: * only one sample for every four bytes of text space and never
545: * have any overlap (the two end cases, above).
546: */
547: asgnsamples()
548: {
549: register int j;
550: UNIT ccnt;
551: double time;
552: unsigned long pcl, pch;
553: register int i;
554: unsigned long overlap;
555: unsigned long svalue0, svalue1;
556:
557: /* read samples and assign to namelist symbols */
558: scale = highpc - lowpc;
559: scale /= nsamples;
560: alignentries();
561: for (i = 0, j = 1; i < nsamples; i++) {
562: ccnt = samples[i];
563: if (ccnt == 0)
564: continue;
565: pcl = lowpc + scale * i;
566: pch = lowpc + scale * (i + 1);
567: time = ccnt;
568: # ifdef DEBUG
569: if ( debug & SAMPLEDEBUG ) {
570: printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" ,
571: pcl , pch , ccnt );
572: }
573: # endif DEBUG
574: totime += time;
575: for (j = j - 1; j < nname; j++) {
576: svalue0 = nl[j].svalue;
577: svalue1 = nl[j+1].svalue;
578: /*
579: * if high end of tick is below entry address,
580: * go for next tick.
581: */
582: if (pch < svalue0)
583: break;
584: /*
585: * if low end of tick into next routine,
586: * go for next routine.
587: */
588: if (pcl >= svalue1)
589: continue;
590: overlap = min(pch, svalue1) - max(pcl, svalue0);
591: if (overlap > 0) {
592: # ifdef DEBUG
593: if (debug & SAMPLEDEBUG) {
594: printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f ticks %d overlap\n",
595: nl[j].value/sizeof(UNIT), svalue0, svalue1,
596: nl[j].name,
597: overlap * time / scale, overlap);
598: }
599: # endif DEBUG
600: nl[j].time += overlap * time / scale;
601: }
602: }
603: }
604: # ifdef DEBUG
605: if (debug & SAMPLEDEBUG) {
606: printf("[asgnsamples] totime %f\n", totime);
607: }
608: # endif DEBUG
609: }
610:
611:
612: unsigned long
613: min(a, b)
614: unsigned long a,b;
615: {
616: if (a<b)
617: return(a);
618: return(b);
619: }
620:
621: unsigned long
622: max(a, b)
623: unsigned long a,b;
624: {
625: if (a>b)
626: return(a);
627: return(b);
628: }
629:
630: /*
631: * calculate scaled entry point addresses (to save time in asgnsamples),
632: * and possibly push the scaled entry points over the entry mask,
633: * if it turns out that the entry point is in one bucket and the code
634: * for a routine is in the next bucket.
635: */
636: alignentries()
637: {
638: register struct nl *nlp;
639: unsigned long bucket_of_entry;
640: unsigned long bucket_of_code;
641:
642: for (nlp = nl; nlp < npe; nlp++) {
643: nlp -> svalue = nlp -> value / sizeof(UNIT);
644: bucket_of_entry = (nlp->svalue - lowpc) / scale;
645: bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
646: if (bucket_of_entry < bucket_of_code) {
647: # ifdef DEBUG
648: if (debug & SAMPLEDEBUG) {
649: printf("[alignentries] pushing svalue 0x%x to 0x%x\n",
650: nlp->svalue, nlp->svalue + UNITS_TO_CODE);
651: }
652: # endif DEBUG
653: nlp->svalue += UNITS_TO_CODE;
654: }
655: }
656: }
657:
658: bool
659: funcsymbol( nlistp )
660: struct nlist *nlistp;
661: {
662: extern char *strtab; /* string table from a.out */
663: extern int aflag; /* if static functions aren't desired */
664: char *name;
665:
666: /*
667: * must be a text symbol,
668: * and static text symbols don't qualify if aflag set.
669: */
670: if ( ! ( ( nlistp -> n_type == ( N_TEXT | N_EXT ) )
671: || ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) {
672: return FALSE;
673: }
674: /*
675: * can't have any `funny' characters in name,
676: * where `funny' includes `.', .o file names
677: * and `$', pascal labels.
678: */
679: for ( name = strtab + nlistp -> n_un.n_strx ; *name ; name += 1 ) {
680: if ( *name == '.' || *name == '$' ) {
681: return FALSE;
682: }
683: }
684: return TRUE;
685: }
686:
687: done()
688: {
689:
690: exit(0);
691: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.