]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/gprof/gprof.c
ping: use the monotonic clock to measure durations
[FreeBSD/FreeBSD.git] / usr.bin / gprof / gprof.c
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       aout_getnfile(a_outname, &defaultEs) == -1))
165         errx(1, "%s: bad format", a_outname);
166         /*
167          *      sort symbol table.
168          */
169     qsort(nl, nname, sizeof(nltype), valcmp);
170         /*
171          *      turn off default functions
172          */
173     for ( sp = defaultEs ; *sp ; sp++ ) {
174         Eflag = TRUE;
175         addlist( Elist , *sp );
176         eflag = TRUE;
177         addlist( elist , *sp );
178     }
179         /*
180          *      get information about mon.out file(s).
181          */
182     do  {
183         getpfile( gmonname );
184         if ( *argv != 0 ) {
185             gmonname = *argv;
186         }
187     } while ( *argv++ != 0 );
188         /*
189          *      how many ticks per second?
190          *      if we can't tell, report time in ticks.
191          */
192     if (hz == 0) {
193         hz = 1;
194         fprintf(stderr, "time is in ticks, not seconds\n");
195     }
196         /*
197          *      dump out a gmon.sum file if requested
198          */
199     if ( sflag ) {
200         dumpsum( GMONSUM );
201     }
202         /*
203          *      assign samples to procedures
204          */
205     asgnsamples();
206         /*
207          *      assemble the dynamic profile
208          */
209     timesortnlp = doarcs();
210         /*
211          *      print the dynamic profile
212          */
213     if(!lflag) {
214             printgprof( timesortnlp );
215     }
216         /*
217          *      print the flat profile
218          */
219     if(!Lflag) {
220             printprof();
221     }
222         /*
223          *      print the index
224          */
225     printindex();
226     exit(0);
227 }
228
229     /*
230      *  information from a gmon.out file is in two parts:
231      *  an array of sampling hits within pc ranges,
232      *  and the arcs.
233      */
234 void
235 getpfile(char *filename)
236 {
237     FILE                *pfile;
238     struct rawarc       arc;
239
240     pfile = openpfile(filename);
241     readsamples(pfile);
242         /*
243          *      the rest of the file consists of
244          *      a bunch of <from,self,count> tuples.
245          */
246     while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
247 #       ifdef DEBUG
248             if ( debug & SAMPLEDEBUG ) {
249                 printf( "[getpfile] frompc 0x%lx selfpc 0x%lx count %ld\n" ,
250                         arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
251             }
252 #       endif /* DEBUG */
253             /*
254              *  add this arc
255              */
256         tally( &arc );
257     }
258     fclose(pfile);
259 }
260
261 FILE *
262 openpfile(char *filename)
263 {
264     struct gmonhdr      tmp;
265     FILE                *pfile;
266     int                 size;
267     int                 rate;
268
269     if((pfile = fopen(filename, "r")) == NULL)
270         err(1, "%s", filename);
271     fread(&tmp, sizeof(struct gmonhdr), 1, pfile);
272     if ( s_highpc != 0 && ( tmp.lpc != gmonhdr.lpc ||
273          tmp.hpc != gmonhdr.hpc || tmp.ncnt != gmonhdr.ncnt ) )
274         errx(1, "%s: incompatible with first gmon file", filename);
275     gmonhdr = tmp;
276     if ( gmonhdr.version == GMONVERSION ) {
277         rate = gmonhdr.profrate;
278         size = sizeof(struct gmonhdr);
279     } else {
280         fseek(pfile, sizeof(struct ophdr), SEEK_SET);
281         size = sizeof(struct ophdr);
282         gmonhdr.profrate = rate = hertz();
283         gmonhdr.version = GMONVERSION;
284     }
285     if (hz == 0) {
286         hz = rate;
287     } else if (hz != rate)
288         errx(0, "%s: profile clock rate (%d) %s (%ld) in first gmon file",
289             filename, rate, "incompatible with clock rate", hz);
290     if ( gmonhdr.histcounter_type == 0 ) {
291         /* Historical case.  The type was u_short (2 bytes in practice). */
292         histcounter_type = 16;
293         histcounter_size = 2;
294     } else {
295         histcounter_type = gmonhdr.histcounter_type;
296         histcounter_size = abs(histcounter_type) / CHAR_BIT;
297     }
298     s_lowpc = (unsigned long) gmonhdr.lpc;
299     s_highpc = (unsigned long) gmonhdr.hpc;
300     lowpc = (unsigned long)gmonhdr.lpc / HISTORICAL_SCALE_2;
301     highpc = (unsigned long)gmonhdr.hpc / HISTORICAL_SCALE_2;
302     sampbytes = gmonhdr.ncnt - size;
303     nsamples = sampbytes / histcounter_size;
304 #   ifdef DEBUG
305         if ( debug & SAMPLEDEBUG ) {
306             printf( "[openpfile] hdr.lpc 0x%lx hdr.hpc 0x%lx hdr.ncnt %d\n",
307                 gmonhdr.lpc , gmonhdr.hpc , gmonhdr.ncnt );
308             printf( "[openpfile]   s_lowpc 0x%lx   s_highpc 0x%lx\n" ,
309                 s_lowpc , s_highpc );
310             printf( "[openpfile]     lowpc 0x%lx     highpc 0x%lx\n" ,
311                 lowpc , highpc );
312             printf( "[openpfile] sampbytes %d nsamples %d\n" ,
313                 sampbytes , nsamples );
314             printf( "[openpfile] sample rate %ld\n" , hz );
315         }
316 #   endif /* DEBUG */
317     return(pfile);
318 }
319
320 void
321 tally(struct rawarc *rawp)
322 {
323     nltype              *parentp;
324     nltype              *childp;
325
326     parentp = nllookup( rawp -> raw_frompc );
327     childp = nllookup( rawp -> raw_selfpc );
328     if ( parentp == 0 || childp == 0 )
329         return;
330     if ( kflag
331          && onlist( kfromlist , parentp -> name )
332          && onlist( ktolist , childp -> name ) ) {
333         return;
334     }
335     childp -> ncall += rawp -> raw_count;
336 #   ifdef DEBUG
337         if ( debug & TALLYDEBUG ) {
338             printf( "[tally] arc from %s to %s traversed %ld times\n" ,
339                     parentp -> name , childp -> name , rawp -> raw_count );
340         }
341 #   endif /* DEBUG */
342     addarc( parentp , childp , rawp -> raw_count );
343 }
344
345 /*
346  * dump out the gmon.sum file
347  */
348 void
349 dumpsum(const char *sumfile)
350 {
351     register nltype *nlp;
352     register arctype *arcp;
353     struct rawarc arc;
354     FILE *sfile;
355
356     if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL )
357         err( 1 , "%s" , sumfile );
358     /*
359      * dump the header; use the last header read in
360      */
361     if ( fwrite( &gmonhdr , sizeof gmonhdr , 1 , sfile ) != 1 )
362         err( 1 , "%s" , sumfile );
363     /*
364      * dump the samples
365      */
366     if (fwrite(samples, histcounter_size, nsamples, sfile) != nsamples)
367         err( 1 , "%s" , sumfile );
368     /*
369      * dump the normalized raw arc information
370      */
371     for ( nlp = nl ; nlp < npe ; nlp++ ) {
372         for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
373             arc.raw_frompc = arcp -> arc_parentp -> value;
374             arc.raw_selfpc = arcp -> arc_childp -> value;
375             arc.raw_count = arcp -> arc_count;
376             if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 )
377                 err( 1 , "%s" , sumfile );
378 #           ifdef DEBUG
379                 if ( debug & SAMPLEDEBUG ) {
380                     printf( "[dumpsum] frompc 0x%lx selfpc 0x%lx count %ld\n" ,
381                             arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
382                 }
383 #           endif /* DEBUG */
384         }
385     }
386     fclose( sfile );
387 }
388
389 static int
390 valcmp(const void *v1, const void *v2)
391 {
392     const nltype *p1 = (const nltype *)v1;
393     const nltype *p2 = (const nltype *)v2;
394
395     if ( p1 -> value < p2 -> value ) {
396         return LESSTHAN;
397     }
398     if ( p1 -> value > p2 -> value ) {
399         return GREATERTHAN;
400     }
401     return EQUALTO;
402 }
403
404 void
405 readsamples(FILE *pfile)
406 {
407     int         i;
408     intmax_t    sample;
409
410     if (samples == 0) {
411         samples = (double *) calloc(nsamples, sizeof(double));
412         if (samples == NULL)
413             errx(0, "no room for %d sample pc's", nsamples);
414     }
415     for (i = 0; i < nsamples; i++) {
416         fread(&sample, histcounter_size, 1, pfile);
417         if (feof(pfile))
418                 break;
419         switch ( histcounter_type ) {
420         case -8:
421             samples[i] += *(int8_t *)&sample;
422             break;
423         case 8:
424             samples[i] += *(u_int8_t *)&sample;
425             break;
426         case -16:
427             samples[i] += *(int16_t *)&sample;
428             break;
429         case 16:
430             samples[i] += *(u_int16_t *)&sample;
431             break;
432         case -32:
433             samples[i] += *(int32_t *)&sample;
434             break;
435         case 32:
436             samples[i] += *(u_int32_t *)&sample;
437             break;
438         case -64:
439             samples[i] += *(int64_t *)&sample;
440             break;
441         case 64:
442             samples[i] += *(u_int64_t *)&sample;
443             break;
444         default:
445             err(1, "unsupported histogram counter type %d", histcounter_type);
446         }
447     }
448     if (i != nsamples)
449         errx(1, "unexpected EOF after reading %d/%d samples", --i , nsamples );
450 }
451
452 /*
453  *      Assign samples to the procedures to which they belong.
454  *
455  *      There are three cases as to where pcl and pch can be
456  *      with respect to the routine entry addresses svalue0 and svalue1
457  *      as shown in the following diagram.  overlap computes the
458  *      distance between the arrows, the fraction of the sample
459  *      that is to be credited to the routine which starts at svalue0.
460  *
461  *          svalue0                                         svalue1
462  *             |                                               |
463  *             v                                               v
464  *
465  *             +-----------------------------------------------+
466  *             |                                               |
467  *        |  ->|    |<-         ->|         |<-         ->|    |<-  |
468  *        |         |             |         |             |         |
469  *        +---------+             +---------+             +---------+
470  *
471  *        ^         ^             ^         ^             ^         ^
472  *        |         |             |         |             |         |
473  *       pcl       pch           pcl       pch           pcl       pch
474  *
475  *      For the vax we assert that samples will never fall in the first
476  *      two bytes of any routine, since that is the entry mask,
477  *      thus we give call alignentries() to adjust the entry points if
478  *      the entry mask falls in one bucket but the code for the routine
479  *      doesn't start until the next bucket.  In conjunction with the
480  *      alignment of routine addresses, this should allow us to have
481  *      only one sample for every four bytes of text space and never
482  *      have any overlap (the two end cases, above).
483  */
484 void
485 asgnsamples(void)
486 {
487     register int        j;
488     double              ccnt;
489     double              thetime;
490     unsigned long       pcl, pch;
491     register int        i;
492     unsigned long       overlap;
493     unsigned long       svalue0, svalue1;
494
495     /* read samples and assign to namelist symbols */
496     scale = highpc - lowpc;
497     scale /= nsamples;
498     alignentries();
499     for (i = 0, j = 1; i < nsamples; i++) {
500         ccnt = samples[i];
501         if (ccnt == 0)
502                 continue;
503         pcl = lowpc + (unsigned long)(scale * i);
504         pch = lowpc + (unsigned long)(scale * (i + 1));
505         thetime = ccnt;
506 #       ifdef DEBUG
507             if ( debug & SAMPLEDEBUG ) {
508                 printf( "[asgnsamples] pcl 0x%lx pch 0x%lx ccnt %.0f\n" ,
509                         pcl , pch , ccnt );
510             }
511 #       endif /* DEBUG */
512         totime += thetime;
513         for (j = j - 1; j < nname; j++) {
514             svalue0 = nl[j].svalue;
515             svalue1 = nl[j+1].svalue;
516                 /*
517                  *      if high end of tick is below entry address,
518                  *      go for next tick.
519                  */
520             if (pch < svalue0)
521                     break;
522                 /*
523                  *      if low end of tick into next routine,
524                  *      go for next routine.
525                  */
526             if (pcl >= svalue1)
527                     continue;
528             overlap = min(pch, svalue1) - max(pcl, svalue0);
529             if (overlap > 0) {
530 #               ifdef DEBUG
531                     if (debug & SAMPLEDEBUG) {
532                         printf("[asgnsamples] (0x%lx->0x%lx-0x%lx) %s gets %f ticks %lu overlap\n",
533                                 nl[j].value / HISTORICAL_SCALE_2,
534                                 svalue0, svalue1, nl[j].name,
535                                 overlap * thetime / scale, overlap);
536                     }
537 #               endif /* DEBUG */
538                 nl[j].time += overlap * thetime / scale;
539             }
540         }
541     }
542 #   ifdef DEBUG
543         if (debug & SAMPLEDEBUG) {
544             printf("[asgnsamples] totime %f\n", totime);
545         }
546 #   endif /* DEBUG */
547 }
548
549
550 unsigned long
551 min(unsigned long a, unsigned long b)
552 {
553     if (a<b)
554         return(a);
555     return(b);
556 }
557
558 unsigned long
559 max(unsigned long a, unsigned long b)
560 {
561     if (a>b)
562         return(a);
563     return(b);
564 }
565
566     /*
567      *  calculate scaled entry point addresses (to save time in asgnsamples),
568      *  and possibly push the scaled entry points over the entry mask,
569      *  if it turns out that the entry point is in one bucket and the code
570      *  for a routine is in the next bucket.
571      */
572 void
573 alignentries(void)
574 {
575     register struct nl  *nlp;
576     unsigned long       bucket_of_entry;
577     unsigned long       bucket_of_code;
578
579     for (nlp = nl; nlp < npe; nlp++) {
580         nlp -> svalue = nlp -> value / HISTORICAL_SCALE_2;
581         bucket_of_entry = (nlp->svalue - lowpc) / scale;
582         bucket_of_code = (nlp->svalue + OFFSET_OF_CODE / HISTORICAL_SCALE_2 -
583           lowpc) / scale;
584         if (bucket_of_entry < bucket_of_code) {
585 #           ifdef DEBUG
586                 if (debug & SAMPLEDEBUG) {
587                     printf("[alignentries] pushing svalue 0x%lx to 0x%lx\n",
588                             nlp->svalue,
589                             nlp->svalue + OFFSET_OF_CODE / HISTORICAL_SCALE_2);
590                 }
591 #           endif /* DEBUG */
592             nlp->svalue += OFFSET_OF_CODE / HISTORICAL_SCALE_2;
593         }
594     }
595 }