xref: /openbsd/gnu/usr.bin/binutils/gprof/hist.c (revision c074d1c9)
1b55d4692Sfgsch /* hist.c  -  Histogram related operations.
2b55d4692Sfgsch 
3*c074d1c9Sdrahn    Copyright 2000, 2001, 2002 Free Software Foundation, Inc.
4b55d4692Sfgsch 
5b55d4692Sfgsch    This file is part of GNU Binutils.
6b55d4692Sfgsch 
7b55d4692Sfgsch    This program is free software; you can redistribute it and/or modify
8b55d4692Sfgsch    it under the terms of the GNU General Public License as published by
9b55d4692Sfgsch    the Free Software Foundation; either version 2 of the License, or
10b55d4692Sfgsch    (at your option) any later version.
11b55d4692Sfgsch 
12b55d4692Sfgsch    This program is distributed in the hope that it will be useful,
13b55d4692Sfgsch    but WITHOUT ANY WARRANTY; without even the implied warranty of
14b55d4692Sfgsch    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15b55d4692Sfgsch    GNU General Public License for more details.
16b55d4692Sfgsch 
17b55d4692Sfgsch    You should have received a copy of the GNU General Public License
18b55d4692Sfgsch    along with this program; if not, write to the Free Software
19b55d4692Sfgsch    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20b55d4692Sfgsch    02111-1307, USA.  */
21b55d4692Sfgsch 
222159047fSniklas #include "libiberty.h"
232159047fSniklas #include "gprof.h"
24*c074d1c9Sdrahn #include "search_list.h"
25*c074d1c9Sdrahn #include "source.h"
26*c074d1c9Sdrahn #include "symtab.h"
27b305b0f1Sespie #include "corefile.h"
282159047fSniklas #include "gmon_io.h"
292159047fSniklas #include "gmon_out.h"
302159047fSniklas #include "hist.h"
312159047fSniklas #include "sym_ids.h"
322159047fSniklas #include "utils.h"
332159047fSniklas 
34b305b0f1Sespie #define UNITS_TO_CODE (offset_to_code / sizeof(UNIT))
35b305b0f1Sespie 
36b305b0f1Sespie static void scale_and_align_entries PARAMS ((void));
37*c074d1c9Sdrahn static void print_header PARAMS ((int));
38*c074d1c9Sdrahn static void print_line PARAMS ((Sym *, double));
39*c074d1c9Sdrahn static int cmp_time PARAMS ((const PTR, const PTR));
40b305b0f1Sespie 
41b55d4692Sfgsch /* Declarations of automatically generated functions to output blurbs.  */
422159047fSniklas extern void flat_blurb PARAMS ((FILE * fp));
432159047fSniklas 
44b55d4692Sfgsch bfd_vma s_lowpc;		/* Lowest address in .text.  */
45b55d4692Sfgsch bfd_vma s_highpc = 0;		/* Highest address in .text.  */
46b55d4692Sfgsch bfd_vma lowpc, highpc;		/* Same, but expressed in UNITs.  */
47b55d4692Sfgsch int hist_num_bins = 0;		/* Number of histogram samples.  */
48b55d4692Sfgsch int *hist_sample = 0;		/* Histogram samples (shorts in the file!).  */
492159047fSniklas double hist_scale;
50b55d4692Sfgsch char hist_dimension[16] = "seconds";
512159047fSniklas char hist_dimension_abbrev = 's';
522159047fSniklas 
53b55d4692Sfgsch static double accum_time;	/* Accumulated time so far for print_line(). */
54b55d4692Sfgsch static double total_time;	/* Total time for all routines.  */
55b55d4692Sfgsch 
56b55d4692Sfgsch /* Table of SI prefixes for powers of 10 (used to automatically
57b55d4692Sfgsch    scale some of the values in the flat profile).  */
582159047fSniklas const struct
592159047fSniklas   {
602159047fSniklas     char prefix;
612159047fSniklas     double scale;
622159047fSniklas   }
632159047fSniklas SItab[] =
642159047fSniklas {
65b55d4692Sfgsch   { 'T', 1e-12 },				/* tera */
66b55d4692Sfgsch   { 'G', 1e-09 },				/* giga */
67b55d4692Sfgsch   { 'M', 1e-06 },				/* mega */
68b55d4692Sfgsch   { 'K', 1e-03 },				/* kilo */
69b55d4692Sfgsch   { ' ', 1e-00 },
70b55d4692Sfgsch   { 'm', 1e+03 },				/* milli */
71b55d4692Sfgsch   { 'u', 1e+06 },				/* micro */
72b55d4692Sfgsch   { 'n', 1e+09 },				/* nano */
73b55d4692Sfgsch   { 'p', 1e+12 },				/* pico */
74b55d4692Sfgsch   { 'f', 1e+15 },				/* femto */
75b55d4692Sfgsch   { 'a', 1e+18 }				/* ato */
762159047fSniklas };
772159047fSniklas 
78b55d4692Sfgsch 
79b55d4692Sfgsch /* Read the histogram from file IFP.  FILENAME is the name of IFP and
80b55d4692Sfgsch    is provided for formatting error messages only.  */
81b55d4692Sfgsch 
822159047fSniklas void
hist_read_rec(ifp,filename)83*c074d1c9Sdrahn hist_read_rec (ifp, filename)
84*c074d1c9Sdrahn      FILE * ifp;
85*c074d1c9Sdrahn      const char *filename;
862159047fSniklas {
872159047fSniklas   bfd_vma n_lowpc, n_highpc;
882159047fSniklas   int i, ncnt, profrate;
892159047fSniklas   UNIT count;
902159047fSniklas 
91b55d4692Sfgsch   if (gmon_io_read_vma (ifp, &n_lowpc)
92b55d4692Sfgsch       || gmon_io_read_vma (ifp, &n_highpc)
93b55d4692Sfgsch       || gmon_io_read_32 (ifp, &ncnt)
94b55d4692Sfgsch       || gmon_io_read_32 (ifp, &profrate)
95b55d4692Sfgsch       || gmon_io_read (ifp, hist_dimension, 15)
96b55d4692Sfgsch       || gmon_io_read (ifp, &hist_dimension_abbrev, 1))
972159047fSniklas     {
98b305b0f1Sespie       fprintf (stderr, _("%s: %s: unexpected end of file\n"),
992159047fSniklas 	       whoami, filename);
100b55d4692Sfgsch 
1012159047fSniklas       done (1);
1022159047fSniklas     }
1032159047fSniklas 
1042159047fSniklas   if (!s_highpc)
1052159047fSniklas     {
106b55d4692Sfgsch       /* This is the first histogram record.  */
1072159047fSniklas       s_lowpc = n_lowpc;
1082159047fSniklas       s_highpc = n_highpc;
1092159047fSniklas       lowpc = (bfd_vma) n_lowpc / sizeof (UNIT);
1102159047fSniklas       highpc = (bfd_vma) n_highpc / sizeof (UNIT);
1112159047fSniklas       hist_num_bins = ncnt;
1122159047fSniklas       hz = profrate;
1132159047fSniklas     }
1142159047fSniklas 
1152159047fSniklas   DBG (SAMPLEDEBUG,
1162159047fSniklas        printf ("[hist_read_rec] n_lowpc 0x%lx n_highpc 0x%lx ncnt %d\n",
117b305b0f1Sespie 	       (unsigned long) n_lowpc, (unsigned long) n_highpc, ncnt);
1182159047fSniklas        printf ("[hist_read_rec] s_lowpc 0x%lx s_highpc 0x%lx nsamples %d\n",
119b305b0f1Sespie 	       (unsigned long) s_lowpc, (unsigned long) s_highpc,
120b305b0f1Sespie 	       hist_num_bins);
1212159047fSniklas        printf ("[hist_read_rec]   lowpc 0x%lx   highpc 0x%lx\n",
122b305b0f1Sespie 	       (unsigned long) lowpc, (unsigned long) highpc));
1232159047fSniklas 
1242159047fSniklas   if (n_lowpc != s_lowpc || n_highpc != s_highpc
1252159047fSniklas       || ncnt != hist_num_bins || hz != profrate)
1262159047fSniklas     {
127b305b0f1Sespie       fprintf (stderr, _("%s: `%s' is incompatible with first gmon file\n"),
1282159047fSniklas 	       whoami, filename);
1292159047fSniklas       done (1);
1302159047fSniklas     }
1312159047fSniklas 
1322159047fSniklas   if (!hist_sample)
1332159047fSniklas     {
1342159047fSniklas       hist_sample = (int *) xmalloc (hist_num_bins * sizeof (hist_sample[0]));
1352159047fSniklas       memset (hist_sample, 0, hist_num_bins * sizeof (hist_sample[0]));
1362159047fSniklas     }
1372159047fSniklas 
1382159047fSniklas   for (i = 0; i < hist_num_bins; ++i)
1392159047fSniklas     {
1402159047fSniklas       if (fread (&count[0], sizeof (count), 1, ifp) != 1)
1412159047fSniklas 	{
1422159047fSniklas 	  fprintf (stderr,
143b305b0f1Sespie 		  _("%s: %s: unexpected EOF after reading %d of %d samples\n"),
1442159047fSniklas 		   whoami, filename, i, hist_num_bins);
1452159047fSniklas 	  done (1);
1462159047fSniklas 	}
1472159047fSniklas       hist_sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) & count[0]);
148b55d4692Sfgsch       DBG (SAMPLEDEBUG,
149b55d4692Sfgsch 	   printf ("[hist_read_rec] 0x%lx: %u\n",
150b55d4692Sfgsch 		   (unsigned long) (n_lowpc + i * (n_highpc - n_lowpc) / ncnt),
151b55d4692Sfgsch 		   hist_sample[i]));
1522159047fSniklas     }
1532159047fSniklas }
1542159047fSniklas 
1552159047fSniklas 
156b55d4692Sfgsch /* Write execution histogram to file OFP.  FILENAME is the name
157b55d4692Sfgsch    of OFP and is provided for formatting error-messages only.  */
158b55d4692Sfgsch 
1592159047fSniklas void
hist_write_hist(ofp,filename)160*c074d1c9Sdrahn hist_write_hist (ofp, filename)
161*c074d1c9Sdrahn      FILE * ofp;
162*c074d1c9Sdrahn      const char *filename;
1632159047fSniklas {
1642159047fSniklas   UNIT count;
1652159047fSniklas   int i;
1662159047fSniklas 
167b55d4692Sfgsch   /* Write header.  */
1682159047fSniklas 
169b55d4692Sfgsch   if (gmon_io_write_8 (ofp, GMON_TAG_TIME_HIST)
170b55d4692Sfgsch       || gmon_io_write_vma (ofp, s_lowpc)
171b55d4692Sfgsch       || gmon_io_write_vma (ofp, s_highpc)
172b55d4692Sfgsch       || gmon_io_write_32 (ofp, hist_num_bins)
173b55d4692Sfgsch       || gmon_io_write_32 (ofp, hz)
174b55d4692Sfgsch       || gmon_io_write (ofp, hist_dimension, 15)
175b55d4692Sfgsch       || gmon_io_write (ofp, &hist_dimension_abbrev, 1))
1762159047fSniklas     {
1772159047fSniklas       perror (filename);
1782159047fSniklas       done (1);
1792159047fSniklas     }
1802159047fSniklas 
1812159047fSniklas   for (i = 0; i < hist_num_bins; ++i)
1822159047fSniklas     {
183*c074d1c9Sdrahn       bfd_put_16 (core_bfd, (bfd_vma) hist_sample[i], (bfd_byte *) &count[0]);
184b55d4692Sfgsch 
1852159047fSniklas       if (fwrite (&count[0], sizeof (count), 1, ofp) != 1)
1862159047fSniklas 	{
1872159047fSniklas 	  perror (filename);
1882159047fSniklas 	  done (1);
1892159047fSniklas 	}
1902159047fSniklas     }
1912159047fSniklas }
1922159047fSniklas 
1932159047fSniklas 
194b55d4692Sfgsch /* Calculate scaled entry point addresses (to save time in
195b55d4692Sfgsch    hist_assign_samples), and, on architectures that have procedure
196b55d4692Sfgsch    entry masks at the start of a function, possibly push the scaled
197b55d4692Sfgsch    entry points over the procedure entry mask, if it turns out that
198b55d4692Sfgsch    the entry point is in one bin and the code for a routine is in the
199b55d4692Sfgsch    next bin.  */
200b55d4692Sfgsch 
2012159047fSniklas static void
scale_and_align_entries()202191aa565Sniklas scale_and_align_entries ()
2032159047fSniklas {
2042159047fSniklas   Sym *sym;
2052159047fSniklas   bfd_vma bin_of_entry;
2062159047fSniklas   bfd_vma bin_of_code;
2072159047fSniklas 
2082159047fSniklas   for (sym = symtab.base; sym < symtab.limit; sym++)
2092159047fSniklas     {
2102159047fSniklas       sym->hist.scaled_addr = sym->addr / sizeof (UNIT);
2112159047fSniklas       bin_of_entry = (sym->hist.scaled_addr - lowpc) / hist_scale;
212b55d4692Sfgsch       bin_of_code = ((sym->hist.scaled_addr + UNITS_TO_CODE - lowpc)
213b55d4692Sfgsch 		     / hist_scale);
2142159047fSniklas       if (bin_of_entry < bin_of_code)
2152159047fSniklas 	{
2162159047fSniklas 	  DBG (SAMPLEDEBUG,
2172159047fSniklas 	       printf ("[scale_and_align_entries] pushing 0x%lx to 0x%lx\n",
218b305b0f1Sespie 		       (unsigned long) sym->hist.scaled_addr,
219b305b0f1Sespie 		       (unsigned long) (sym->hist.scaled_addr
220b305b0f1Sespie 					+ UNITS_TO_CODE)));
221191aa565Sniklas 	  sym->hist.scaled_addr += UNITS_TO_CODE;
2222159047fSniklas 	}
2232159047fSniklas     }
2242159047fSniklas }
2252159047fSniklas 
2262159047fSniklas 
227b55d4692Sfgsch /* Assign samples to the symbol to which they belong.
228b55d4692Sfgsch 
229b55d4692Sfgsch    Histogram bin I covers some address range [BIN_LOWPC,BIN_HIGH_PC)
230b55d4692Sfgsch    which may overlap one more symbol address ranges.  If a symbol
231b55d4692Sfgsch    overlaps with the bin's address range by O percent, then O percent
232b55d4692Sfgsch    of the bin's count is credited to that symbol.
233b55d4692Sfgsch 
234b55d4692Sfgsch    There are three cases as to where BIN_LOW_PC and BIN_HIGH_PC can be
235b55d4692Sfgsch    with respect to the symbol's address range [SYM_LOW_PC,
236b55d4692Sfgsch    SYM_HIGH_PC) as shown in the following diagram.  OVERLAP computes
237b55d4692Sfgsch    the distance (in UNITs) between the arrows, the fraction of the
238b55d4692Sfgsch    sample that is to be credited to the symbol which starts at
239b55d4692Sfgsch    SYM_LOW_PC.
240b55d4692Sfgsch 
241b55d4692Sfgsch 	  sym_low_pc                                      sym_high_pc
242b55d4692Sfgsch 	       |                                               |
243b55d4692Sfgsch 	       v                                               v
244b55d4692Sfgsch 
245b55d4692Sfgsch 	       +-----------------------------------------------+
246b55d4692Sfgsch 	       |                                               |
247b55d4692Sfgsch 	  |  ->|    |<-         ->|         |<-         ->|    |<-  |
248b55d4692Sfgsch 	  |         |             |         |             |         |
249b55d4692Sfgsch 	  +---------+             +---------+             +---------+
250b55d4692Sfgsch 
251b55d4692Sfgsch 	  ^         ^             ^         ^             ^         ^
252b55d4692Sfgsch 	  |         |             |         |             |         |
253b55d4692Sfgsch      bin_low_pc bin_high_pc  bin_low_pc bin_high_pc  bin_low_pc bin_high_pc
254b55d4692Sfgsch 
255b55d4692Sfgsch    For the VAX we assert that samples will never fall in the first two
256b55d4692Sfgsch    bytes of any routine, since that is the entry mask, thus we call
257b55d4692Sfgsch    scale_and_align_entries() to adjust the entry points if the entry
258b55d4692Sfgsch    mask falls in one bin but the code for the routine doesn't start
259b55d4692Sfgsch    until the next bin.  In conjunction with the alignment of routine
260b55d4692Sfgsch    addresses, this should allow us to have only one sample for every
261b55d4692Sfgsch    four bytes of text space and never have any overlap (the two end
262b55d4692Sfgsch    cases, above).  */
263b55d4692Sfgsch 
2642159047fSniklas void
hist_assign_samples()265*c074d1c9Sdrahn hist_assign_samples ()
2662159047fSniklas {
2672159047fSniklas   bfd_vma bin_low_pc, bin_high_pc;
2682159047fSniklas   bfd_vma sym_low_pc, sym_high_pc;
2692159047fSniklas   bfd_vma overlap, addr;
270b305b0f1Sespie   int bin_count, i;
271b305b0f1Sespie   unsigned int j;
2722159047fSniklas   double time, credit;
2732159047fSniklas 
274b55d4692Sfgsch   /* Read samples and assign to symbols.  */
2752159047fSniklas   hist_scale = highpc - lowpc;
2762159047fSniklas   hist_scale /= hist_num_bins;
2772159047fSniklas   scale_and_align_entries ();
2782159047fSniklas 
279b55d4692Sfgsch   /* Iterate over all sample bins.  */
2802159047fSniklas   for (i = 0, j = 1; i < hist_num_bins; ++i)
2812159047fSniklas     {
2822159047fSniklas       bin_count = hist_sample[i];
2832159047fSniklas       if (! bin_count)
2842159047fSniklas 	continue;
285b55d4692Sfgsch 
2862159047fSniklas       bin_low_pc = lowpc + (bfd_vma) (hist_scale * i);
2872159047fSniklas       bin_high_pc = lowpc + (bfd_vma) (hist_scale * (i + 1));
2882159047fSniklas       time = bin_count;
289b55d4692Sfgsch 
2902159047fSniklas       DBG (SAMPLEDEBUG,
2912159047fSniklas 	   printf (
2922159047fSniklas       "[assign_samples] bin_low_pc=0x%lx, bin_high_pc=0x%lx, bin_count=%d\n",
293b305b0f1Sespie 		    (unsigned long) (sizeof (UNIT) * bin_low_pc),
294b305b0f1Sespie 		    (unsigned long) (sizeof (UNIT) * bin_high_pc),
2952159047fSniklas 		    bin_count));
2962159047fSniklas       total_time += time;
2972159047fSniklas 
298b55d4692Sfgsch       /* Credit all symbols that are covered by bin I.  */
2992159047fSniklas       for (j = j - 1; j < symtab.len; ++j)
3002159047fSniklas 	{
3012159047fSniklas 	  sym_low_pc = symtab.base[j].hist.scaled_addr;
3022159047fSniklas 	  sym_high_pc = symtab.base[j + 1].hist.scaled_addr;
303b55d4692Sfgsch 
304b55d4692Sfgsch 	  /* If high end of bin is below entry address,
305b55d4692Sfgsch 	     go for next bin.  */
3062159047fSniklas 	  if (bin_high_pc < sym_low_pc)
3072159047fSniklas 	    break;
308b55d4692Sfgsch 
309b55d4692Sfgsch 	  /* If low end of bin is above high end of symbol,
310b55d4692Sfgsch 	     go for next symbol.  */
3112159047fSniklas 	  if (bin_low_pc >= sym_high_pc)
3122159047fSniklas 	    continue;
313b55d4692Sfgsch 
3142159047fSniklas 	  overlap =
3152159047fSniklas 	    MIN (bin_high_pc, sym_high_pc) - MAX (bin_low_pc, sym_low_pc);
3162159047fSniklas 	  if (overlap > 0)
3172159047fSniklas 	    {
3182159047fSniklas 	      DBG (SAMPLEDEBUG,
3192159047fSniklas 		   printf (
3202159047fSniklas 	       "[assign_samples] [0x%lx,0x%lx) %s gets %f ticks %ld overlap\n",
321b305b0f1Sespie 			   (unsigned long) symtab.base[j].addr,
322b305b0f1Sespie 			   (unsigned long) (sizeof (UNIT) * sym_high_pc),
3232159047fSniklas 			   symtab.base[j].name, overlap * time / hist_scale,
324b305b0f1Sespie 			   (long) overlap));
325b55d4692Sfgsch 
3262159047fSniklas 	      addr = symtab.base[j].addr;
3272159047fSniklas 	      credit = overlap * time / hist_scale;
328b55d4692Sfgsch 
329b55d4692Sfgsch 	      /* Credit symbol if it appears in INCL_FLAT or that
330b55d4692Sfgsch 		 table is empty and it does not appear it in
331b55d4692Sfgsch 		 EXCL_FLAT.  */
3322159047fSniklas 	      if (sym_lookup (&syms[INCL_FLAT], addr)
3332159047fSniklas 		  || (syms[INCL_FLAT].len == 0
3342159047fSniklas 		      && !sym_lookup (&syms[EXCL_FLAT], addr)))
3352159047fSniklas 		{
3362159047fSniklas 		  symtab.base[j].hist.time += credit;
3372159047fSniklas 		}
3382159047fSniklas 	      else
3392159047fSniklas 		{
3402159047fSniklas 		  total_time -= credit;
3412159047fSniklas 		}
3422159047fSniklas 	    }
3432159047fSniklas 	}
3442159047fSniklas     }
345b55d4692Sfgsch 
3462159047fSniklas   DBG (SAMPLEDEBUG, printf ("[assign_samples] total_time %f\n",
3472159047fSniklas 			    total_time));
3482159047fSniklas }
3492159047fSniklas 
3502159047fSniklas 
351b55d4692Sfgsch /* Print header for flag histogram profile.  */
352b55d4692Sfgsch 
3532159047fSniklas static void
print_header(prefix)354*c074d1c9Sdrahn print_header (prefix)
355*c074d1c9Sdrahn      int prefix;
3562159047fSniklas {
3572159047fSniklas   char unit[64];
3582159047fSniklas 
359b305b0f1Sespie   sprintf (unit, _("%c%c/call"), prefix, hist_dimension_abbrev);
3602159047fSniklas 
3612159047fSniklas   if (bsd_style_output)
3622159047fSniklas     {
363b305b0f1Sespie       printf (_("\ngranularity: each sample hit covers %ld byte(s)"),
3642159047fSniklas 	      (long) hist_scale * sizeof (UNIT));
3652159047fSniklas       if (total_time > 0.0)
3662159047fSniklas 	{
367b305b0f1Sespie 	  printf (_(" for %.2f%% of %.2f %s\n\n"),
3682159047fSniklas 		  100.0 / total_time, total_time / hz, hist_dimension);
3692159047fSniklas 	}
3702159047fSniklas     }
3712159047fSniklas   else
3722159047fSniklas     {
373b305b0f1Sespie       printf (_("\nEach sample counts as %g %s.\n"), 1.0 / hz, hist_dimension);
3742159047fSniklas     }
3752159047fSniklas 
3762159047fSniklas   if (total_time <= 0.0)
3772159047fSniklas     {
378b305b0f1Sespie       printf (_(" no time accumulated\n\n"));
379b55d4692Sfgsch 
380b55d4692Sfgsch       /* This doesn't hurt since all the numerators will be zero.  */
3812159047fSniklas       total_time = 1.0;
3822159047fSniklas     }
3832159047fSniklas 
3842159047fSniklas   printf ("%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s  %-8.8s\n",
385b55d4692Sfgsch 	  "%  ", _("cumulative"), _("self  "), "", _("self  "), _("total "),
386b55d4692Sfgsch 	  "");
3872159047fSniklas   printf ("%5.5s %9.9s  %8.8s %8.8s %8.8s %8.8s  %-8.8s\n",
388b305b0f1Sespie 	  _("time"), hist_dimension, hist_dimension, _("calls"), unit, unit,
389b305b0f1Sespie 	  _("name"));
3902159047fSniklas }
3912159047fSniklas 
3922159047fSniklas 
3932159047fSniklas static void
print_line(sym,scale)394*c074d1c9Sdrahn print_line (sym, scale)
395*c074d1c9Sdrahn      Sym *sym;
396*c074d1c9Sdrahn      double scale;
3972159047fSniklas {
3982159047fSniklas   if (ignore_zeros && sym->ncalls == 0 && sym->hist.time == 0)
3992159047fSniklas     return;
4002159047fSniklas 
4012159047fSniklas   accum_time += sym->hist.time;
402b55d4692Sfgsch 
4032159047fSniklas   if (bsd_style_output)
4042159047fSniklas     printf ("%5.1f %10.2f %8.2f",
4052159047fSniklas 	    total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
4062159047fSniklas 	    accum_time / hz, sym->hist.time / hz);
4072159047fSniklas   else
4082159047fSniklas     printf ("%6.2f %9.2f %8.2f",
4092159047fSniklas 	    total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
4102159047fSniklas 	    accum_time / hz, sym->hist.time / hz);
411b55d4692Sfgsch 
412b305b0f1Sespie   if (sym->ncalls != 0)
413b305b0f1Sespie     printf (" %8lu %8.2f %8.2f  ",
4142159047fSniklas 	    sym->ncalls, scale * sym->hist.time / hz / sym->ncalls,
4152159047fSniklas 	    scale * (sym->hist.time + sym->cg.child_time) / hz / sym->ncalls);
4162159047fSniklas   else
4172159047fSniklas     printf (" %8.8s %8.8s %8.8s  ", "", "", "");
418b55d4692Sfgsch 
4192159047fSniklas   if (bsd_style_output)
4202159047fSniklas     print_name (sym);
4212159047fSniklas   else
4222159047fSniklas     print_name_only (sym);
423b55d4692Sfgsch 
4242159047fSniklas   printf ("\n");
4252159047fSniklas }
4262159047fSniklas 
4272159047fSniklas 
428b55d4692Sfgsch /* Compare LP and RP.  The primary comparison key is execution time,
429b55d4692Sfgsch    the secondary is number of invocation, and the tertiary is the
430b55d4692Sfgsch    lexicographic order of the function names.  */
431b55d4692Sfgsch 
4322159047fSniklas static int
cmp_time(lp,rp)433*c074d1c9Sdrahn cmp_time (lp, rp)
434*c074d1c9Sdrahn      const PTR lp;
435*c074d1c9Sdrahn      const PTR rp;
4362159047fSniklas {
4372159047fSniklas   const Sym *left = *(const Sym **) lp;
4382159047fSniklas   const Sym *right = *(const Sym **) rp;
4392159047fSniklas   double time_diff;
4402159047fSniklas 
4412159047fSniklas   time_diff = right->hist.time - left->hist.time;
442b55d4692Sfgsch 
4432159047fSniklas   if (time_diff > 0.0)
4442159047fSniklas     return 1;
445b55d4692Sfgsch 
4462159047fSniklas   if (time_diff < 0.0)
4472159047fSniklas     return -1;
4482159047fSniklas 
449b305b0f1Sespie   if (right->ncalls > left->ncalls)
4502159047fSniklas     return 1;
451b55d4692Sfgsch 
452b305b0f1Sespie   if (right->ncalls < left->ncalls)
4532159047fSniklas     return -1;
4542159047fSniklas 
4552159047fSniklas   return strcmp (left->name, right->name);
4562159047fSniklas }
4572159047fSniklas 
4582159047fSniklas 
459b55d4692Sfgsch /* Print the flat histogram profile.  */
460b55d4692Sfgsch 
4612159047fSniklas void
hist_print()462*c074d1c9Sdrahn hist_print ()
4632159047fSniklas {
4642159047fSniklas   Sym **time_sorted_syms, *top_dog, *sym;
465b305b0f1Sespie   unsigned int index;
466*c074d1c9Sdrahn   unsigned log_scale;
4672159047fSniklas   double top_time, time;
4682159047fSniklas   bfd_vma addr;
4692159047fSniklas 
4702159047fSniklas   if (first_output)
4712159047fSniklas     first_output = FALSE;
4722159047fSniklas   else
4732159047fSniklas     printf ("\f\n");
4742159047fSniklas 
4752159047fSniklas   accum_time = 0.0;
476b55d4692Sfgsch 
4772159047fSniklas   if (bsd_style_output)
4782159047fSniklas     {
4792159047fSniklas       if (print_descriptions)
4802159047fSniklas 	{
481b305b0f1Sespie 	  printf (_("\n\n\nflat profile:\n"));
4822159047fSniklas 	  flat_blurb (stdout);
4832159047fSniklas 	}
4842159047fSniklas     }
4852159047fSniklas   else
4862159047fSniklas     {
487b305b0f1Sespie       printf (_("Flat profile:\n"));
4882159047fSniklas     }
489b55d4692Sfgsch 
490b55d4692Sfgsch   /* Sort the symbol table by time (call-count and name as secondary
491b55d4692Sfgsch      and tertiary keys).  */
4922159047fSniklas   time_sorted_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *));
493b55d4692Sfgsch 
4942159047fSniklas   for (index = 0; index < symtab.len; ++index)
4952159047fSniklas     time_sorted_syms[index] = &symtab.base[index];
496b55d4692Sfgsch 
4972159047fSniklas   qsort (time_sorted_syms, symtab.len, sizeof (Sym *), cmp_time);
4982159047fSniklas 
4992159047fSniklas   if (bsd_style_output)
5002159047fSniklas     {
501b55d4692Sfgsch       log_scale = 5;		/* Milli-seconds is BSD-default.  */
5022159047fSniklas     }
5032159047fSniklas   else
5042159047fSniklas     {
505b55d4692Sfgsch       /* Search for symbol with highest per-call
506b55d4692Sfgsch 	 execution time and scale accordingly.  */
5072159047fSniklas       log_scale = 0;
5082159047fSniklas       top_dog = 0;
5092159047fSniklas       top_time = 0.0;
510b55d4692Sfgsch 
5112159047fSniklas       for (index = 0; index < symtab.len; ++index)
5122159047fSniklas 	{
5132159047fSniklas 	  sym = time_sorted_syms[index];
514b55d4692Sfgsch 
515b305b0f1Sespie 	  if (sym->ncalls != 0)
5162159047fSniklas 	    {
5172159047fSniklas 	      time = (sym->hist.time + sym->cg.child_time) / sym->ncalls;
518b55d4692Sfgsch 
5192159047fSniklas 	      if (time > top_time)
5202159047fSniklas 		{
5212159047fSniklas 		  top_dog = sym;
5222159047fSniklas 		  top_time = time;
5232159047fSniklas 		}
5242159047fSniklas 	    }
5252159047fSniklas 	}
526b55d4692Sfgsch 
527b305b0f1Sespie       if (top_dog && top_dog->ncalls != 0 && top_time > 0.0)
5282159047fSniklas 	{
5292159047fSniklas 	  top_time /= hz;
530b55d4692Sfgsch 
531*c074d1c9Sdrahn 	  for (log_scale = 0; log_scale < ARRAY_SIZE (SItab); log_scale ++)
5322159047fSniklas 	    {
533*c074d1c9Sdrahn 	      double scaled_value = SItab[log_scale].scale * top_time;
534*c074d1c9Sdrahn 
535*c074d1c9Sdrahn 	      if (scaled_value >= 1.0 && scaled_value < 1000.0)
536*c074d1c9Sdrahn 		break;
5372159047fSniklas 	    }
5382159047fSniklas 	}
5392159047fSniklas     }
5402159047fSniklas 
541b55d4692Sfgsch   /* For now, the dimension is always seconds.  In the future, we
542b55d4692Sfgsch      may also want to support other (pseudo-)dimensions (such as
543b55d4692Sfgsch      I-cache misses etc.).  */
5442159047fSniklas   print_header (SItab[log_scale].prefix);
545b55d4692Sfgsch 
5462159047fSniklas   for (index = 0; index < symtab.len; ++index)
5472159047fSniklas     {
5482159047fSniklas       addr = time_sorted_syms[index]->addr;
549b55d4692Sfgsch 
550b55d4692Sfgsch       /* Print symbol if its in INCL_FLAT table or that table
551b55d4692Sfgsch 	is empty and the symbol is not in EXCL_FLAT.  */
5522159047fSniklas       if (sym_lookup (&syms[INCL_FLAT], addr)
5532159047fSniklas 	  || (syms[INCL_FLAT].len == 0
5542159047fSniklas 	      && !sym_lookup (&syms[EXCL_FLAT], addr)))
5552159047fSniklas 	print_line (time_sorted_syms[index], SItab[log_scale].scale);
5562159047fSniklas     }
557b55d4692Sfgsch 
5582159047fSniklas   free (time_sorted_syms);
5592159047fSniklas 
5602159047fSniklas   if (print_descriptions && !bsd_style_output)
5612159047fSniklas     flat_blurb (stdout);
5622159047fSniklas }
563