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