xref: /freebsd/usr.bin/gprof/gprof.c (revision 38effe887ee979f91ad5abf42a2291558e7ff8d1)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1983, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #ifndef lint
33 static const char copyright[] =
34 "@(#) Copyright (c) 1983, 1993\n\
35 	The Regents of the University of California.  All rights reserved.\n";
36 #endif /* not lint */
37 
38 #if 0
39 #ifndef lint
40 static char sccsid[] = "@(#)gprof.c	8.1 (Berkeley) 6/6/93";
41 #endif /* not lint */
42 #endif
43 
44 #include <sys/cdefs.h>
45 __FBSDID("$FreeBSD$");
46 
47 #include <err.h>
48 #include <limits.h>
49 #include <stdint.h>
50 #include <string.h>
51 
52 #include "gprof.h"
53 
54 static int valcmp(const void *, const void *);
55 
56 static struct gmonhdr	gmonhdr;
57 static int lflag;
58 static int Lflag;
59 
60 int
61 main(int argc, char **argv)
62 {
63     char	**sp;
64     nltype	**timesortnlp;
65     char	**defaultEs;
66 
67     --argc;
68     argv++;
69     debug = 0;
70     bflag = TRUE;
71     while ( *argv != 0 && **argv == '-' ) {
72 	(*argv)++;
73 	switch ( **argv ) {
74 	case 'a':
75 	    aflag = TRUE;
76 	    break;
77 	case 'b':
78 	    bflag = FALSE;
79 	    break;
80 	case 'C':
81 	    Cflag = TRUE;
82 	    cyclethreshold = atoi( *++argv );
83 	    break;
84 	case 'd':
85 	    dflag = TRUE;
86 	    setlinebuf(stdout);
87 	    debug |= atoi( *++argv );
88 	    debug |= ANYDEBUG;
89 #	    ifdef DEBUG
90 		printf("[main] debug = %d\n", debug);
91 #	    else /* not DEBUG */
92 		printf("gprof: -d ignored\n");
93 #	    endif /* DEBUG */
94 	    break;
95 	case 'E':
96 	    ++argv;
97 	    addlist( Elist , *argv );
98 	    Eflag = TRUE;
99 	    addlist( elist , *argv );
100 	    eflag = TRUE;
101 	    break;
102 	case 'e':
103 	    addlist( elist , *++argv );
104 	    eflag = TRUE;
105 	    break;
106 	case 'F':
107 	    ++argv;
108 	    addlist( Flist , *argv );
109 	    Fflag = TRUE;
110 	    addlist( flist , *argv );
111 	    fflag = TRUE;
112 	    break;
113 	case 'f':
114 	    addlist( flist , *++argv );
115 	    fflag = TRUE;
116 	    break;
117 	case 'k':
118 	    addlist( kfromlist , *++argv );
119 	    addlist( ktolist , *++argv );
120 	    kflag = TRUE;
121 	    break;
122 	case 'K':
123 	    Kflag = TRUE;
124 	    break;
125 	case 'l':
126 	    lflag = 1;
127 	    Lflag = 0;
128 	    break;
129 	case 'L':
130 	    Lflag = 1;
131 	    lflag = 0;
132 	    break;
133 	case 's':
134 	    sflag = TRUE;
135 	    break;
136 	case 'u':
137 	    uflag = TRUE;
138 	    break;
139 	case 'z':
140 	    zflag = TRUE;
141 	    break;
142 	}
143 	argv++;
144     }
145     if ( *argv != 0 ) {
146 	a_outname  = *argv;
147 	argv++;
148     } else {
149 	a_outname  = A_OUTNAME;
150     }
151     if ( *argv != 0 ) {
152 	gmonname = *argv;
153 	argv++;
154     } else {
155 	gmonname = (char *) malloc(strlen(a_outname)+6);
156 	strcpy(gmonname, a_outname);
157 	strcat(gmonname, ".gmon");
158     }
159 	/*
160 	 *	get information from the executable file.
161 	 */
162     if ((Kflag && kernel_getnfile(a_outname, &defaultEs) == -1) ||
163       (!Kflag && elf_getnfile(a_outname, &defaultEs) == -1
164 #ifdef WITH_AOUT
165       && aout_getnfile(a_outname, &defaultEs) == -1
166 #endif
167       ))
168 	errx(1, "%s: bad format", a_outname);
169 	/*
170 	 *	sort symbol table.
171 	 */
172     qsort(nl, nname, sizeof(nltype), valcmp);
173 	/*
174 	 *	turn off default functions
175 	 */
176     for ( sp = defaultEs ; *sp ; sp++ ) {
177 	Eflag = TRUE;
178 	addlist( Elist , *sp );
179 	eflag = TRUE;
180 	addlist( elist , *sp );
181     }
182 	/*
183 	 *	get information about mon.out file(s).
184 	 */
185     do	{
186 	getpfile( gmonname );
187 	if ( *argv != 0 ) {
188 	    gmonname = *argv;
189 	}
190     } while ( *argv++ != 0 );
191 	/*
192 	 *	how many ticks per second?
193 	 *	if we can't tell, report time in ticks.
194 	 */
195     if (hz == 0) {
196 	hz = 1;
197 	fprintf(stderr, "time is in ticks, not seconds\n");
198     }
199 	/*
200 	 *	dump out a gmon.sum file if requested
201 	 */
202     if ( sflag ) {
203 	dumpsum( GMONSUM );
204     }
205 	/*
206 	 *	assign samples to procedures
207 	 */
208     asgnsamples();
209 	/*
210 	 *	assemble the dynamic profile
211 	 */
212     timesortnlp = doarcs();
213 	/*
214 	 *	print the dynamic profile
215 	 */
216     if(!lflag) {
217 	    printgprof( timesortnlp );
218     }
219 	/*
220 	 *	print the flat profile
221 	 */
222     if(!Lflag) {
223 	    printprof();
224     }
225 	/*
226 	 *	print the index
227 	 */
228     printindex();
229     exit(0);
230 }
231 
232     /*
233      *	information from a gmon.out file is in two parts:
234      *	an array of sampling hits within pc ranges,
235      *	and the arcs.
236      */
237 void
238 getpfile(char *filename)
239 {
240     FILE		*pfile;
241     struct rawarc	arc;
242 
243     pfile = openpfile(filename);
244     readsamples(pfile);
245 	/*
246 	 *	the rest of the file consists of
247 	 *	a bunch of <from,self,count> tuples.
248 	 */
249     while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
250 #	ifdef DEBUG
251 	    if ( debug & SAMPLEDEBUG ) {
252 		printf( "[getpfile] frompc 0x%lx selfpc 0x%lx count %ld\n" ,
253 			arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
254 	    }
255 #	endif /* DEBUG */
256 	    /*
257 	     *	add this arc
258 	     */
259 	tally( &arc );
260     }
261     fclose(pfile);
262 }
263 
264 FILE *
265 openpfile(char *filename)
266 {
267     struct gmonhdr	tmp;
268     FILE		*pfile;
269     int			size;
270     int			rate;
271 
272     if((pfile = fopen(filename, "r")) == NULL)
273 	err(1, "%s", filename);
274     fread(&tmp, sizeof(struct gmonhdr), 1, pfile);
275     if ( s_highpc != 0 && ( tmp.lpc != gmonhdr.lpc ||
276 	 tmp.hpc != gmonhdr.hpc || tmp.ncnt != gmonhdr.ncnt ) )
277 	errx(1, "%s: incompatible with first gmon file", filename);
278     gmonhdr = tmp;
279     if ( gmonhdr.version == GMONVERSION ) {
280 	rate = gmonhdr.profrate;
281 	size = sizeof(struct gmonhdr);
282     } else {
283 	fseek(pfile, sizeof(struct ophdr), SEEK_SET);
284 	size = sizeof(struct ophdr);
285 	gmonhdr.profrate = rate = hertz();
286 	gmonhdr.version = GMONVERSION;
287     }
288     if (hz == 0) {
289 	hz = rate;
290     } else if (hz != rate)
291 	errx(0, "%s: profile clock rate (%d) %s (%ld) in first gmon file",
292 	    filename, rate, "incompatible with clock rate", hz);
293     if ( gmonhdr.histcounter_type == 0 ) {
294 	/* Historical case.  The type was u_short (2 bytes in practice). */
295 	histcounter_type = 16;
296 	histcounter_size = 2;
297     } else {
298 	histcounter_type = gmonhdr.histcounter_type;
299 	histcounter_size = abs(histcounter_type) / CHAR_BIT;
300     }
301     s_lowpc = (unsigned long) gmonhdr.lpc;
302     s_highpc = (unsigned long) gmonhdr.hpc;
303     lowpc = (unsigned long)gmonhdr.lpc / HISTORICAL_SCALE_2;
304     highpc = (unsigned long)gmonhdr.hpc / HISTORICAL_SCALE_2;
305     sampbytes = gmonhdr.ncnt - size;
306     nsamples = sampbytes / histcounter_size;
307 #   ifdef DEBUG
308 	if ( debug & SAMPLEDEBUG ) {
309 	    printf( "[openpfile] hdr.lpc 0x%lx hdr.hpc 0x%lx hdr.ncnt %d\n",
310 		gmonhdr.lpc , gmonhdr.hpc , gmonhdr.ncnt );
311 	    printf( "[openpfile]   s_lowpc 0x%lx   s_highpc 0x%lx\n" ,
312 		s_lowpc , s_highpc );
313 	    printf( "[openpfile]     lowpc 0x%lx     highpc 0x%lx\n" ,
314 		lowpc , highpc );
315 	    printf( "[openpfile] sampbytes %d nsamples %d\n" ,
316 		sampbytes , nsamples );
317 	    printf( "[openpfile] sample rate %ld\n" , hz );
318 	}
319 #   endif /* DEBUG */
320     return(pfile);
321 }
322 
323 void
324 tally(struct rawarc *rawp)
325 {
326     nltype		*parentp;
327     nltype		*childp;
328 
329     parentp = nllookup( rawp -> raw_frompc );
330     childp = nllookup( rawp -> raw_selfpc );
331     if ( parentp == 0 || childp == 0 )
332 	return;
333     if ( kflag
334 	 && onlist( kfromlist , parentp -> name )
335 	 && onlist( ktolist , childp -> name ) ) {
336 	return;
337     }
338     childp -> ncall += rawp -> raw_count;
339 #   ifdef DEBUG
340 	if ( debug & TALLYDEBUG ) {
341 	    printf( "[tally] arc from %s to %s traversed %ld times\n" ,
342 		    parentp -> name , childp -> name , rawp -> raw_count );
343 	}
344 #   endif /* DEBUG */
345     addarc( parentp , childp , rawp -> raw_count );
346 }
347 
348 /*
349  * dump out the gmon.sum file
350  */
351 void
352 dumpsum(const char *sumfile)
353 {
354     register nltype *nlp;
355     register arctype *arcp;
356     struct rawarc arc;
357     FILE *sfile;
358 
359     if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL )
360 	err( 1 , "%s" , sumfile );
361     /*
362      * dump the header; use the last header read in
363      */
364     if ( fwrite( &gmonhdr , sizeof gmonhdr , 1 , sfile ) != 1 )
365 	err( 1 , "%s" , sumfile );
366     /*
367      * dump the samples
368      */
369     if (fwrite(samples, histcounter_size, nsamples, sfile) != nsamples)
370 	err( 1 , "%s" , sumfile );
371     /*
372      * dump the normalized raw arc information
373      */
374     for ( nlp = nl ; nlp < npe ; nlp++ ) {
375 	for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
376 	    arc.raw_frompc = arcp -> arc_parentp -> value;
377 	    arc.raw_selfpc = arcp -> arc_childp -> value;
378 	    arc.raw_count = arcp -> arc_count;
379 	    if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 )
380 		err( 1 , "%s" , sumfile );
381 #	    ifdef DEBUG
382 		if ( debug & SAMPLEDEBUG ) {
383 		    printf( "[dumpsum] frompc 0x%lx selfpc 0x%lx count %ld\n" ,
384 			    arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
385 		}
386 #	    endif /* DEBUG */
387 	}
388     }
389     fclose( sfile );
390 }
391 
392 static int
393 valcmp(const void *v1, const void *v2)
394 {
395     const nltype *p1 = (const nltype *)v1;
396     const nltype *p2 = (const nltype *)v2;
397 
398     if ( p1 -> value < p2 -> value ) {
399 	return LESSTHAN;
400     }
401     if ( p1 -> value > p2 -> value ) {
402 	return GREATERTHAN;
403     }
404     return EQUALTO;
405 }
406 
407 void
408 readsamples(FILE *pfile)
409 {
410     int		i;
411     intmax_t	sample;
412 
413     if (samples == 0) {
414 	samples = (double *) calloc(nsamples, sizeof(double));
415 	if (samples == NULL)
416 	    errx(0, "no room for %d sample pc's", nsamples);
417     }
418     for (i = 0; i < nsamples; i++) {
419 	fread(&sample, histcounter_size, 1, pfile);
420 	if (feof(pfile))
421 		break;
422 	switch ( histcounter_type ) {
423 	case -8:
424 	    samples[i] += *(int8_t *)&sample;
425 	    break;
426 	case 8:
427 	    samples[i] += *(u_int8_t *)&sample;
428 	    break;
429 	case -16:
430 	    samples[i] += *(int16_t *)&sample;
431 	    break;
432 	case 16:
433 	    samples[i] += *(u_int16_t *)&sample;
434 	    break;
435 	case -32:
436 	    samples[i] += *(int32_t *)&sample;
437 	    break;
438 	case 32:
439 	    samples[i] += *(u_int32_t *)&sample;
440 	    break;
441 	case -64:
442 	    samples[i] += *(int64_t *)&sample;
443 	    break;
444 	case 64:
445 	    samples[i] += *(u_int64_t *)&sample;
446 	    break;
447 	default:
448 	    err(1, "unsupported histogram counter type %d", histcounter_type);
449 	}
450     }
451     if (i != nsamples)
452 	errx(1, "unexpected EOF after reading %d/%d samples", --i , nsamples );
453 }
454 
455 /*
456  *	Assign samples to the procedures to which they belong.
457  *
458  *	There are three cases as to where pcl and pch can be
459  *	with respect to the routine entry addresses svalue0 and svalue1
460  *	as shown in the following diagram.  overlap computes the
461  *	distance between the arrows, the fraction of the sample
462  *	that is to be credited to the routine which starts at svalue0.
463  *
464  *	    svalue0                                         svalue1
465  *	       |                                               |
466  *	       v                                               v
467  *
468  *	       +-----------------------------------------------+
469  *	       |					       |
470  *	  |  ->|    |<-		->|         |<-		->|    |<-  |
471  *	  |         |		  |         |		  |         |
472  *	  +---------+		  +---------+		  +---------+
473  *
474  *	  ^         ^		  ^         ^		  ^         ^
475  *	  |         |		  |         |		  |         |
476  *	 pcl       pch		 pcl       pch		 pcl       pch
477  *
478  *	For the vax we assert that samples will never fall in the first
479  *	two bytes of any routine, since that is the entry mask,
480  *	thus we give call alignentries() to adjust the entry points if
481  *	the entry mask falls in one bucket but the code for the routine
482  *	doesn't start until the next bucket.  In conjunction with the
483  *	alignment of routine addresses, this should allow us to have
484  *	only one sample for every four bytes of text space and never
485  *	have any overlap (the two end cases, above).
486  */
487 void
488 asgnsamples(void)
489 {
490     register int	j;
491     double		ccnt;
492     double		thetime;
493     unsigned long	pcl, pch;
494     register int	i;
495     unsigned long	overlap;
496     unsigned long	svalue0, svalue1;
497 
498     /* read samples and assign to namelist symbols */
499     scale = highpc - lowpc;
500     scale /= nsamples;
501     alignentries();
502     for (i = 0, j = 1; i < nsamples; i++) {
503 	ccnt = samples[i];
504 	if (ccnt == 0)
505 		continue;
506 	pcl = lowpc + (unsigned long)(scale * i);
507 	pch = lowpc + (unsigned long)(scale * (i + 1));
508 	thetime = ccnt;
509 #	ifdef DEBUG
510 	    if ( debug & SAMPLEDEBUG ) {
511 		printf( "[asgnsamples] pcl 0x%lx pch 0x%lx ccnt %.0f\n" ,
512 			pcl , pch , ccnt );
513 	    }
514 #	endif /* DEBUG */
515 	totime += thetime;
516 	for (j = j - 1; j < nname; j++) {
517 	    svalue0 = nl[j].svalue;
518 	    svalue1 = nl[j+1].svalue;
519 		/*
520 		 *	if high end of tick is below entry address,
521 		 *	go for next tick.
522 		 */
523 	    if (pch < svalue0)
524 		    break;
525 		/*
526 		 *	if low end of tick into next routine,
527 		 *	go for next routine.
528 		 */
529 	    if (pcl >= svalue1)
530 		    continue;
531 	    overlap = min(pch, svalue1) - max(pcl, svalue0);
532 	    if (overlap > 0) {
533 #		ifdef DEBUG
534 		    if (debug & SAMPLEDEBUG) {
535 			printf("[asgnsamples] (0x%lx->0x%lx-0x%lx) %s gets %f ticks %lu overlap\n",
536 				nl[j].value / HISTORICAL_SCALE_2,
537 				svalue0, svalue1, nl[j].name,
538 				overlap * thetime / scale, overlap);
539 		    }
540 #		endif /* DEBUG */
541 		nl[j].time += overlap * thetime / scale;
542 	    }
543 	}
544     }
545 #   ifdef DEBUG
546 	if (debug & SAMPLEDEBUG) {
547 	    printf("[asgnsamples] totime %f\n", totime);
548 	}
549 #   endif /* DEBUG */
550 }
551 
552 
553 unsigned long
554 min(unsigned long a, unsigned long b)
555 {
556     if (a<b)
557 	return(a);
558     return(b);
559 }
560 
561 unsigned long
562 max(unsigned long a, unsigned long b)
563 {
564     if (a>b)
565 	return(a);
566     return(b);
567 }
568 
569     /*
570      *	calculate scaled entry point addresses (to save time in asgnsamples),
571      *	and possibly push the scaled entry points over the entry mask,
572      *	if it turns out that the entry point is in one bucket and the code
573      *	for a routine is in the next bucket.
574      */
575 void
576 alignentries(void)
577 {
578     register struct nl	*nlp;
579     unsigned long	bucket_of_entry;
580     unsigned long	bucket_of_code;
581 
582     for (nlp = nl; nlp < npe; nlp++) {
583 	nlp -> svalue = nlp -> value / HISTORICAL_SCALE_2;
584 	bucket_of_entry = (nlp->svalue - lowpc) / scale;
585 	bucket_of_code = (nlp->svalue + OFFSET_OF_CODE / HISTORICAL_SCALE_2 -
586 	  lowpc) / scale;
587 	if (bucket_of_entry < bucket_of_code) {
588 #	    ifdef DEBUG
589 		if (debug & SAMPLEDEBUG) {
590 		    printf("[alignentries] pushing svalue 0x%lx to 0x%lx\n",
591 			    nlp->svalue,
592 			    nlp->svalue + OFFSET_OF_CODE / HISTORICAL_SCALE_2);
593 		}
594 #	    endif /* DEBUG */
595 	    nlp->svalue += OFFSET_OF_CODE / HISTORICAL_SCALE_2;
596 	}
597     }
598 }
599