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