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