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