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