xref: /openbsd/gnu/usr.bin/binutils/gprof/hist.c (revision 191aa565)
12159047fSniklas /*
22159047fSniklas  * Histogram related operations.
32159047fSniklas  */
42159047fSniklas #include <stdio.h>
52159047fSniklas #include "libiberty.h"
62159047fSniklas #include "gprof.h"
72159047fSniklas #include "core.h"
82159047fSniklas #include "gmon_io.h"
92159047fSniklas #include "gmon_out.h"
102159047fSniklas #include "hist.h"
112159047fSniklas #include "symtab.h"
122159047fSniklas #include "sym_ids.h"
132159047fSniklas #include "utils.h"
142159047fSniklas 
152159047fSniklas /* declarations of automatically generated functions to output blurbs: */
162159047fSniklas extern void flat_blurb PARAMS ((FILE * fp));
172159047fSniklas 
182159047fSniklas bfd_vma s_lowpc;		/* lowest address in .text */
192159047fSniklas bfd_vma s_highpc = 0;		/* highest address in .text */
202159047fSniklas bfd_vma lowpc, highpc;		/* same, but expressed in UNITs */
212159047fSniklas int hist_num_bins = 0;		/* number of histogram samples */
222159047fSniklas int *hist_sample = 0;		/* histogram samples (shorts in the file!) */
232159047fSniklas double hist_scale;
242159047fSniklas char hist_dimension[sizeof (((struct gmon_hist_hdr *) 0)->dimen) + 1] =
252159047fSniklas   "seconds";
262159047fSniklas char hist_dimension_abbrev = 's';
272159047fSniklas 
282159047fSniklas static double accum_time;	/* accumulated time so far for print_line() */
292159047fSniklas static double total_time;	/* total time for all routines */
302159047fSniklas /*
312159047fSniklas  * Table of SI prefixes for powers of 10 (used to automatically
322159047fSniklas  * scale some of the values in the flat profile).
332159047fSniklas  */
342159047fSniklas const struct
352159047fSniklas   {
362159047fSniklas     char prefix;
372159047fSniklas     double scale;
382159047fSniklas   }
392159047fSniklas SItab[] =
402159047fSniklas {
412159047fSniklas   {
422159047fSniklas     'T', 1e-12
432159047fSniklas   }
442159047fSniklas   ,				/* tera */
452159047fSniklas   {
462159047fSniklas     'G', 1e-09
472159047fSniklas   }
482159047fSniklas   ,				/* giga */
492159047fSniklas   {
502159047fSniklas     'M', 1e-06
512159047fSniklas   }
522159047fSniklas   ,				/* mega */
532159047fSniklas   {
542159047fSniklas     'K', 1e-03
552159047fSniklas   }
562159047fSniklas   ,				/* kilo */
572159047fSniklas   {
582159047fSniklas     ' ', 1e-00
592159047fSniklas   }
602159047fSniklas   ,
612159047fSniklas   {
622159047fSniklas     'm', 1e+03
632159047fSniklas   }
642159047fSniklas   ,				/* milli */
652159047fSniklas   {
662159047fSniklas     'u', 1e+06
672159047fSniklas   }
682159047fSniklas   ,				/* micro */
692159047fSniklas   {
702159047fSniklas     'n', 1e+09
712159047fSniklas   }
722159047fSniklas   ,				/* nano */
732159047fSniklas   {
742159047fSniklas     'p', 1e+12
752159047fSniklas   }
762159047fSniklas   ,				/* pico */
772159047fSniklas   {
782159047fSniklas     'f', 1e+15
792159047fSniklas   }
802159047fSniklas   ,				/* femto */
812159047fSniklas   {
822159047fSniklas     'a', 1e+18
832159047fSniklas   }
842159047fSniklas   ,				/* ato */
852159047fSniklas };
862159047fSniklas 
872159047fSniklas /*
882159047fSniklas  * Read the histogram from file IFP.  FILENAME is the name of IFP and
892159047fSniklas  * is provided for formatting error messages only.
902159047fSniklas  */
912159047fSniklas void
922159047fSniklas DEFUN (hist_read_rec, (ifp, filename), FILE * ifp AND const char *filename)
932159047fSniklas {
942159047fSniklas   struct gmon_hist_hdr hdr;
952159047fSniklas   bfd_vma n_lowpc, n_highpc;
962159047fSniklas   int i, ncnt, profrate;
972159047fSniklas   UNIT count;
982159047fSniklas 
992159047fSniklas   if (fread (&hdr, sizeof (hdr), 1, ifp) != 1)
1002159047fSniklas     {
1012159047fSniklas       fprintf (stderr, "%s: %s: unexpected end of file\n",
1022159047fSniklas 	       whoami, filename);
1032159047fSniklas       done (1);
1042159047fSniklas     }
1052159047fSniklas 
1062159047fSniklas   n_lowpc = (bfd_vma) get_vma (core_bfd, (bfd_byte *) hdr.low_pc);
1072159047fSniklas   n_highpc = (bfd_vma) get_vma (core_bfd, (bfd_byte *) hdr.high_pc);
1082159047fSniklas   ncnt = bfd_get_32 (core_bfd, (bfd_byte *) hdr.hist_size);
1092159047fSniklas   profrate = bfd_get_32 (core_bfd, (bfd_byte *) hdr.prof_rate);
1102159047fSniklas   strncpy (hist_dimension, hdr.dimen, sizeof (hdr.dimen));
1112159047fSniklas   hist_dimension[sizeof (hdr.dimen)] = '\0';
1122159047fSniklas   hist_dimension_abbrev = hdr.dimen_abbrev;
1132159047fSniklas 
1142159047fSniklas   if (!s_highpc)
1152159047fSniklas     {
1162159047fSniklas 
1172159047fSniklas       /* this is the first histogram record: */
1182159047fSniklas 
1192159047fSniklas       s_lowpc = n_lowpc;
1202159047fSniklas       s_highpc = n_highpc;
1212159047fSniklas       lowpc = (bfd_vma) n_lowpc / sizeof (UNIT);
1222159047fSniklas       highpc = (bfd_vma) n_highpc / sizeof (UNIT);
1232159047fSniklas       hist_num_bins = ncnt;
1242159047fSniklas       hz = profrate;
1252159047fSniklas     }
1262159047fSniklas 
1272159047fSniklas   DBG (SAMPLEDEBUG,
1282159047fSniklas        printf ("[hist_read_rec] n_lowpc 0x%lx n_highpc 0x%lx ncnt %d\n",
1292159047fSniklas 	       n_lowpc, n_highpc, ncnt);
1302159047fSniklas        printf ("[hist_read_rec] s_lowpc 0x%lx s_highpc 0x%lx nsamples %d\n",
1312159047fSniklas 	       s_lowpc, s_highpc, hist_num_bins);
1322159047fSniklas        printf ("[hist_read_rec]   lowpc 0x%lx   highpc 0x%lx\n",
1332159047fSniklas 	       lowpc, highpc));
1342159047fSniklas 
1352159047fSniklas   if (n_lowpc != s_lowpc || n_highpc != s_highpc
1362159047fSniklas       || ncnt != hist_num_bins || hz != profrate)
1372159047fSniklas     {
1382159047fSniklas       fprintf (stderr, "%s: `%s' is incompatible with first gmon file\n",
1392159047fSniklas 	       whoami, filename);
1402159047fSniklas       done (1);
1412159047fSniklas     }
1422159047fSniklas 
1432159047fSniklas   if (!hist_sample)
1442159047fSniklas     {
1452159047fSniklas       hist_sample = (int *) xmalloc (hist_num_bins * sizeof (hist_sample[0]));
1462159047fSniklas       memset (hist_sample, 0, hist_num_bins * sizeof (hist_sample[0]));
1472159047fSniklas     }
1482159047fSniklas 
1492159047fSniklas   for (i = 0; i < hist_num_bins; ++i)
1502159047fSniklas     {
1512159047fSniklas       if (fread (&count[0], sizeof (count), 1, ifp) != 1)
1522159047fSniklas 	{
1532159047fSniklas 	  fprintf (stderr,
1542159047fSniklas 		   "%s: %s: unexpected EOF after reading %d of %d samples\n",
1552159047fSniklas 		   whoami, filename, i, hist_num_bins);
1562159047fSniklas 	  done (1);
1572159047fSniklas 	}
1582159047fSniklas       hist_sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) & count[0]);
1592159047fSniklas     }
1602159047fSniklas }
1612159047fSniklas 
1622159047fSniklas 
1632159047fSniklas /*
1642159047fSniklas  * Write execution histogram to file OFP.  FILENAME is the name
1652159047fSniklas  * of OFP and is provided for formatting error-messages only.
1662159047fSniklas  */
1672159047fSniklas void
1682159047fSniklas DEFUN (hist_write_hist, (ofp, filename), FILE * ofp AND const char *filename)
1692159047fSniklas {
1702159047fSniklas   struct gmon_hist_hdr hdr;
1712159047fSniklas   unsigned char tag;
1722159047fSniklas   UNIT count;
1732159047fSniklas   int i;
1742159047fSniklas 
1752159047fSniklas   /* write header: */
1762159047fSniklas 
1772159047fSniklas   tag = GMON_TAG_TIME_HIST;
1782159047fSniklas   put_vma (core_bfd, s_lowpc, (bfd_byte *) hdr.low_pc);
1792159047fSniklas   put_vma (core_bfd, s_highpc, (bfd_byte *) hdr.high_pc);
1802159047fSniklas   bfd_put_32 (core_bfd, hist_num_bins, (bfd_byte *) hdr.hist_size);
1812159047fSniklas   bfd_put_32 (core_bfd, hz, (bfd_byte *) hdr.prof_rate);
1822159047fSniklas   strncpy (hdr.dimen, hist_dimension, sizeof (hdr.dimen));
1832159047fSniklas   hdr.dimen_abbrev = hist_dimension_abbrev;
1842159047fSniklas 
1852159047fSniklas   if (fwrite (&tag, sizeof (tag), 1, ofp) != 1
1862159047fSniklas       || fwrite (&hdr, sizeof (hdr), 1, ofp) != 1)
1872159047fSniklas     {
1882159047fSniklas       perror (filename);
1892159047fSniklas       done (1);
1902159047fSniklas     }
1912159047fSniklas 
1922159047fSniklas   for (i = 0; i < hist_num_bins; ++i)
1932159047fSniklas     {
1942159047fSniklas       bfd_put_16 (core_bfd, hist_sample[i], (bfd_byte *) & count[0]);
1952159047fSniklas       if (fwrite (&count[0], sizeof (count), 1, ofp) != 1)
1962159047fSniklas 	{
1972159047fSniklas 	  perror (filename);
1982159047fSniklas 	  done (1);
1992159047fSniklas 	}
2002159047fSniklas     }
2012159047fSniklas }
2022159047fSniklas 
2032159047fSniklas 
2042159047fSniklas /*
2052159047fSniklas  * Calculate scaled entry point addresses (to save time in
2062159047fSniklas  * hist_assign_samples), and, on architectures that have procedure
2072159047fSniklas  * entry masks at the start of a function, possibly push the scaled
2082159047fSniklas  * entry points over the procedure entry mask, if it turns out that
2092159047fSniklas  * the entry point is in one bin and the code for a routine is in the
2102159047fSniklas  * next bin.
2112159047fSniklas  */
2122159047fSniklas static void
213*191aa565Sniklas scale_and_align_entries ()
2142159047fSniklas {
2152159047fSniklas   Sym *sym;
2162159047fSniklas   bfd_vma bin_of_entry;
2172159047fSniklas   bfd_vma bin_of_code;
2182159047fSniklas 
2192159047fSniklas   for (sym = symtab.base; sym < symtab.limit; sym++)
2202159047fSniklas     {
2212159047fSniklas       sym->hist.scaled_addr = sym->addr / sizeof (UNIT);
2222159047fSniklas       bin_of_entry = (sym->hist.scaled_addr - lowpc) / hist_scale;
2232159047fSniklas       bin_of_code = (sym->hist.scaled_addr + UNITS_TO_CODE - lowpc) / hist_scale;
2242159047fSniklas       if (bin_of_entry < bin_of_code)
2252159047fSniklas 	{
2262159047fSniklas 	  DBG (SAMPLEDEBUG,
2272159047fSniklas 	       printf ("[scale_and_align_entries] pushing 0x%lx to 0x%lx\n",
228*191aa565Sniklas 		       sym->hist.scaled_addr,
229*191aa565Sniklas 		       sym->hist.scaled_addr + UNITS_TO_CODE));
230*191aa565Sniklas 	  sym->hist.scaled_addr += UNITS_TO_CODE;
2312159047fSniklas 	}
2322159047fSniklas     }
2332159047fSniklas }
2342159047fSniklas 
2352159047fSniklas 
2362159047fSniklas /*
2372159047fSniklas  * Assign samples to the symbol to which they belong.
2382159047fSniklas  *
2392159047fSniklas  * Histogram bin I covers some address range [BIN_LOWPC,BIN_HIGH_PC)
2402159047fSniklas  * which may overlap one more symbol address ranges.  If a symbol
2412159047fSniklas  * overlaps with the bin's address range by O percent, then O percent
2422159047fSniklas  * of the bin's count is credited to that symbol.
2432159047fSniklas  *
2442159047fSniklas  * There are three cases as to where BIN_LOW_PC and BIN_HIGH_PC can be
2452159047fSniklas  * with respect to the symbol's address range [SYM_LOW_PC,
2462159047fSniklas  * SYM_HIGH_PC) as shown in the following diagram.  OVERLAP computes
2472159047fSniklas  * the distance (in UNITs) between the arrows, the fraction of the
2482159047fSniklas  * sample that is to be credited to the symbol which starts at
2492159047fSniklas  * SYM_LOW_PC.
2502159047fSniklas  *
2512159047fSniklas  *        sym_low_pc                                      sym_high_pc
2522159047fSniklas  *             |                                               |
2532159047fSniklas  *             v                                               v
2542159047fSniklas  *
2552159047fSniklas  *             +-----------------------------------------------+
2562159047fSniklas  *             |                                               |
2572159047fSniklas  *        |  ->|    |<-         ->|         |<-         ->|    |<-  |
2582159047fSniklas  *        |         |             |         |             |         |
2592159047fSniklas  *        +---------+             +---------+             +---------+
2602159047fSniklas  *
2612159047fSniklas  *        ^         ^             ^         ^             ^         ^
2622159047fSniklas  *        |         |             |         |             |         |
2632159047fSniklas  *   bin_low_pc bin_high_pc  bin_low_pc bin_high_pc  bin_low_pc bin_high_pc
2642159047fSniklas  *
2652159047fSniklas  * For the VAX we assert that samples will never fall in the first two
2662159047fSniklas  * bytes of any routine, since that is the entry mask, thus we call
2672159047fSniklas  * scale_and_align_entries() to adjust the entry points if the entry
2682159047fSniklas  * mask falls in one bin but the code for the routine doesn't start
2692159047fSniklas  * until the next bin.  In conjunction with the alignment of routine
2702159047fSniklas  * addresses, this should allow us to have only one sample for every
2712159047fSniklas  * four bytes of text space and never have any overlap (the two end
2722159047fSniklas  * cases, above).
2732159047fSniklas  */
2742159047fSniklas void
2752159047fSniklas DEFUN_VOID (hist_assign_samples)
2762159047fSniklas {
2772159047fSniklas   bfd_vma bin_low_pc, bin_high_pc;
2782159047fSniklas   bfd_vma sym_low_pc, sym_high_pc;
2792159047fSniklas   bfd_vma overlap, addr;
2802159047fSniklas   int bin_count, i, j;
2812159047fSniklas   double time, credit;
2822159047fSniklas 
2832159047fSniklas   /* read samples and assign to symbols: */
2842159047fSniklas   hist_scale = highpc - lowpc;
2852159047fSniklas   hist_scale /= hist_num_bins;
2862159047fSniklas   scale_and_align_entries ();
2872159047fSniklas 
2882159047fSniklas   /* iterate over all sample bins: */
2892159047fSniklas 
2902159047fSniklas   for (i = 0, j = 1; i < hist_num_bins; ++i)
2912159047fSniklas     {
2922159047fSniklas       bin_count = hist_sample[i];
2932159047fSniklas       if (!bin_count)
2942159047fSniklas 	{
2952159047fSniklas 	  continue;
2962159047fSniklas 	}
2972159047fSniklas       bin_low_pc = lowpc + (bfd_vma) (hist_scale * i);
2982159047fSniklas       bin_high_pc = lowpc + (bfd_vma) (hist_scale * (i + 1));
2992159047fSniklas       time = bin_count;
3002159047fSniklas       DBG (SAMPLEDEBUG,
3012159047fSniklas 	   printf (
3022159047fSniklas       "[assign_samples] bin_low_pc=0x%lx, bin_high_pc=0x%lx, bin_count=%d\n",
3032159047fSniklas 		    sizeof (UNIT) * bin_low_pc, sizeof (UNIT) * bin_high_pc,
3042159047fSniklas 		    bin_count));
3052159047fSniklas       total_time += time;
3062159047fSniklas 
3072159047fSniklas       /* credit all symbols that are covered by bin I: */
3082159047fSniklas 
3092159047fSniklas       for (j = j - 1; j < symtab.len; ++j)
3102159047fSniklas 	{
3112159047fSniklas 	  sym_low_pc = symtab.base[j].hist.scaled_addr;
3122159047fSniklas 	  sym_high_pc = symtab.base[j + 1].hist.scaled_addr;
3132159047fSniklas 	  /*
3142159047fSniklas 	   * If high end of bin is below entry address, go for next
3152159047fSniklas 	   * bin:
3162159047fSniklas 	   */
3172159047fSniklas 	  if (bin_high_pc < sym_low_pc)
3182159047fSniklas 	    {
3192159047fSniklas 	      break;
3202159047fSniklas 	    }
3212159047fSniklas 	  /*
3222159047fSniklas 	   * If low end of bin is above high end of symbol, go for
3232159047fSniklas 	   * next symbol.
3242159047fSniklas 	   */
3252159047fSniklas 	  if (bin_low_pc >= sym_high_pc)
3262159047fSniklas 	    {
3272159047fSniklas 	      continue;
3282159047fSniklas 	    }
3292159047fSniklas 	  overlap =
3302159047fSniklas 	    MIN (bin_high_pc, sym_high_pc) - MAX (bin_low_pc, sym_low_pc);
3312159047fSniklas 	  if (overlap > 0)
3322159047fSniklas 	    {
3332159047fSniklas 	      DBG (SAMPLEDEBUG,
3342159047fSniklas 		   printf (
3352159047fSniklas 			    "[assign_samples] [0x%lx,0x%lx) %s gets %f ticks %ld overlap\n",
3362159047fSniklas 			    symtab.base[j].addr, sizeof (UNIT) * sym_high_pc,
3372159047fSniklas 			    symtab.base[j].name, overlap * time / hist_scale,
3382159047fSniklas 			    overlap));
3392159047fSniklas 	      addr = symtab.base[j].addr;
3402159047fSniklas 	      credit = overlap * time / hist_scale;
3412159047fSniklas 	      /*
3422159047fSniklas 	       * Credit symbol if it appears in INCL_FLAT or that
3432159047fSniklas 	       * table is empty and it does not appear it in
3442159047fSniklas 	       * EXCL_FLAT.
3452159047fSniklas 	       */
3462159047fSniklas 	      if (sym_lookup (&syms[INCL_FLAT], addr)
3472159047fSniklas 		  || (syms[INCL_FLAT].len == 0
3482159047fSniklas 		      && !sym_lookup (&syms[EXCL_FLAT], addr)))
3492159047fSniklas 		{
3502159047fSniklas 		  symtab.base[j].hist.time += credit;
3512159047fSniklas 		}
3522159047fSniklas 	      else
3532159047fSniklas 		{
3542159047fSniklas 		  total_time -= credit;
3552159047fSniklas 		}
3562159047fSniklas 	    }
3572159047fSniklas 	}
3582159047fSniklas     }
3592159047fSniklas   DBG (SAMPLEDEBUG, printf ("[assign_samples] total_time %f\n",
3602159047fSniklas 			    total_time));
3612159047fSniklas }
3622159047fSniklas 
3632159047fSniklas 
3642159047fSniklas /*
3652159047fSniklas  * Print header for flag histogram profile:
3662159047fSniklas  */
3672159047fSniklas static void
3682159047fSniklas DEFUN (print_header, (prefix), const char prefix)
3692159047fSniklas {
3702159047fSniklas   char unit[64];
3712159047fSniklas 
3722159047fSniklas   sprintf (unit, "%c%c/call", prefix, hist_dimension_abbrev);
3732159047fSniklas 
3742159047fSniklas   if (bsd_style_output)
3752159047fSniklas     {
3762159047fSniklas       printf ("\ngranularity: each sample hit covers %ld byte(s)",
3772159047fSniklas 	      (long) hist_scale * sizeof (UNIT));
3782159047fSniklas       if (total_time > 0.0)
3792159047fSniklas 	{
3802159047fSniklas 	  printf (" for %.2f%% of %.2f %s\n\n",
3812159047fSniklas 		  100.0 / total_time, total_time / hz, hist_dimension);
3822159047fSniklas 	}
3832159047fSniklas     }
3842159047fSniklas   else
3852159047fSniklas     {
3862159047fSniklas       printf ("\nEach sample counts as %g %s.\n", 1.0 / hz, hist_dimension);
3872159047fSniklas     }
3882159047fSniklas 
3892159047fSniklas   if (total_time <= 0.0)
3902159047fSniklas     {
3912159047fSniklas       printf (" no time accumulated\n\n");
3922159047fSniklas       /* this doesn't hurt since all the numerators will be zero: */
3932159047fSniklas       total_time = 1.0;
3942159047fSniklas     }
3952159047fSniklas 
3962159047fSniklas   printf ("%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s  %-8.8s\n",
3972159047fSniklas 	  "%  ", "cumulative", "self  ", "", "self  ", "total ", "");
3982159047fSniklas   printf ("%5.5s %9.9s  %8.8s %8.8s %8.8s %8.8s  %-8.8s\n",
3992159047fSniklas 	  "time", hist_dimension, hist_dimension, "calls", unit, unit,
4002159047fSniklas 	  "name");
4012159047fSniklas }
4022159047fSniklas 
4032159047fSniklas 
4042159047fSniklas static void
4052159047fSniklas DEFUN (print_line, (sym, scale), Sym * sym AND double scale)
4062159047fSniklas {
4072159047fSniklas   if (ignore_zeros && sym->ncalls == 0 && sym->hist.time == 0)
4082159047fSniklas     {
4092159047fSniklas       return;
4102159047fSniklas     }
4112159047fSniklas 
4122159047fSniklas   accum_time += sym->hist.time;
4132159047fSniklas   if (bsd_style_output)
4142159047fSniklas     {
4152159047fSniklas       printf ("%5.1f %10.2f %8.2f",
4162159047fSniklas 	      total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
4172159047fSniklas 	      accum_time / hz, sym->hist.time / hz);
4182159047fSniklas     }
4192159047fSniklas   else
4202159047fSniklas     {
4212159047fSniklas       printf ("%6.2f %9.2f %8.2f",
4222159047fSniklas 	      total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
4232159047fSniklas 	      accum_time / hz, sym->hist.time / hz);
4242159047fSniklas     }
4252159047fSniklas   if (sym->ncalls)
4262159047fSniklas     {
4272159047fSniklas       printf (" %8d %8.2f %8.2f  ",
4282159047fSniklas 	      sym->ncalls, scale * sym->hist.time / hz / sym->ncalls,
4292159047fSniklas 	  scale * (sym->hist.time + sym->cg.child_time) / hz / sym->ncalls);
4302159047fSniklas     }
4312159047fSniklas   else
4322159047fSniklas     {
4332159047fSniklas       printf (" %8.8s %8.8s %8.8s  ", "", "", "");
4342159047fSniklas     }
4352159047fSniklas   if (bsd_style_output)
4362159047fSniklas     {
4372159047fSniklas       print_name (sym);
4382159047fSniklas     }
4392159047fSniklas   else
4402159047fSniklas     {
4412159047fSniklas       print_name_only (sym);
4422159047fSniklas     }
4432159047fSniklas   printf ("\n");
4442159047fSniklas }
4452159047fSniklas 
4462159047fSniklas 
4472159047fSniklas /*
4482159047fSniklas  * Compare LP and RP.  The primary comparison key is execution time,
4492159047fSniklas  * the secondary is number of invocation, and the tertiary is the
4502159047fSniklas  * lexicographic order of the function names.
4512159047fSniklas  */
4522159047fSniklas static int
4532159047fSniklas DEFUN (cmp_time, (lp, rp), const PTR lp AND const PTR rp)
4542159047fSniklas {
4552159047fSniklas   const Sym *left = *(const Sym **) lp;
4562159047fSniklas   const Sym *right = *(const Sym **) rp;
4572159047fSniklas   double time_diff;
4582159047fSniklas   long call_diff;
4592159047fSniklas 
4602159047fSniklas   time_diff = right->hist.time - left->hist.time;
4612159047fSniklas   if (time_diff > 0.0)
4622159047fSniklas     {
4632159047fSniklas       return 1;
4642159047fSniklas     }
4652159047fSniklas   if (time_diff < 0.0)
4662159047fSniklas     {
4672159047fSniklas       return -1;
4682159047fSniklas     }
4692159047fSniklas 
4702159047fSniklas   call_diff = right->ncalls - left->ncalls;
4712159047fSniklas   if (call_diff > 0)
4722159047fSniklas     {
4732159047fSniklas       return 1;
4742159047fSniklas     }
4752159047fSniklas   if (call_diff < 0)
4762159047fSniklas     {
4772159047fSniklas       return -1;
4782159047fSniklas     }
4792159047fSniklas 
4802159047fSniklas   return strcmp (left->name, right->name);
4812159047fSniklas }
4822159047fSniklas 
4832159047fSniklas 
4842159047fSniklas /*
4852159047fSniklas  * Print the flat histogram profile.
4862159047fSniklas  */
4872159047fSniklas void
4882159047fSniklas DEFUN_VOID (hist_print)
4892159047fSniklas {
4902159047fSniklas   Sym **time_sorted_syms, *top_dog, *sym;
4912159047fSniklas   int index, log_scale;
4922159047fSniklas   double top_time, time;
4932159047fSniklas   bfd_vma addr;
4942159047fSniklas 
4952159047fSniklas   if (first_output)
4962159047fSniklas     {
4972159047fSniklas       first_output = FALSE;
4982159047fSniklas     }
4992159047fSniklas   else
5002159047fSniklas     {
5012159047fSniklas       printf ("\f\n");
5022159047fSniklas     }
5032159047fSniklas 
5042159047fSniklas   accum_time = 0.0;
5052159047fSniklas   if (bsd_style_output)
5062159047fSniklas     {
5072159047fSniklas       if (print_descriptions)
5082159047fSniklas 	{
5092159047fSniklas 	  printf ("\n\n\nflat profile:\n");
5102159047fSniklas 	  flat_blurb (stdout);
5112159047fSniklas 	}
5122159047fSniklas     }
5132159047fSniklas   else
5142159047fSniklas     {
5152159047fSniklas       printf ("Flat profile:\n");
5162159047fSniklas     }
5172159047fSniklas   /*
5182159047fSniklas    * Sort the symbol table by time (call-count and name as secondary
5192159047fSniklas    * and tertiary keys):
5202159047fSniklas    */
5212159047fSniklas   time_sorted_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *));
5222159047fSniklas   for (index = 0; index < symtab.len; ++index)
5232159047fSniklas     {
5242159047fSniklas       time_sorted_syms[index] = &symtab.base[index];
5252159047fSniklas     }
5262159047fSniklas   qsort (time_sorted_syms, symtab.len, sizeof (Sym *), cmp_time);
5272159047fSniklas 
5282159047fSniklas   if (bsd_style_output)
5292159047fSniklas     {
5302159047fSniklas       log_scale = 5;		/* milli-seconds is BSD-default */
5312159047fSniklas     }
5322159047fSniklas   else
5332159047fSniklas     {
5342159047fSniklas       /*
5352159047fSniklas        * Search for symbol with highest per-call execution time and
5362159047fSniklas        * scale accordingly:
5372159047fSniklas        */
5382159047fSniklas       log_scale = 0;
5392159047fSniklas       top_dog = 0;
5402159047fSniklas       top_time = 0.0;
5412159047fSniklas       for (index = 0; index < symtab.len; ++index)
5422159047fSniklas 	{
5432159047fSniklas 	  sym = time_sorted_syms[index];
5442159047fSniklas 	  if (sym->ncalls)
5452159047fSniklas 	    {
5462159047fSniklas 	      time = (sym->hist.time + sym->cg.child_time) / sym->ncalls;
5472159047fSniklas 	      if (time > top_time)
5482159047fSniklas 		{
5492159047fSniklas 		  top_dog = sym;
5502159047fSniklas 		  top_time = time;
5512159047fSniklas 		}
5522159047fSniklas 	    }
5532159047fSniklas 	}
5542159047fSniklas       if (top_dog && top_dog->ncalls && top_time > 0.0)
5552159047fSniklas 	{
5562159047fSniklas 	  top_time /= hz;
5572159047fSniklas 	  while (SItab[log_scale].scale * top_time < 1000.0
5582159047fSniklas 		 && log_scale < sizeof (SItab) / sizeof (SItab[0]) - 1)
5592159047fSniklas 	    {
5602159047fSniklas 	      ++log_scale;
5612159047fSniklas 	    }
5622159047fSniklas 	}
5632159047fSniklas     }
5642159047fSniklas 
5652159047fSniklas   /*
5662159047fSniklas    * For now, the dimension is always seconds.  In the future, we
5672159047fSniklas    * may also want to support other (pseudo-)dimensions (such as
5682159047fSniklas    * I-cache misses etc.).
5692159047fSniklas    */
5702159047fSniklas   print_header (SItab[log_scale].prefix);
5712159047fSniklas   for (index = 0; index < symtab.len; ++index)
5722159047fSniklas     {
5732159047fSniklas       addr = time_sorted_syms[index]->addr;
5742159047fSniklas       /*
5752159047fSniklas        * Print symbol if its in INCL_FLAT table or that table
5762159047fSniklas        * is empty and the symbol is not in EXCL_FLAT.
5772159047fSniklas        */
5782159047fSniklas       if (sym_lookup (&syms[INCL_FLAT], addr)
5792159047fSniklas 	  || (syms[INCL_FLAT].len == 0
5802159047fSniklas 	      && !sym_lookup (&syms[EXCL_FLAT], addr)))
5812159047fSniklas 	{
5822159047fSniklas 	  print_line (time_sorted_syms[index], SItab[log_scale].scale);
5832159047fSniklas 	}
5842159047fSniklas     }
5852159047fSniklas   free (time_sorted_syms);
5862159047fSniklas 
5872159047fSniklas   if (print_descriptions && !bsd_style_output)
5882159047fSniklas     {
5892159047fSniklas       flat_blurb (stdout);
5902159047fSniklas     }
5912159047fSniklas }
592