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