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