]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/gprof/gprof.c
ifconfig(8): wordsmith -G and -g descriptions
[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 #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 }