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