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