xref: /openbsd-src/gnu/usr.bin/binutils-2.17/gprof/hist.c (revision 3d8817e467ea46cf4772788d6804dd293abfb01a)
1*3d8817e4Smiod /* hist.c  -  Histogram related operations.
2*3d8817e4Smiod 
3*3d8817e4Smiod    Copyright 1999, 2000, 2001, 2002, 2004, 2005
4*3d8817e4Smiod    Free Software Foundation, Inc.
5*3d8817e4Smiod 
6*3d8817e4Smiod    This file is part of GNU Binutils.
7*3d8817e4Smiod 
8*3d8817e4Smiod    This program is free software; you can redistribute it and/or modify
9*3d8817e4Smiod    it under the terms of the GNU General Public License as published by
10*3d8817e4Smiod    the Free Software Foundation; either version 2 of the License, or
11*3d8817e4Smiod    (at your option) any later version.
12*3d8817e4Smiod 
13*3d8817e4Smiod    This program is distributed in the hope that it will be useful,
14*3d8817e4Smiod    but WITHOUT ANY WARRANTY; without even the implied warranty of
15*3d8817e4Smiod    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16*3d8817e4Smiod    GNU General Public License for more details.
17*3d8817e4Smiod 
18*3d8817e4Smiod    You should have received a copy of the GNU General Public License
19*3d8817e4Smiod    along with this program; if not, write to the Free Software
20*3d8817e4Smiod    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
21*3d8817e4Smiod    02110-1301, USA.  */
22*3d8817e4Smiod 
23*3d8817e4Smiod #include "libiberty.h"
24*3d8817e4Smiod #include "gprof.h"
25*3d8817e4Smiod #include "search_list.h"
26*3d8817e4Smiod #include "source.h"
27*3d8817e4Smiod #include "symtab.h"
28*3d8817e4Smiod #include "corefile.h"
29*3d8817e4Smiod #include "gmon_io.h"
30*3d8817e4Smiod #include "gmon_out.h"
31*3d8817e4Smiod #include "hist.h"
32*3d8817e4Smiod #include "sym_ids.h"
33*3d8817e4Smiod #include "utils.h"
34*3d8817e4Smiod 
35*3d8817e4Smiod #define UNITS_TO_CODE (offset_to_code / sizeof(UNIT))
36*3d8817e4Smiod 
37*3d8817e4Smiod static void scale_and_align_entries (void);
38*3d8817e4Smiod static void print_header (int);
39*3d8817e4Smiod static void print_line (Sym *, double);
40*3d8817e4Smiod static int cmp_time (const PTR, const PTR);
41*3d8817e4Smiod 
42*3d8817e4Smiod /* Declarations of automatically generated functions to output blurbs.  */
43*3d8817e4Smiod extern void flat_blurb (FILE * fp);
44*3d8817e4Smiod 
45*3d8817e4Smiod bfd_vma s_lowpc;		/* Lowest address in .text.  */
46*3d8817e4Smiod bfd_vma s_highpc = 0;		/* Highest address in .text.  */
47*3d8817e4Smiod bfd_vma lowpc, highpc;		/* Same, but expressed in UNITs.  */
48*3d8817e4Smiod unsigned int hist_num_bins = 0;	/* Number of histogram samples.  */
49*3d8817e4Smiod int *hist_sample = 0;		/* Histogram samples (shorts in the file!).  */
50*3d8817e4Smiod double hist_scale;
51*3d8817e4Smiod static char hist_dimension[16] = "seconds";
52*3d8817e4Smiod static char hist_dimension_abbrev = 's';
53*3d8817e4Smiod 
54*3d8817e4Smiod static double accum_time;	/* Accumulated time so far for print_line(). */
55*3d8817e4Smiod static double total_time;	/* Total time for all routines.  */
56*3d8817e4Smiod 
57*3d8817e4Smiod /* Table of SI prefixes for powers of 10 (used to automatically
58*3d8817e4Smiod    scale some of the values in the flat profile).  */
59*3d8817e4Smiod const struct
60*3d8817e4Smiod   {
61*3d8817e4Smiod     char prefix;
62*3d8817e4Smiod     double scale;
63*3d8817e4Smiod   }
64*3d8817e4Smiod SItab[] =
65*3d8817e4Smiod {
66*3d8817e4Smiod   { 'T', 1e-12 },				/* tera */
67*3d8817e4Smiod   { 'G', 1e-09 },				/* giga */
68*3d8817e4Smiod   { 'M', 1e-06 },				/* mega */
69*3d8817e4Smiod   { 'K', 1e-03 },				/* kilo */
70*3d8817e4Smiod   { ' ', 1e-00 },
71*3d8817e4Smiod   { 'm', 1e+03 },				/* milli */
72*3d8817e4Smiod   { 'u', 1e+06 },				/* micro */
73*3d8817e4Smiod   { 'n', 1e+09 },				/* nano */
74*3d8817e4Smiod   { 'p', 1e+12 },				/* pico */
75*3d8817e4Smiod   { 'f', 1e+15 },				/* femto */
76*3d8817e4Smiod   { 'a', 1e+18 }				/* ato */
77*3d8817e4Smiod };
78*3d8817e4Smiod 
79*3d8817e4Smiod 
80*3d8817e4Smiod /* Read the histogram from file IFP.  FILENAME is the name of IFP and
81*3d8817e4Smiod    is provided for formatting error messages only.  */
82*3d8817e4Smiod 
83*3d8817e4Smiod void
hist_read_rec(FILE * ifp,const char * filename)84*3d8817e4Smiod hist_read_rec (FILE * ifp, const char *filename)
85*3d8817e4Smiod {
86*3d8817e4Smiod   bfd_vma n_lowpc, n_highpc;
87*3d8817e4Smiod   unsigned int i, ncnt, profrate;
88*3d8817e4Smiod   UNIT count;
89*3d8817e4Smiod 
90*3d8817e4Smiod   if (gmon_io_read_vma (ifp, &n_lowpc)
91*3d8817e4Smiod       || gmon_io_read_vma (ifp, &n_highpc)
92*3d8817e4Smiod       || gmon_io_read_32 (ifp, &ncnt)
93*3d8817e4Smiod       || gmon_io_read_32 (ifp, &profrate)
94*3d8817e4Smiod       || gmon_io_read (ifp, hist_dimension, 15)
95*3d8817e4Smiod       || gmon_io_read (ifp, &hist_dimension_abbrev, 1))
96*3d8817e4Smiod     {
97*3d8817e4Smiod       fprintf (stderr, _("%s: %s: unexpected end of file\n"),
98*3d8817e4Smiod 	       whoami, filename);
99*3d8817e4Smiod 
100*3d8817e4Smiod       done (1);
101*3d8817e4Smiod     }
102*3d8817e4Smiod 
103*3d8817e4Smiod   if (!s_highpc)
104*3d8817e4Smiod     {
105*3d8817e4Smiod       /* This is the first histogram record.  */
106*3d8817e4Smiod       s_lowpc = n_lowpc;
107*3d8817e4Smiod       s_highpc = n_highpc;
108*3d8817e4Smiod       lowpc = (bfd_vma) n_lowpc / sizeof (UNIT);
109*3d8817e4Smiod       highpc = (bfd_vma) n_highpc / sizeof (UNIT);
110*3d8817e4Smiod       hist_num_bins = ncnt;
111*3d8817e4Smiod       hz = profrate;
112*3d8817e4Smiod     }
113*3d8817e4Smiod 
114*3d8817e4Smiod   DBG (SAMPLEDEBUG,
115*3d8817e4Smiod        printf ("[hist_read_rec] n_lowpc 0x%lx n_highpc 0x%lx ncnt %u\n",
116*3d8817e4Smiod 	       (unsigned long) n_lowpc, (unsigned long) n_highpc, ncnt);
117*3d8817e4Smiod        printf ("[hist_read_rec] s_lowpc 0x%lx s_highpc 0x%lx nsamples %u\n",
118*3d8817e4Smiod 	       (unsigned long) s_lowpc, (unsigned long) s_highpc,
119*3d8817e4Smiod 	       hist_num_bins);
120*3d8817e4Smiod        printf ("[hist_read_rec]   lowpc 0x%lx   highpc 0x%lx\n",
121*3d8817e4Smiod 	       (unsigned long) lowpc, (unsigned long) highpc));
122*3d8817e4Smiod 
123*3d8817e4Smiod   if (n_lowpc != s_lowpc || n_highpc != s_highpc
124*3d8817e4Smiod       || ncnt != hist_num_bins || hz != (int) profrate)
125*3d8817e4Smiod     {
126*3d8817e4Smiod       fprintf (stderr, _("%s: `%s' is incompatible with first gmon file\n"),
127*3d8817e4Smiod 	       whoami, filename);
128*3d8817e4Smiod       done (1);
129*3d8817e4Smiod     }
130*3d8817e4Smiod 
131*3d8817e4Smiod   if (!hist_sample)
132*3d8817e4Smiod     {
133*3d8817e4Smiod       hist_sample = (int *) xmalloc (hist_num_bins * sizeof (hist_sample[0]));
134*3d8817e4Smiod       memset (hist_sample, 0, hist_num_bins * sizeof (hist_sample[0]));
135*3d8817e4Smiod     }
136*3d8817e4Smiod 
137*3d8817e4Smiod   for (i = 0; i < hist_num_bins; ++i)
138*3d8817e4Smiod     {
139*3d8817e4Smiod       if (fread (&count[0], sizeof (count), 1, ifp) != 1)
140*3d8817e4Smiod 	{
141*3d8817e4Smiod 	  fprintf (stderr,
142*3d8817e4Smiod 		  _("%s: %s: unexpected EOF after reading %u of %u samples\n"),
143*3d8817e4Smiod 		   whoami, filename, i, hist_num_bins);
144*3d8817e4Smiod 	  done (1);
145*3d8817e4Smiod 	}
146*3d8817e4Smiod       hist_sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) & count[0]);
147*3d8817e4Smiod       DBG (SAMPLEDEBUG,
148*3d8817e4Smiod 	   printf ("[hist_read_rec] 0x%lx: %u\n",
149*3d8817e4Smiod 		   (unsigned long) (n_lowpc + i * (n_highpc - n_lowpc) / ncnt),
150*3d8817e4Smiod 		   hist_sample[i]));
151*3d8817e4Smiod     }
152*3d8817e4Smiod }
153*3d8817e4Smiod 
154*3d8817e4Smiod 
155*3d8817e4Smiod /* Write execution histogram to file OFP.  FILENAME is the name
156*3d8817e4Smiod    of OFP and is provided for formatting error-messages only.  */
157*3d8817e4Smiod 
158*3d8817e4Smiod void
hist_write_hist(FILE * ofp,const char * filename)159*3d8817e4Smiod hist_write_hist (FILE * ofp, const char *filename)
160*3d8817e4Smiod {
161*3d8817e4Smiod   UNIT count;
162*3d8817e4Smiod   unsigned int i;
163*3d8817e4Smiod 
164*3d8817e4Smiod   /* Write header.  */
165*3d8817e4Smiod 
166*3d8817e4Smiod   if (gmon_io_write_8 (ofp, GMON_TAG_TIME_HIST)
167*3d8817e4Smiod       || gmon_io_write_vma (ofp, s_lowpc)
168*3d8817e4Smiod       || gmon_io_write_vma (ofp, s_highpc)
169*3d8817e4Smiod       || gmon_io_write_32 (ofp, hist_num_bins)
170*3d8817e4Smiod       || gmon_io_write_32 (ofp, hz)
171*3d8817e4Smiod       || gmon_io_write (ofp, hist_dimension, 15)
172*3d8817e4Smiod       || gmon_io_write (ofp, &hist_dimension_abbrev, 1))
173*3d8817e4Smiod     {
174*3d8817e4Smiod       perror (filename);
175*3d8817e4Smiod       done (1);
176*3d8817e4Smiod     }
177*3d8817e4Smiod 
178*3d8817e4Smiod   for (i = 0; i < hist_num_bins; ++i)
179*3d8817e4Smiod     {
180*3d8817e4Smiod       bfd_put_16 (core_bfd, (bfd_vma) hist_sample[i], (bfd_byte *) &count[0]);
181*3d8817e4Smiod 
182*3d8817e4Smiod       if (fwrite (&count[0], sizeof (count), 1, ofp) != 1)
183*3d8817e4Smiod 	{
184*3d8817e4Smiod 	  perror (filename);
185*3d8817e4Smiod 	  done (1);
186*3d8817e4Smiod 	}
187*3d8817e4Smiod     }
188*3d8817e4Smiod }
189*3d8817e4Smiod 
190*3d8817e4Smiod 
191*3d8817e4Smiod /* Calculate scaled entry point addresses (to save time in
192*3d8817e4Smiod    hist_assign_samples), and, on architectures that have procedure
193*3d8817e4Smiod    entry masks at the start of a function, possibly push the scaled
194*3d8817e4Smiod    entry points over the procedure entry mask, if it turns out that
195*3d8817e4Smiod    the entry point is in one bin and the code for a routine is in the
196*3d8817e4Smiod    next bin.  */
197*3d8817e4Smiod 
198*3d8817e4Smiod static void
scale_and_align_entries()199*3d8817e4Smiod scale_and_align_entries ()
200*3d8817e4Smiod {
201*3d8817e4Smiod   Sym *sym;
202*3d8817e4Smiod   bfd_vma bin_of_entry;
203*3d8817e4Smiod   bfd_vma bin_of_code;
204*3d8817e4Smiod 
205*3d8817e4Smiod   for (sym = symtab.base; sym < symtab.limit; sym++)
206*3d8817e4Smiod     {
207*3d8817e4Smiod       sym->hist.scaled_addr = sym->addr / sizeof (UNIT);
208*3d8817e4Smiod       bin_of_entry = (sym->hist.scaled_addr - lowpc) / hist_scale;
209*3d8817e4Smiod       bin_of_code = ((sym->hist.scaled_addr + UNITS_TO_CODE - lowpc)
210*3d8817e4Smiod 		     / hist_scale);
211*3d8817e4Smiod       if (bin_of_entry < bin_of_code)
212*3d8817e4Smiod 	{
213*3d8817e4Smiod 	  DBG (SAMPLEDEBUG,
214*3d8817e4Smiod 	       printf ("[scale_and_align_entries] pushing 0x%lx to 0x%lx\n",
215*3d8817e4Smiod 		       (unsigned long) sym->hist.scaled_addr,
216*3d8817e4Smiod 		       (unsigned long) (sym->hist.scaled_addr
217*3d8817e4Smiod 					+ UNITS_TO_CODE)));
218*3d8817e4Smiod 	  sym->hist.scaled_addr += UNITS_TO_CODE;
219*3d8817e4Smiod 	}
220*3d8817e4Smiod     }
221*3d8817e4Smiod }
222*3d8817e4Smiod 
223*3d8817e4Smiod 
224*3d8817e4Smiod /* Assign samples to the symbol to which they belong.
225*3d8817e4Smiod 
226*3d8817e4Smiod    Histogram bin I covers some address range [BIN_LOWPC,BIN_HIGH_PC)
227*3d8817e4Smiod    which may overlap one more symbol address ranges.  If a symbol
228*3d8817e4Smiod    overlaps with the bin's address range by O percent, then O percent
229*3d8817e4Smiod    of the bin's count is credited to that symbol.
230*3d8817e4Smiod 
231*3d8817e4Smiod    There are three cases as to where BIN_LOW_PC and BIN_HIGH_PC can be
232*3d8817e4Smiod    with respect to the symbol's address range [SYM_LOW_PC,
233*3d8817e4Smiod    SYM_HIGH_PC) as shown in the following diagram.  OVERLAP computes
234*3d8817e4Smiod    the distance (in UNITs) between the arrows, the fraction of the
235*3d8817e4Smiod    sample that is to be credited to the symbol which starts at
236*3d8817e4Smiod    SYM_LOW_PC.
237*3d8817e4Smiod 
238*3d8817e4Smiod 	  sym_low_pc                                      sym_high_pc
239*3d8817e4Smiod 	       |                                               |
240*3d8817e4Smiod 	       v                                               v
241*3d8817e4Smiod 
242*3d8817e4Smiod 	       +-----------------------------------------------+
243*3d8817e4Smiod 	       |                                               |
244*3d8817e4Smiod 	  |  ->|    |<-         ->|         |<-         ->|    |<-  |
245*3d8817e4Smiod 	  |         |             |         |             |         |
246*3d8817e4Smiod 	  +---------+             +---------+             +---------+
247*3d8817e4Smiod 
248*3d8817e4Smiod 	  ^         ^             ^         ^             ^         ^
249*3d8817e4Smiod 	  |         |             |         |             |         |
250*3d8817e4Smiod      bin_low_pc bin_high_pc  bin_low_pc bin_high_pc  bin_low_pc bin_high_pc
251*3d8817e4Smiod 
252*3d8817e4Smiod    For the VAX we assert that samples will never fall in the first two
253*3d8817e4Smiod    bytes of any routine, since that is the entry mask, thus we call
254*3d8817e4Smiod    scale_and_align_entries() to adjust the entry points if the entry
255*3d8817e4Smiod    mask falls in one bin but the code for the routine doesn't start
256*3d8817e4Smiod    until the next bin.  In conjunction with the alignment of routine
257*3d8817e4Smiod    addresses, this should allow us to have only one sample for every
258*3d8817e4Smiod    four bytes of text space and never have any overlap (the two end
259*3d8817e4Smiod    cases, above).  */
260*3d8817e4Smiod 
261*3d8817e4Smiod void
hist_assign_samples()262*3d8817e4Smiod hist_assign_samples ()
263*3d8817e4Smiod {
264*3d8817e4Smiod   bfd_vma bin_low_pc, bin_high_pc;
265*3d8817e4Smiod   bfd_vma sym_low_pc, sym_high_pc;
266*3d8817e4Smiod   bfd_vma overlap, addr;
267*3d8817e4Smiod   unsigned int bin_count;
268*3d8817e4Smiod   unsigned int i, j;
269*3d8817e4Smiod   double time, credit;
270*3d8817e4Smiod 
271*3d8817e4Smiod   /* Read samples and assign to symbols.  */
272*3d8817e4Smiod   hist_scale = highpc - lowpc;
273*3d8817e4Smiod   hist_scale /= hist_num_bins;
274*3d8817e4Smiod   scale_and_align_entries ();
275*3d8817e4Smiod 
276*3d8817e4Smiod   /* Iterate over all sample bins.  */
277*3d8817e4Smiod   for (i = 0, j = 1; i < hist_num_bins; ++i)
278*3d8817e4Smiod     {
279*3d8817e4Smiod       bin_count = hist_sample[i];
280*3d8817e4Smiod       if (! bin_count)
281*3d8817e4Smiod 	continue;
282*3d8817e4Smiod 
283*3d8817e4Smiod       bin_low_pc = lowpc + (bfd_vma) (hist_scale * i);
284*3d8817e4Smiod       bin_high_pc = lowpc + (bfd_vma) (hist_scale * (i + 1));
285*3d8817e4Smiod       time = bin_count;
286*3d8817e4Smiod 
287*3d8817e4Smiod       DBG (SAMPLEDEBUG,
288*3d8817e4Smiod 	   printf (
289*3d8817e4Smiod       "[assign_samples] bin_low_pc=0x%lx, bin_high_pc=0x%lx, bin_count=%u\n",
290*3d8817e4Smiod 		    (unsigned long) (sizeof (UNIT) * bin_low_pc),
291*3d8817e4Smiod 		    (unsigned long) (sizeof (UNIT) * bin_high_pc),
292*3d8817e4Smiod 		    bin_count));
293*3d8817e4Smiod       total_time += time;
294*3d8817e4Smiod 
295*3d8817e4Smiod       /* Credit all symbols that are covered by bin I.  */
296*3d8817e4Smiod       for (j = j - 1; j < symtab.len; ++j)
297*3d8817e4Smiod 	{
298*3d8817e4Smiod 	  sym_low_pc = symtab.base[j].hist.scaled_addr;
299*3d8817e4Smiod 	  sym_high_pc = symtab.base[j + 1].hist.scaled_addr;
300*3d8817e4Smiod 
301*3d8817e4Smiod 	  /* If high end of bin is below entry address,
302*3d8817e4Smiod 	     go for next bin.  */
303*3d8817e4Smiod 	  if (bin_high_pc < sym_low_pc)
304*3d8817e4Smiod 	    break;
305*3d8817e4Smiod 
306*3d8817e4Smiod 	  /* If low end of bin is above high end of symbol,
307*3d8817e4Smiod 	     go for next symbol.  */
308*3d8817e4Smiod 	  if (bin_low_pc >= sym_high_pc)
309*3d8817e4Smiod 	    continue;
310*3d8817e4Smiod 
311*3d8817e4Smiod 	  overlap =
312*3d8817e4Smiod 	    MIN (bin_high_pc, sym_high_pc) - MAX (bin_low_pc, sym_low_pc);
313*3d8817e4Smiod 	  if (overlap > 0)
314*3d8817e4Smiod 	    {
315*3d8817e4Smiod 	      DBG (SAMPLEDEBUG,
316*3d8817e4Smiod 		   printf (
317*3d8817e4Smiod 	       "[assign_samples] [0x%lx,0x%lx) %s gets %f ticks %ld overlap\n",
318*3d8817e4Smiod 			   (unsigned long) symtab.base[j].addr,
319*3d8817e4Smiod 			   (unsigned long) (sizeof (UNIT) * sym_high_pc),
320*3d8817e4Smiod 			   symtab.base[j].name, overlap * time / hist_scale,
321*3d8817e4Smiod 			   (long) overlap));
322*3d8817e4Smiod 
323*3d8817e4Smiod 	      addr = symtab.base[j].addr;
324*3d8817e4Smiod 	      credit = overlap * time / hist_scale;
325*3d8817e4Smiod 
326*3d8817e4Smiod 	      /* Credit symbol if it appears in INCL_FLAT or that
327*3d8817e4Smiod 		 table is empty and it does not appear it in
328*3d8817e4Smiod 		 EXCL_FLAT.  */
329*3d8817e4Smiod 	      if (sym_lookup (&syms[INCL_FLAT], addr)
330*3d8817e4Smiod 		  || (syms[INCL_FLAT].len == 0
331*3d8817e4Smiod 		      && !sym_lookup (&syms[EXCL_FLAT], addr)))
332*3d8817e4Smiod 		{
333*3d8817e4Smiod 		  symtab.base[j].hist.time += credit;
334*3d8817e4Smiod 		}
335*3d8817e4Smiod 	      else
336*3d8817e4Smiod 		{
337*3d8817e4Smiod 		  total_time -= credit;
338*3d8817e4Smiod 		}
339*3d8817e4Smiod 	    }
340*3d8817e4Smiod 	}
341*3d8817e4Smiod     }
342*3d8817e4Smiod 
343*3d8817e4Smiod   DBG (SAMPLEDEBUG, printf ("[assign_samples] total_time %f\n",
344*3d8817e4Smiod 			    total_time));
345*3d8817e4Smiod }
346*3d8817e4Smiod 
347*3d8817e4Smiod 
348*3d8817e4Smiod /* Print header for flag histogram profile.  */
349*3d8817e4Smiod 
350*3d8817e4Smiod static void
print_header(int prefix)351*3d8817e4Smiod print_header (int prefix)
352*3d8817e4Smiod {
353*3d8817e4Smiod   char unit[64];
354*3d8817e4Smiod 
355*3d8817e4Smiod   sprintf (unit, _("%c%c/call"), prefix, hist_dimension_abbrev);
356*3d8817e4Smiod 
357*3d8817e4Smiod   if (bsd_style_output)
358*3d8817e4Smiod     {
359*3d8817e4Smiod       printf (_("\ngranularity: each sample hit covers %ld byte(s)"),
360*3d8817e4Smiod 	      (long) hist_scale * sizeof (UNIT));
361*3d8817e4Smiod       if (total_time > 0.0)
362*3d8817e4Smiod 	{
363*3d8817e4Smiod 	  printf (_(" for %.2f%% of %.2f %s\n\n"),
364*3d8817e4Smiod 		  100.0 / total_time, total_time / hz, hist_dimension);
365*3d8817e4Smiod 	}
366*3d8817e4Smiod     }
367*3d8817e4Smiod   else
368*3d8817e4Smiod     {
369*3d8817e4Smiod       printf (_("\nEach sample counts as %g %s.\n"), 1.0 / hz, hist_dimension);
370*3d8817e4Smiod     }
371*3d8817e4Smiod 
372*3d8817e4Smiod   if (total_time <= 0.0)
373*3d8817e4Smiod     {
374*3d8817e4Smiod       printf (_(" no time accumulated\n\n"));
375*3d8817e4Smiod 
376*3d8817e4Smiod       /* This doesn't hurt since all the numerators will be zero.  */
377*3d8817e4Smiod       total_time = 1.0;
378*3d8817e4Smiod     }
379*3d8817e4Smiod 
380*3d8817e4Smiod   printf ("%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s  %-8.8s\n",
381*3d8817e4Smiod 	  "%  ", _("cumulative"), _("self  "), "", _("self  "), _("total "),
382*3d8817e4Smiod 	  "");
383*3d8817e4Smiod   printf ("%5.5s %9.9s  %8.8s %8.8s %8.8s %8.8s  %-8.8s\n",
384*3d8817e4Smiod 	  _("time"), hist_dimension, hist_dimension, _("calls"), unit, unit,
385*3d8817e4Smiod 	  _("name"));
386*3d8817e4Smiod }
387*3d8817e4Smiod 
388*3d8817e4Smiod 
389*3d8817e4Smiod static void
print_line(Sym * sym,double scale)390*3d8817e4Smiod print_line (Sym *sym, double scale)
391*3d8817e4Smiod {
392*3d8817e4Smiod   if (ignore_zeros && sym->ncalls == 0 && sym->hist.time == 0)
393*3d8817e4Smiod     return;
394*3d8817e4Smiod 
395*3d8817e4Smiod   accum_time += sym->hist.time;
396*3d8817e4Smiod 
397*3d8817e4Smiod   if (bsd_style_output)
398*3d8817e4Smiod     printf ("%5.1f %10.2f %8.2f",
399*3d8817e4Smiod 	    total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
400*3d8817e4Smiod 	    accum_time / hz, sym->hist.time / hz);
401*3d8817e4Smiod   else
402*3d8817e4Smiod     printf ("%6.2f %9.2f %8.2f",
403*3d8817e4Smiod 	    total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
404*3d8817e4Smiod 	    accum_time / hz, sym->hist.time / hz);
405*3d8817e4Smiod 
406*3d8817e4Smiod   if (sym->ncalls != 0)
407*3d8817e4Smiod     printf (" %8lu %8.2f %8.2f  ",
408*3d8817e4Smiod 	    sym->ncalls, scale * sym->hist.time / hz / sym->ncalls,
409*3d8817e4Smiod 	    scale * (sym->hist.time + sym->cg.child_time) / hz / sym->ncalls);
410*3d8817e4Smiod   else
411*3d8817e4Smiod     printf (" %8.8s %8.8s %8.8s  ", "", "", "");
412*3d8817e4Smiod 
413*3d8817e4Smiod   if (bsd_style_output)
414*3d8817e4Smiod     print_name (sym);
415*3d8817e4Smiod   else
416*3d8817e4Smiod     print_name_only (sym);
417*3d8817e4Smiod 
418*3d8817e4Smiod   printf ("\n");
419*3d8817e4Smiod }
420*3d8817e4Smiod 
421*3d8817e4Smiod 
422*3d8817e4Smiod /* Compare LP and RP.  The primary comparison key is execution time,
423*3d8817e4Smiod    the secondary is number of invocation, and the tertiary is the
424*3d8817e4Smiod    lexicographic order of the function names.  */
425*3d8817e4Smiod 
426*3d8817e4Smiod static int
cmp_time(const PTR lp,const PTR rp)427*3d8817e4Smiod cmp_time (const PTR lp, const PTR rp)
428*3d8817e4Smiod {
429*3d8817e4Smiod   const Sym *left = *(const Sym **) lp;
430*3d8817e4Smiod   const Sym *right = *(const Sym **) rp;
431*3d8817e4Smiod   double time_diff;
432*3d8817e4Smiod 
433*3d8817e4Smiod   time_diff = right->hist.time - left->hist.time;
434*3d8817e4Smiod 
435*3d8817e4Smiod   if (time_diff > 0.0)
436*3d8817e4Smiod     return 1;
437*3d8817e4Smiod 
438*3d8817e4Smiod   if (time_diff < 0.0)
439*3d8817e4Smiod     return -1;
440*3d8817e4Smiod 
441*3d8817e4Smiod   if (right->ncalls > left->ncalls)
442*3d8817e4Smiod     return 1;
443*3d8817e4Smiod 
444*3d8817e4Smiod   if (right->ncalls < left->ncalls)
445*3d8817e4Smiod     return -1;
446*3d8817e4Smiod 
447*3d8817e4Smiod   return strcmp (left->name, right->name);
448*3d8817e4Smiod }
449*3d8817e4Smiod 
450*3d8817e4Smiod 
451*3d8817e4Smiod /* Print the flat histogram profile.  */
452*3d8817e4Smiod 
453*3d8817e4Smiod void
hist_print()454*3d8817e4Smiod hist_print ()
455*3d8817e4Smiod {
456*3d8817e4Smiod   Sym **time_sorted_syms, *top_dog, *sym;
457*3d8817e4Smiod   unsigned int index;
458*3d8817e4Smiod   unsigned log_scale;
459*3d8817e4Smiod   double top_time, time;
460*3d8817e4Smiod   bfd_vma addr;
461*3d8817e4Smiod 
462*3d8817e4Smiod   if (first_output)
463*3d8817e4Smiod     first_output = FALSE;
464*3d8817e4Smiod   else
465*3d8817e4Smiod     printf ("\f\n");
466*3d8817e4Smiod 
467*3d8817e4Smiod   accum_time = 0.0;
468*3d8817e4Smiod 
469*3d8817e4Smiod   if (bsd_style_output)
470*3d8817e4Smiod     {
471*3d8817e4Smiod       if (print_descriptions)
472*3d8817e4Smiod 	{
473*3d8817e4Smiod 	  printf (_("\n\n\nflat profile:\n"));
474*3d8817e4Smiod 	  flat_blurb (stdout);
475*3d8817e4Smiod 	}
476*3d8817e4Smiod     }
477*3d8817e4Smiod   else
478*3d8817e4Smiod     {
479*3d8817e4Smiod       printf (_("Flat profile:\n"));
480*3d8817e4Smiod     }
481*3d8817e4Smiod 
482*3d8817e4Smiod   /* Sort the symbol table by time (call-count and name as secondary
483*3d8817e4Smiod      and tertiary keys).  */
484*3d8817e4Smiod   time_sorted_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *));
485*3d8817e4Smiod 
486*3d8817e4Smiod   for (index = 0; index < symtab.len; ++index)
487*3d8817e4Smiod     time_sorted_syms[index] = &symtab.base[index];
488*3d8817e4Smiod 
489*3d8817e4Smiod   qsort (time_sorted_syms, symtab.len, sizeof (Sym *), cmp_time);
490*3d8817e4Smiod 
491*3d8817e4Smiod   if (bsd_style_output)
492*3d8817e4Smiod     {
493*3d8817e4Smiod       log_scale = 5;		/* Milli-seconds is BSD-default.  */
494*3d8817e4Smiod     }
495*3d8817e4Smiod   else
496*3d8817e4Smiod     {
497*3d8817e4Smiod       /* Search for symbol with highest per-call
498*3d8817e4Smiod 	 execution time and scale accordingly.  */
499*3d8817e4Smiod       log_scale = 0;
500*3d8817e4Smiod       top_dog = 0;
501*3d8817e4Smiod       top_time = 0.0;
502*3d8817e4Smiod 
503*3d8817e4Smiod       for (index = 0; index < symtab.len; ++index)
504*3d8817e4Smiod 	{
505*3d8817e4Smiod 	  sym = time_sorted_syms[index];
506*3d8817e4Smiod 
507*3d8817e4Smiod 	  if (sym->ncalls != 0)
508*3d8817e4Smiod 	    {
509*3d8817e4Smiod 	      time = (sym->hist.time + sym->cg.child_time) / sym->ncalls;
510*3d8817e4Smiod 
511*3d8817e4Smiod 	      if (time > top_time)
512*3d8817e4Smiod 		{
513*3d8817e4Smiod 		  top_dog = sym;
514*3d8817e4Smiod 		  top_time = time;
515*3d8817e4Smiod 		}
516*3d8817e4Smiod 	    }
517*3d8817e4Smiod 	}
518*3d8817e4Smiod 
519*3d8817e4Smiod       if (top_dog && top_dog->ncalls != 0 && top_time > 0.0)
520*3d8817e4Smiod 	{
521*3d8817e4Smiod 	  top_time /= hz;
522*3d8817e4Smiod 
523*3d8817e4Smiod 	  for (log_scale = 0; log_scale < ARRAY_SIZE (SItab); log_scale ++)
524*3d8817e4Smiod 	    {
525*3d8817e4Smiod 	      double scaled_value = SItab[log_scale].scale * top_time;
526*3d8817e4Smiod 
527*3d8817e4Smiod 	      if (scaled_value >= 1.0 && scaled_value < 1000.0)
528*3d8817e4Smiod 		break;
529*3d8817e4Smiod 	    }
530*3d8817e4Smiod 	}
531*3d8817e4Smiod     }
532*3d8817e4Smiod 
533*3d8817e4Smiod   /* For now, the dimension is always seconds.  In the future, we
534*3d8817e4Smiod      may also want to support other (pseudo-)dimensions (such as
535*3d8817e4Smiod      I-cache misses etc.).  */
536*3d8817e4Smiod   print_header (SItab[log_scale].prefix);
537*3d8817e4Smiod 
538*3d8817e4Smiod   for (index = 0; index < symtab.len; ++index)
539*3d8817e4Smiod     {
540*3d8817e4Smiod       addr = time_sorted_syms[index]->addr;
541*3d8817e4Smiod 
542*3d8817e4Smiod       /* Print symbol if its in INCL_FLAT table or that table
543*3d8817e4Smiod 	is empty and the symbol is not in EXCL_FLAT.  */
544*3d8817e4Smiod       if (sym_lookup (&syms[INCL_FLAT], addr)
545*3d8817e4Smiod 	  || (syms[INCL_FLAT].len == 0
546*3d8817e4Smiod 	      && !sym_lookup (&syms[EXCL_FLAT], addr)))
547*3d8817e4Smiod 	print_line (time_sorted_syms[index], SItab[log_scale].scale);
548*3d8817e4Smiod     }
549*3d8817e4Smiod 
550*3d8817e4Smiod   free (time_sorted_syms);
551*3d8817e4Smiod 
552*3d8817e4Smiod   if (print_descriptions && !bsd_style_output)
553*3d8817e4Smiod     flat_blurb (stdout);
554*3d8817e4Smiod }
555