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