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