9c4701c8ccaa4f235235722cbb95e056267ed367
[platform/upstream/binutils.git] / gprof / gprof.c
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that: (1) source distributions retain this entire copyright
7  * notice and comment, and (2) distributions including binaries display
8  * the following acknowledgement:  ``This product includes software
9  * developed by the University of California, Berkeley and its contributors''
10  * in the documentation or other materials provided with the distribution
11  * and in all advertising materials mentioning features or use of this
12  * software. Neither the name of the University nor the names of its
13  * contributors may be used to endorse or promote products derived
14  * from this software without specific prior written permission.
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18  */
19
20 #ifndef lint
21 char copyright[] =
22 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
23  All rights reserved.\n";
24 #endif /* not lint */
25
26 #ifndef lint
27 static char sccsid[] = "@(#)gprof.c     5.6 (Berkeley) 6/1/90";
28 #endif /* not lint */
29
30 #include "gprof.h"
31
32 char    *whoami = "gprof";
33
34     /*
35      *  things which get -E excluded by default.
36      */
37 char    *defaultEs[] = { "mcount" , "__mcleanup" , 0 };
38
39 main(argc, argv)
40     int argc;
41     char **argv;
42 {
43     char        **sp;
44     nltype      **timesortnlp;
45
46     --argc;
47     argv++;
48     debug = 0;
49     bflag = TRUE;
50     while ( *argv != 0 && **argv == '-' ) {
51         (*argv)++;
52         switch ( **argv ) {
53         case 'a':
54             aflag = TRUE;
55             break;
56         case 'b':
57             bflag = FALSE;
58             break;
59         case 'c':
60             cflag = TRUE;
61             break;
62         case 'd':
63             dflag = TRUE;
64             (*argv)++;
65             debug |= atoi( *argv );
66             debug |= ANYDEBUG;
67 #           ifdef DEBUG
68                 printf("[main] debug = %d\n", debug);
69 #           else not DEBUG
70                 printf("%s: -d ignored\n", whoami);
71 #           endif DEBUG
72             break;
73         case 'E':
74             ++argv;
75             addlist( Elist , *argv );
76             Eflag = TRUE;
77             addlist( elist , *argv );
78             eflag = TRUE;
79             break;
80         case 'e':
81             addlist( elist , *++argv );
82             eflag = TRUE;
83             break;
84         case 'F':
85             ++argv;
86             addlist( Flist , *argv );
87             Fflag = TRUE;
88             addlist( flist , *argv );
89             fflag = TRUE;
90             break;
91         case 'f':
92             addlist( flist , *++argv );
93             fflag = TRUE;
94             break;
95         case 'k':
96             addlist( kfromlist , *++argv );
97             addlist( ktolist , *++argv );
98             kflag = TRUE;
99             break;
100         case 's':
101             sflag = TRUE;
102             break;
103         case 'z':
104             zflag = TRUE;
105             break;
106         }
107         argv++;
108     }
109     if ( *argv != 0 ) {
110         a_outname  = *argv;
111         argv++;
112     } else {
113         a_outname  = A_OUTNAME;
114     }
115     if ( *argv != 0 ) {
116         gmonname = *argv;
117         argv++;
118     } else {
119         gmonname = GMONNAME;
120     }
121         /*
122          *      turn off default functions
123          */
124     for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
125         Eflag = TRUE;
126         addlist( Elist , *sp );
127         eflag = TRUE;
128         addlist( elist , *sp );
129     }
130         /*
131          *      how many ticks per second?
132          *      if we can't tell, report time in ticks.
133          */
134     hz = hertz();
135     if (hz == 0) {
136         hz = 1;
137         fprintf(stderr, "time is in ticks, not seconds\n");
138     }
139         /*
140          *      get information about a.out file.
141          */
142     getnfile();
143         /*
144          *      get information about mon.out file(s).
145          */
146     do  {
147         getpfile( gmonname );
148         if ( *argv != 0 ) {
149             gmonname = *argv;
150         }
151     } while ( *argv++ != 0 );
152         /*
153          *      dump out a gmon.sum file if requested
154          */
155     if ( sflag ) {
156         dumpsum( GMONSUM );
157     }
158         /*
159          *      assign samples to procedures
160          */
161     asgnsamples();
162         /*
163          *      assemble the dynamic profile
164          */
165     timesortnlp = doarcs();
166         /*
167          *      print the dynamic profile
168          */
169     printgprof( timesortnlp );  
170         /*
171          *      print the flat profile
172          */
173     printprof();        
174         /*
175          *      print the index
176          */
177     printindex();       
178     done();
179 }
180
181     /*
182      * Set up string and symbol tables from a.out.
183      *  and optionally the text space.
184      * On return symbol table is sorted by value.
185      */
186 getnfile()
187 {
188   bfd   *abfd;
189   int           valcmp();
190
191   abfd = bfd_openr (a_outname, NULL);
192
193   if (abfd == NULL) {
194     perror (a_outname);
195     done();
196   }
197
198   if (!bfd_check_format (abfd, bfd_object)) {
199     fprintf (stderr, "%s: %s: bad format\n", whoami, a_outname);
200     done();
201   }
202
203 /*  getstrtab(nfile); */
204   getsymtab(abfd);
205   gettextspace( abfd );
206   qsort(nl, nname, sizeof(nltype), valcmp);
207
208 #   ifdef DEBUG
209   if ( debug & AOUTDEBUG ) {
210     register int j;
211     
212     for (j = 0; j < nname; j++){
213       printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name);
214     }
215   }
216 #   endif DEBUG
217 }
218
219 #if 0
220 getstrtab(nfile)
221     FILE        *nfile;
222 {
223
224     fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
225     if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
226         fprintf(stderr, "%s: %s: no string table (old format?)\n" ,
227                 whoami , a_outname );
228         done();
229     }
230     strtab = (char *)calloc(ssiz, 1);
231     if (strtab == NULL) {
232         fprintf(stderr, "%s: %s: no room for %d bytes of string table",
233                 whoami , a_outname , ssiz);
234         done();
235     }
236     if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
237         fprintf(stderr, "%s: %s: error reading string table\n",
238                 whoami , a_outname );
239         done();
240     }
241 }
242 #endif  /* 0 */
243     /*
244      * Read in symbol table
245      */
246 getsymtab(abfd)
247 bfd     *abfd;
248 {
249     register long       i;
250     int                 askfor;
251     int                 nosyms;
252     asymbol             **syms;
253     i = get_symtab_upper_bound (abfd);  /* This will probably give us more
254                                          * than we need, but that's ok.
255                                          */
256     syms = malloc (i);
257     nosyms = bfd_canonicalize_symtab (abfd, syms);
258
259     nname = 0;
260     for (i = 0; i < nosyms; i++) {
261       if (!funcsymbol (syms[i]))
262         continue;
263       nname++;
264     }
265
266     if (nname == 0) {
267       fprintf(stderr, "%s: %s: no symbols\n", whoami , a_outname );
268       done();
269     }
270     askfor = nname + 1;
271     nl = (nltype *) calloc( askfor , sizeof(nltype) );
272     if (nl == 0) {
273         fprintf(stderr, "%s: No room for %d bytes of symbol table\n",
274                 whoami, askfor * sizeof(nltype) );
275         done();
276     }
277
278     /* pass2 - read symbols */
279     npe = nl;
280     nname = 0;
281     for (i = 0; i < nosyms; i++) {
282       if (!funcsymbol (syms[i])) {
283 #           ifdef DEBUG
284         if ( debug & AOUTDEBUG ) {
285           printf( "[getsymtab] rejecting: 0x%x %s\n" ,
286                  syms[i]->value, syms[i]->name);
287         }
288 #           endif DEBUG
289         continue;
290       }
291       npe->value = syms[i]->value + syms[i]->section->vma;
292       npe->name = syms[i]->name;
293 #       ifdef DEBUG
294       if ( debug & AOUTDEBUG ) {
295         printf( "[getsymtab] %d %s 0x%08x\n" ,
296                nname , npe -> name , npe -> value );
297       }
298 #       endif DEBUG
299       npe++;
300       nname++;
301     }
302     npe->value = -1;
303 }
304
305 /*
306  *      read in the text space of an a.out file
307  */
308 gettextspace( abfd )
309      bfd        *abfd;
310 {
311   asection      *texsec;
312     
313   if ( cflag == 0 ) {
314     return;
315   }
316
317   texsec = bfd_get_section_by_name (abfd, ".text");
318   if (texsec == NULL) {
319     return;
320   }
321
322   textspace = (u_char *) malloc( texsec->_cooked_size );
323
324   if ( textspace == 0 ) {
325     fprintf( stderr , "%s: ran out room for %d bytes of text space:  " ,
326             whoami , texsec->_cooked_size);
327     fprintf( stderr , "can't do -c\n" );
328     return;
329   }
330   bfd_get_section_contents (abfd, texsec, textspace, texsec->filepos, 
331                             texsec->_cooked_size);
332 }
333 /*
334  *      information from a gmon.out file is in two parts:
335  *      an array of sampling hits within pc ranges,
336  *      and the arcs.
337  */
338 getpfile(filename)
339     char *filename;
340 {
341     FILE                *pfile;
342     FILE                *openpfile();
343     struct rawarc       arc;
344
345     pfile = openpfile(filename);
346     readsamples(pfile);
347         /*
348          *      the rest of the file consists of
349          *      a bunch of <from,self,count> tuples.
350          */
351     while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
352 #       ifdef DEBUG
353             if ( debug & SAMPLEDEBUG ) {
354                 printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" ,
355                         arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
356             }
357 #       endif DEBUG
358             /*
359              *  add this arc
360              */
361         tally( &arc );
362     }
363     fclose(pfile);
364 }
365
366 FILE *
367 openpfile(filename)
368     char *filename;
369 {
370     struct hdr  tmp;
371     FILE        *pfile;
372
373     if((pfile = fopen(filename, "r")) == NULL) {
374         perror(filename);
375         done();
376     }
377     fread(&tmp, sizeof(struct hdr), 1, pfile);
378     if ( s_highpc != 0 && ( tmp.lowpc != h.lowpc ||
379          tmp.highpc != h.highpc || tmp.ncnt != h.ncnt ) ) {
380         fprintf(stderr, "%s: incompatible with first gmon file\n", filename);
381         done();
382     }
383     h = tmp;
384     s_lowpc = (unsigned long) h.lowpc;
385     s_highpc = (unsigned long) h.highpc;
386     lowpc = (unsigned long)h.lowpc / sizeof(UNIT);
387     highpc = (unsigned long)h.highpc / sizeof(UNIT);
388     sampbytes = h.ncnt - sizeof(struct hdr);
389     nsamples = sampbytes / sizeof (UNIT);
390 #   ifdef DEBUG
391         if ( debug & SAMPLEDEBUG ) {
392             printf( "[openpfile] hdr.lowpc 0x%x hdr.highpc 0x%x hdr.ncnt %d\n",
393                 h.lowpc , h.highpc , h.ncnt );
394             printf( "[openpfile]   s_lowpc 0x%x   s_highpc 0x%x\n" ,
395                 s_lowpc , s_highpc );
396             printf( "[openpfile]     lowpc 0x%x     highpc 0x%x\n" ,
397                 lowpc , highpc );
398             printf( "[openpfile] sampbytes %d nsamples %d\n" ,
399                 sampbytes , nsamples );
400         }
401 #   endif DEBUG
402     return(pfile);
403 }
404
405 tally( rawp )
406     struct rawarc       *rawp;
407 {
408     nltype              *parentp;
409     nltype              *childp;
410
411     parentp = nllookup( rawp -> raw_frompc );
412     childp = nllookup( rawp -> raw_selfpc );
413     if ( kflag
414          && onlist( kfromlist , parentp -> name )
415          && onlist( ktolist , childp -> name ) ) {
416         return;
417     }
418     childp -> ncall += rawp -> raw_count;
419 #   ifdef DEBUG
420         if ( debug & TALLYDEBUG ) {
421             printf( "[tally] arc from %s to %s traversed %d times\n" ,
422                     parentp -> name , childp -> name , rawp -> raw_count );
423         }
424 #   endif DEBUG
425     addarc( parentp , childp , rawp -> raw_count );
426 }
427
428 /*
429  * dump out the gmon.sum file
430  */
431 dumpsum( sumfile )
432     char *sumfile;
433 {
434     register nltype *nlp;
435     register arctype *arcp;
436     struct rawarc arc;
437     FILE *sfile;
438
439     if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) {
440         perror( sumfile );
441         done();
442     }
443     /*
444      * dump the header; use the last header read in
445      */
446     if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) {
447         perror( sumfile );
448         done();
449     }
450     /*
451      * dump the samples
452      */
453     if (fwrite(samples, sizeof (UNIT), nsamples, sfile) != nsamples) {
454         perror( sumfile );
455         done();
456     }
457     /*
458      * dump the normalized raw arc information
459      */
460     for ( nlp = nl ; nlp < npe ; nlp++ ) {
461         for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
462             arc.raw_frompc = arcp -> arc_parentp -> value;
463             arc.raw_selfpc = arcp -> arc_childp -> value;
464             arc.raw_count = arcp -> arc_count;
465             if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) {
466                 perror( sumfile );
467                 done();
468             }
469 #           ifdef DEBUG
470                 if ( debug & SAMPLEDEBUG ) {
471                     printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" ,
472                             arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
473                 }
474 #           endif DEBUG
475         }
476     }
477     fclose( sfile );
478 }
479
480 valcmp(p1, p2)
481     nltype *p1, *p2;
482 {
483     if ( p1 -> value < p2 -> value ) {
484         return LESSTHAN;
485     }
486     if ( p1 -> value > p2 -> value ) {
487         return GREATERTHAN;
488     }
489     return EQUALTO;
490 }
491
492 readsamples(pfile)
493     FILE        *pfile;
494 {
495     register i;
496     UNIT        sample;
497     
498     if (samples == 0) {
499         samples = (UNIT *) calloc(sampbytes, sizeof (UNIT));
500         if (samples == 0) {
501             fprintf( stderr , "%s: No room for %d sample pc's\n", 
502                 whoami , sampbytes / sizeof (UNIT));
503             done();
504         }
505     }
506     for (i = 0; i < nsamples; i++) {
507         fread(&sample, sizeof (UNIT), 1, pfile);
508         if (feof(pfile))
509                 break;
510         samples[i] += sample;
511     }
512     if (i != nsamples) {
513         fprintf(stderr,
514             "%s: unexpected EOF after reading %d/%d samples\n",
515                 whoami , --i , nsamples );
516         done();
517     }
518 }
519
520 /*
521  *      Assign samples to the procedures to which they belong.
522  *
523  *      There are three cases as to where pcl and pch can be
524  *      with respect to the routine entry addresses svalue0 and svalue1
525  *      as shown in the following diagram.  overlap computes the
526  *      distance between the arrows, the fraction of the sample
527  *      that is to be credited to the routine which starts at svalue0.
528  *
529  *          svalue0                                         svalue1
530  *             |                                               |
531  *             v                                               v
532  *
533  *             +-----------------------------------------------+
534  *             |                                               |
535  *        |  ->|    |<-         ->|         |<-         ->|    |<-  |
536  *        |         |             |         |             |         |
537  *        +---------+             +---------+             +---------+
538  *
539  *        ^         ^             ^         ^             ^         ^
540  *        |         |             |         |             |         |
541  *       pcl       pch           pcl       pch           pcl       pch
542  *
543  *      For the vax we assert that samples will never fall in the first
544  *      two bytes of any routine, since that is the entry mask,
545  *      thus we give call alignentries() to adjust the entry points if
546  *      the entry mask falls in one bucket but the code for the routine
547  *      doesn't start until the next bucket.  In conjunction with the
548  *      alignment of routine addresses, this should allow us to have
549  *      only one sample for every four bytes of text space and never
550  *      have any overlap (the two end cases, above).
551  */
552 asgnsamples()
553 {
554     register int        j;
555     UNIT                ccnt;
556     double              time;
557     unsigned long       pcl, pch;
558     register int        i;
559     unsigned long       overlap;
560     unsigned long       svalue0, svalue1;
561
562     /* read samples and assign to namelist symbols */
563     scale = highpc - lowpc;
564     scale /= nsamples;
565     alignentries();
566     for (i = 0, j = 1; i < nsamples; i++) {
567         ccnt = samples[i];
568         if (ccnt == 0)
569                 continue;
570         pcl = lowpc + scale * i;
571         pch = lowpc + scale * (i + 1);
572         time = ccnt;
573 #       ifdef DEBUG
574             if ( debug & SAMPLEDEBUG ) {
575                 printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" ,
576                         pcl , pch , ccnt );
577             }
578 #       endif DEBUG
579         totime += time;
580         for (j = j - 1; j < nname; j++) {
581             svalue0 = nl[j].svalue;
582             svalue1 = nl[j+1].svalue;
583                 /*
584                  *      if high end of tick is below entry address, 
585                  *      go for next tick.
586                  */
587             if (pch < svalue0)
588                     break;
589                 /*
590                  *      if low end of tick into next routine,
591                  *      go for next routine.
592                  */
593             if (pcl >= svalue1)
594                     continue;
595             overlap = min(pch, svalue1) - max(pcl, svalue0);
596             if (overlap > 0) {
597 #               ifdef DEBUG
598                     if (debug & SAMPLEDEBUG) {
599                         printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f ticks %d overlap\n",
600                                 nl[j].value/sizeof(UNIT), svalue0, svalue1,
601                                 nl[j].name, 
602                                 overlap * time / scale, overlap);
603                     }
604 #               endif DEBUG
605                 nl[j].time += overlap * time / scale;
606             }
607         }
608     }
609 #   ifdef DEBUG
610         if (debug & SAMPLEDEBUG) {
611             printf("[asgnsamples] totime %f\n", totime);
612         }
613 #   endif DEBUG
614 }
615
616
617 unsigned long
618 min(a, b)
619     unsigned long a,b;
620 {
621     if (a<b)
622         return(a);
623     return(b);
624 }
625
626 unsigned long
627 max(a, b)
628     unsigned long a,b;
629 {
630     if (a>b)
631         return(a);
632     return(b);
633 }
634
635     /*
636      *  calculate scaled entry point addresses (to save time in asgnsamples),
637      *  and possibly push the scaled entry points over the entry mask,
638      *  if it turns out that the entry point is in one bucket and the code
639      *  for a routine is in the next bucket.
640      */
641 alignentries()
642 {
643     register struct nl  *nlp;
644     unsigned long       bucket_of_entry;
645     unsigned long       bucket_of_code;
646
647     for (nlp = nl; nlp < npe; nlp++) {
648         nlp -> svalue = nlp -> value / sizeof(UNIT);
649         bucket_of_entry = (nlp->svalue - lowpc) / scale;
650         bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
651         if (bucket_of_entry < bucket_of_code) {
652 #           ifdef DEBUG
653                 if (debug & SAMPLEDEBUG) {
654                     printf("[alignentries] pushing svalue 0x%x to 0x%x\n",
655                             nlp->svalue, nlp->svalue + UNITS_TO_CODE);
656                 }
657 #           endif DEBUG
658             nlp->svalue += UNITS_TO_CODE;
659         }
660     }
661 }
662
663 bool
664 funcsymbol( symp )
665      asymbol *symp;
666 {
667   extern char   *strtab;        /* string table from a.out */
668   extern int    aflag;          /* if static functions aren't desired */
669   char  *name;
670   
671   /*
672    *    must be a text symbol,
673    *    and static text symbols don't qualify if aflag set.
674    */
675   
676   if (!symp->section)
677     return FALSE;
678
679   if (!aflag && (symp->flags&BSF_LOCAL)) {
680 #ifdef  DEBUG
681     fprintf (stderr, "%s(%d):  %s:  not a function\n", __FILE__, __LINE__, symp->name);
682 #endif
683     return FALSE;
684   }
685
686   /*
687    *    can't have any `funny' characters in name,
688    *    where `funny' includes  `.', .o file names
689    *                    and     `$', pascal labels.
690    */
691   if (!symp->name)
692     return FALSE;
693
694   for (name = symp->name; *name; name++) {
695     if ( *name == '.' || *name == '$' ) {
696       return FALSE;
697     }
698   }
699   return TRUE;
700 }
701
702 done()
703 {
704
705     exit(0);
706 }