xref: /original-bsd/usr.bin/gprof/gprof.c (revision 62734ea8)
1 #ifndef lint
2     static	char *sccsid = "@(#)gprof.c	1.18 (Berkeley) 06/21/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 = (unsigned long)h.lowpc / sizeof(UNIT);
331     highpc = (unsigned long)h.highpc / sizeof(UNIT);
332     sampbytes = h.ncnt - sizeof(struct hdr);
333     nsamples = sampbytes / sizeof (unsigned UNIT);
334 #   ifdef DEBUG
335 	if ( debug & SAMPLEDEBUG ) {
336 	    printf( "[openpfile] hdr.lowpc 0x%x hdr.highpc 0x%x hdr.ncnt %d\n",
337 		h.lowpc , h.highpc , h.ncnt );
338 	    printf( "[openpfile]   s_lowpc 0x%x   s_highpc 0x%x\n" ,
339 		s_lowpc , s_highpc );
340 	    printf( "[openpfile]     lowpc 0x%x     highpc 0x%x\n" ,
341 		lowpc , highpc );
342 	    printf( "[openpfile] sampbytes %d nsamples %d\n" ,
343 		sampbytes , nsamples );
344 	}
345 #   endif DEBUG
346     return(pfile);
347 }
348 
349 tally( rawp )
350     struct rawarc	*rawp;
351 {
352     nltype		*parentp;
353     nltype		*childp;
354 
355     parentp = nllookup( rawp -> raw_frompc );
356     childp = nllookup( rawp -> raw_selfpc );
357     childp -> ncall += rawp -> raw_count;
358 #   ifdef DEBUG
359 	if ( debug & TALLYDEBUG ) {
360 	    printf( "[tally] arc from %s to %s traversed %d times\n" ,
361 		    parentp -> name , childp -> name , rawp -> raw_count );
362 	}
363 #   endif DEBUG
364     addarc( parentp , childp , rawp -> raw_count );
365 }
366 
367 /*
368  * dump out the gmon.sum file
369  */
370 dumpsum( sumfile )
371     char *sumfile;
372 {
373     register nltype *nlp;
374     register arctype *arcp;
375     struct rawarc arc;
376     FILE *sfile;
377 
378     if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) {
379 	perror( sumfile );
380 	done();
381     }
382     /*
383      * dump the header; use the last header read in
384      */
385     if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) {
386 	perror( sumfile );
387 	done();
388     }
389     /*
390      * dump the samples
391      */
392     if (fwrite(samples, sizeof(unsigned UNIT), nsamples, sfile) != nsamples) {
393 	perror( sumfile );
394 	done();
395     }
396     /*
397      * dump the normalized raw arc information
398      */
399     for ( nlp = nl ; nlp < npe - 1 ; nlp++ ) {
400 	for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
401 	    arc.raw_frompc = arcp -> arc_parentp -> value;
402 	    arc.raw_selfpc = arcp -> arc_childp -> value;
403 	    arc.raw_count = arcp -> arc_count;
404 	    if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) {
405 		perror( sumfile );
406 		done();
407 	    }
408 #	    ifdef DEBUG
409 		if ( debug & SAMPLEDEBUG ) {
410 		    printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" ,
411 			    arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
412 		}
413 #	    endif DEBUG
414 	}
415     }
416     fclose( sfile );
417 }
418 
419 valcmp(p1, p2)
420     nltype *p1, *p2;
421 {
422     if ( p1 -> value < p2 -> value ) {
423 	return LESSTHAN;
424     }
425     if ( p1 -> value > p2 -> value ) {
426 	return GREATERTHAN;
427     }
428     return EQUALTO;
429 }
430 
431 readsamples(pfile)
432     FILE	*pfile;
433 {
434     register i;
435     unsigned UNIT	sample;
436 
437     if (samples == 0) {
438 	samples = (unsigned UNIT *) calloc(sampbytes, sizeof (unsigned UNIT));
439 	if (samples == 0) {
440 	    fprintf( stderr , "%s: No room for %d sample pc's\n",
441 		whoami , sampbytes / sizeof (unsigned UNIT));
442 	    done();
443 	}
444     }
445     for (i = 0; i < nsamples; i++) {
446 	fread(&sample, sizeof (unsigned UNIT), 1, pfile);
447 	if (feof(pfile))
448 		break;
449 	samples[i] += sample;
450     }
451     if (i != nsamples) {
452 	fprintf(stderr,
453 	    "%s: unexpected EOF after reading %d/%d samples\n",
454 		whoami , --i , nsamples );
455 	done();
456     }
457 }
458 
459 /*
460  * Assign samples to the procedures to which they belong.
461  */
462 asgnsamples()
463 {
464     register int	j;
465     unsigned UNIT	ccnt;
466     double		time;
467     unsigned long	pcl, pch;
468     register int	i;
469     unsigned long	overlap;
470     unsigned long	svalue0, svalue1;
471 
472     /* read samples and assign to namelist symbols */
473     scale = highpc - lowpc;
474     scale /= nsamples;
475     for (i = 0, j = 1; i < nsamples; i++) {
476 	ccnt = samples[i];
477 	if (ccnt == 0)
478 		continue;
479 	pcl = lowpc + scale*i;
480 	pch = lowpc + scale*(i+1);
481 	time = ccnt;
482 #	ifdef DEBUG
483 	    if ( debug & SAMPLEDEBUG ) {
484 		printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" ,
485 			pcl , pch , ccnt );
486 	    }
487 #	endif DEBUG
488 	totime += time;
489 	for (j = j - 1; j < nname; j++) {
490 	    svalue0 = nl[j].value / sizeof(UNIT);
491 	    svalue1 = nl[j+1].value / sizeof(UNIT);
492 	    if (pch < svalue0)
493 		    break;
494 	    if (pcl >= svalue1)
495 		    continue;
496 	    overlap=min(pch,svalue1) - max(pcl,svalue0);
497 	    if (overlap>0) {
498 #		ifdef DEBUG
499 		    if ( debug & SAMPLEDEBUG ) {
500 			printf( "[asgnsamples] (0x%x-0x%x) %s gets %f ticks\n" ,
501 				svalue0 , svalue1 , nl[j].name ,
502 				overlap*time/scale );
503 		    }
504 #		endif DEBUG
505 		nl[j].time += overlap*time/scale;
506 	    }
507 	}
508     }
509 #   ifdef DEBUG
510 	if ( debug & SAMPLEDEBUG ) {
511 	    printf( "[asgnsamples] totime %f\n" , totime );
512 	}
513 #   endif DEBUG
514 }
515 
516 
517 unsigned long
518 min(a, b)
519     unsigned long a,b;
520 {
521     if (a<b)
522 	return(a);
523     return(b);
524 }
525 
526 unsigned long
527 max(a, b)
528     unsigned long a,b;
529 {
530     if (a>b)
531 	return(a);
532     return(b);
533 }
534 
535 bool
536 funcsymbol( nlistp )
537     struct nlist	*nlistp;
538 {
539     extern char	*strtab;	/* string table from a.out */
540     extern int	aflag;		/* if static functions aren't desired */
541     char	*name;
542 
543 	/*
544 	 *	must be a text symbol,
545 	 *	and static text symbols don't qualify if aflag set.
546 	 */
547     if ( ! (  ( nlistp -> n_type == ( N_TEXT | N_EXT ) )
548 	   || ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) {
549 	return FALSE;
550     }
551 	/*
552 	 *	can't have any `funny' characters in name,
553 	 *	where `funny' includes	`.', .o file names
554 	 *			and	`$', pascal labels.
555 	 */
556     for ( name = strtab + nlistp -> n_un.n_strx ; *name ; name += 1 ) {
557 	if ( *name == '.' || *name == '$' ) {
558 	    return FALSE;
559 	}
560     }
561     return TRUE;
562 }
563 
564 done()
565 {
566 
567     exit(0);
568 }
569