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