* elf64-ppc.c (dec_dynrel_count): Don't error when elf_gc_sweep_symbol
[external/binutils.git] / gprof / hist.c
index f63e37d..91b0000 100644 (file)
@@ -1,12 +1,13 @@
 /* hist.c  -  Histogram related operations.
 
-   Copyright 2000 Free Software Foundation, Inc.
+   Copyright 1999, 2000, 2001, 2002, 2004, 2005, 2007, 2009
+   Free Software Foundation, Inc.
 
    This file is part of GNU Binutils.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
 \f
-#include <stdio.h>
-#include "libiberty.h"
 #include "gprof.h"
+#include "libiberty.h"
+#include "search_list.h"
+#include "source.h"
+#include "symtab.h"
 #include "corefile.h"
 #include "gmon_io.h"
 #include "gmon_out.h"
 #include "hist.h"
-#include "symtab.h"
 #include "sym_ids.h"
 #include "utils.h"
+#include "math.h"
+#include "stdio.h"
+#include "stdlib.h"
 
 #define UNITS_TO_CODE (offset_to_code / sizeof(UNIT))
 
-static void scale_and_align_entries PARAMS ((void));
+static void scale_and_align_entries (void);
+static void print_header (int);
+static void print_line (Sym *, double);
+static int cmp_time (const PTR, const PTR);
 
 /* Declarations of automatically generated functions to output blurbs.  */
-extern void flat_blurb PARAMS ((FILE * fp));
+extern void flat_blurb (FILE * fp);
+
+static histogram *find_histogram (bfd_vma lowpc, bfd_vma highpc);
+static histogram *find_histogram_for_pc (bfd_vma pc);
 
-bfd_vma s_lowpc;               /* Lowest address in .text.  */
-bfd_vma s_highpc = 0;          /* Highest address in .text.  */
-bfd_vma lowpc, highpc;         /* Same, but expressed in UNITs.  */
-int hist_num_bins = 0;         /* Number of histogram samples.  */
-int *hist_sample = 0;          /* Histogram samples (shorts in the file!).  */
+histogram * histograms;
+unsigned num_histograms;
 double hist_scale;
-char hist_dimension[sizeof (((struct gmon_hist_hdr *) 0)->dimen) + 1] =
-  "seconds";
-char hist_dimension_abbrev = 's';
+static char hist_dimension[16] = "seconds";
+static char hist_dimension_abbrev = 's';
 
-static double accum_time;      /* Accumulated time so far for print_line().  */
+static double accum_time;      /* Accumulated time so far for print_line(). */
 static double total_time;      /* Total time for all routines.  */
 
 /* Table of SI prefixes for powers of 10 (used to automatically
@@ -59,168 +66,222 @@ const struct
   }
 SItab[] =
 {
-  {
-    'T', 1e-12
-  }
-  ,                            /* tera */
-  {
-    'G', 1e-09
-  }
-  ,                            /* giga */
-  {
-    'M', 1e-06
-  }
-  ,                            /* mega */
-  {
-    'K', 1e-03
-  }
-  ,                            /* kilo */
-  {
-    ' ', 1e-00
-  }
-  ,
-  {
-    'm', 1e+03
-  }
-  ,                            /* milli */
-  {
-    'u', 1e+06
-  }
-  ,                            /* micro */
-  {
-    'n', 1e+09
-  }
-  ,                            /* nano */
-  {
-    'p', 1e+12
-  }
-  ,                            /* pico */
-  {
-    'f', 1e+15
-  }
-  ,                            /* femto */
-  {
-    'a', 1e+18
-  }
-  ,                            /* ato */
+  { 'T', 1e-12 },                              /* tera */
+  { 'G', 1e-09 },                              /* giga */
+  { 'M', 1e-06 },                              /* mega */
+  { 'K', 1e-03 },                              /* kilo */
+  { ' ', 1e-00 },
+  { 'm', 1e+03 },                              /* milli */
+  { 'u', 1e+06 },                              /* micro */
+  { 'n', 1e+09 },                              /* nano */
+  { 'p', 1e+12 },                              /* pico */
+  { 'f', 1e+15 },                              /* femto */
+  { 'a', 1e+18 }                               /* ato */
 };
 
+/* Reads just the header part of histogram record into
+   *RECORD from IFP.  FILENAME is the name of IFP and
+   is provided for formatting error messages only.  
 
-/* Read the histogram from file IFP.  FILENAME is the name of IFP and
-   is provided for formatting error messages only.  */
-
-void
-DEFUN (hist_read_rec, (ifp, filename), FILE * ifp AND const char *filename)
+   If FIRST is non-zero, sets global variables HZ, HIST_DIMENSION,
+   HIST_DIMENSION_ABBREV, HIST_SCALE.  If FIRST is zero, checks
+   that the new histogram is compatible with already-set values
+   of those variables and emits an error if that's not so.  */
+static void
+read_histogram_header (histogram *record, 
+                      FILE *ifp, const char *filename,
+                      int first)
 {
-  struct gmon_hist_hdr hdr;
-  bfd_vma n_lowpc, n_highpc;
-  int i, ncnt, profrate;
-  UNIT count;
-
-  if (fread (&hdr, sizeof (hdr), 1, ifp) != 1)
+  unsigned int profrate;
+  char n_hist_dimension[15];
+  char n_hist_dimension_abbrev;
+  double n_hist_scale;
+
+  if (gmon_io_read_vma (ifp, &record->lowpc)
+      || gmon_io_read_vma (ifp, &record->highpc)
+      || gmon_io_read_32 (ifp, &record->num_bins)
+      || gmon_io_read_32 (ifp, &profrate)
+      || gmon_io_read (ifp, n_hist_dimension, 15)
+      || gmon_io_read (ifp, &n_hist_dimension_abbrev, 1))
     {
       fprintf (stderr, _("%s: %s: unexpected end of file\n"),
               whoami, filename);
+
       done (1);
     }
 
-  n_lowpc = (bfd_vma) get_vma (core_bfd, (bfd_byte *) hdr.low_pc);
-  n_highpc = (bfd_vma) get_vma (core_bfd, (bfd_byte *) hdr.high_pc);
-  ncnt = bfd_get_32 (core_bfd, (bfd_byte *) hdr.hist_size);
-  profrate = bfd_get_32 (core_bfd, (bfd_byte *) hdr.prof_rate);
-  strncpy (hist_dimension, hdr.dimen, sizeof (hdr.dimen));
-  hist_dimension[sizeof (hdr.dimen)] = '\0';
-  hist_dimension_abbrev = hdr.dimen_abbrev;
+  n_hist_scale = (double)((record->highpc - record->lowpc) / sizeof (UNIT)) 
+    / record->num_bins;
 
-  if (!s_highpc)
+  if (first)
     {
-      /* This is the first histogram record.  */
-      s_lowpc = n_lowpc;
-      s_highpc = n_highpc;
-      lowpc = (bfd_vma) n_lowpc / sizeof (UNIT);
-      highpc = (bfd_vma) n_highpc / sizeof (UNIT);
-      hist_num_bins = ncnt;
+      /* We don't try to veryfy profrate is the same for all histogram
+        records.  If we have two histogram records for the same
+        address range and profiling samples is done as often
+        as possible as opposed on timer, then the actual profrate will
+        be slightly different.  Most of the time the difference does not
+        matter and insisting that profiling rate is exactly the same
+        will only create inconvenient.  */
       hz = profrate;
+      memcpy (hist_dimension, n_hist_dimension, 15);
+      hist_dimension_abbrev = n_hist_dimension_abbrev;
+      hist_scale = n_hist_scale;      
     }
-
-  DBG (SAMPLEDEBUG,
-       printf ("[hist_read_rec] n_lowpc 0x%lx n_highpc 0x%lx ncnt %d\n",
-              (unsigned long) n_lowpc, (unsigned long) n_highpc, ncnt);
-       printf ("[hist_read_rec] s_lowpc 0x%lx s_highpc 0x%lx nsamples %d\n",
-              (unsigned long) s_lowpc, (unsigned long) s_highpc,
-              hist_num_bins);
-       printf ("[hist_read_rec]   lowpc 0x%lx   highpc 0x%lx\n",
-              (unsigned long) lowpc, (unsigned long) highpc));
-
-  if (n_lowpc != s_lowpc || n_highpc != s_highpc
-      || ncnt != hist_num_bins || hz != profrate)
+  else
     {
-      fprintf (stderr, _("%s: `%s' is incompatible with first gmon file\n"),
-              whoami, filename);
-      done (1);
+      if (strncmp (n_hist_dimension, hist_dimension, 15) != 0)
+       {
+         fprintf (stderr, 
+                  _("%s: dimension unit changed between histogram records\n"
+                    "%s: from '%s'\n"
+                    "%s: to '%s'\n"),
+                  whoami, whoami, hist_dimension, whoami, n_hist_dimension);
+         done (1);
+       }
+
+      if (n_hist_dimension_abbrev != hist_dimension_abbrev)
+       {
+         fprintf (stderr, 
+                  _("%s: dimension abbreviation changed between histogram records\n"
+                    "%s: from '%c'\n"
+                    "%s: to '%c'\n"),
+                  whoami, whoami, hist_dimension_abbrev, whoami, n_hist_dimension_abbrev);
+         done (1);       
+       }
+
+      /* The only reason we require the same scale for histograms is that
+        there's code (notably printing code), that prints units,
+        and it would be very confusing to have one unit mean different
+        things for different functions.  */
+      if (fabs (hist_scale - n_hist_scale) > 0.000001)
+       {
+         fprintf (stderr, 
+                  _("%s: different scales in histogram records"),
+                  whoami);
+         done (1);      
+       }
     }
+}
+
+/* Read the histogram from file IFP.  FILENAME is the name of IFP and
+   is provided for formatting error messages only.  */
+
+void
+hist_read_rec (FILE * ifp, const char *filename)
+{
+  bfd_vma lowpc, highpc;
+  histogram n_record;
+  histogram *record, *existing_record;
+  unsigned i;
+
+  /* 1. Read the header and see if there's existing record for the
+     same address range and that there are no overlapping records.  */
+  read_histogram_header (&n_record, ifp, filename, num_histograms == 0);
 
-  if (!hist_sample)
+  existing_record = find_histogram (n_record.lowpc, n_record.highpc);
+  if (existing_record)
     {
-      hist_sample = (int *) xmalloc (hist_num_bins * sizeof (hist_sample[0]));
-      memset (hist_sample, 0, hist_num_bins * sizeof (hist_sample[0]));
+      record = existing_record;
     }
+  else
+    {
+      /* If this record overlaps, but does not completely match an existing
+        record, it's an error.  */
+      lowpc = n_record.lowpc;
+      highpc = n_record.highpc;
+      hist_clip_symbol_address (&lowpc, &highpc);
+      if (lowpc != highpc)
+       {
+         fprintf (stderr, 
+                  _("%s: overlapping histogram records\n"),
+                  whoami);
+         done (1);      
+       }
+
+      /* This is new record.  Add it to global array and allocate space for
+        the samples.  */
+      histograms = (struct histogram *)
+          xrealloc (histograms, sizeof (histogram) * (num_histograms + 1));
+      memcpy (histograms + num_histograms,
+             &n_record, sizeof (histogram));
+      record = &histograms[num_histograms];      
+      ++num_histograms;
+
+      record->sample = (int *) xmalloc (record->num_bins 
+                                       * sizeof (record->sample[0]));
+      memset (record->sample, 0, record->num_bins * sizeof (record->sample[0]));
+    }
+
+  /* 2. We have either a new record (with zeroed histogram data), or an existing
+     record with some data in the histogram already.  Read new data into the
+     record, adding hit counts.  */
 
-  for (i = 0; i < hist_num_bins; ++i)
+  DBG (SAMPLEDEBUG,
+       printf ("[hist_read_rec] n_lowpc 0x%lx n_highpc 0x%lx ncnt %u\n",
+              (unsigned long) record->lowpc, (unsigned long) record->highpc, 
+               record->num_bins));
+           
+  for (i = 0; i < record->num_bins; ++i)
     {
+      UNIT count;
       if (fread (&count[0], sizeof (count), 1, ifp) != 1)
        {
          fprintf (stderr,
-                  _("%s: %s: unexpected EOF after reading %d of %d samples\n"),
-                  whoami, filename, i, hist_num_bins);
+                 _("%s: %s: unexpected EOF after reading %u of %u samples\n"),
+                  whoami, filename, i, record->num_bins);
          done (1);
        }
-      hist_sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) & count[0]);
+      record->sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) & count[0]);
+      DBG (SAMPLEDEBUG,
+          printf ("[hist_read_rec] 0x%lx: %u\n",
+                  (unsigned long) (record->lowpc 
+                                    + i * (record->highpc - record->lowpc) 
+                                    / record->num_bins),
+                  record->sample[i]));
     }
 }
 
 
-/* Write execution histogram to file OFP.  FILENAME is the name
+/* Write all execution histograms file OFP.  FILENAME is the name
    of OFP and is provided for formatting error-messages only.  */
 
 void
-DEFUN (hist_write_hist, (ofp, filename), FILE * ofp AND const char *filename)
+hist_write_hist (FILE * ofp, const char *filename)
 {
-  struct gmon_hist_hdr hdr;
-  unsigned char tag;
   UNIT count;
-  int i;
-
-  /* Write header.  */
+  unsigned int i, r;
 
-  tag = GMON_TAG_TIME_HIST;
-  put_vma (core_bfd, s_lowpc, (bfd_byte *) hdr.low_pc);
-  put_vma (core_bfd, s_highpc, (bfd_byte *) hdr.high_pc);
-  bfd_put_32 (core_bfd, hist_num_bins, (bfd_byte *) hdr.hist_size);
-  bfd_put_32 (core_bfd, hz, (bfd_byte *) hdr.prof_rate);
-  strncpy (hdr.dimen, hist_dimension, sizeof (hdr.dimen));
-  hdr.dimen_abbrev = hist_dimension_abbrev;
-
-  if (fwrite (&tag, sizeof (tag), 1, ofp) != 1
-      || fwrite (&hdr, sizeof (hdr), 1, ofp) != 1)
+  for (r = 0; r < num_histograms; ++r)
     {
-      perror (filename);
-      done (1);
-    }
+      histogram *record = &histograms[r];
 
-  for (i = 0; i < hist_num_bins; ++i)
-    {
-      bfd_put_16 (core_bfd, hist_sample[i], (bfd_byte *) & count[0]);
+      /* Write header.  */
       
-      if (fwrite (&count[0], sizeof (count), 1, ofp) != 1)
+      if (gmon_io_write_8 (ofp, GMON_TAG_TIME_HIST)
+         || gmon_io_write_vma (ofp, record->lowpc)
+         || gmon_io_write_vma (ofp, record->highpc)
+         || gmon_io_write_32 (ofp, record->num_bins)
+         || gmon_io_write_32 (ofp, hz)
+         || gmon_io_write (ofp, hist_dimension, 15)
+         || gmon_io_write (ofp, &hist_dimension_abbrev, 1))
        {
          perror (filename);
          done (1);
        }
+      
+      for (i = 0; i < record->num_bins; ++i)
+       {
+         bfd_put_16 (core_bfd, (bfd_vma) record->sample[i], (bfd_byte *) &count[0]);
+         
+         if (fwrite (&count[0], sizeof (count), 1, ofp) != 1)
+           {
+             perror (filename);
+             done (1);
+           }
+       }
     }
 }
 
-
 /* Calculate scaled entry point addresses (to save time in
    hist_assign_samples), and, on architectures that have procedure
    entry masks at the start of a function, possibly push the scaled
@@ -237,50 +298,57 @@ scale_and_align_entries ()
 
   for (sym = symtab.base; sym < symtab.limit; sym++)
     {
+      histogram *r = find_histogram_for_pc (sym->addr);
+
       sym->hist.scaled_addr = sym->addr / sizeof (UNIT);
-      bin_of_entry = (sym->hist.scaled_addr - lowpc) / hist_scale;
-      bin_of_code = (sym->hist.scaled_addr + UNITS_TO_CODE - lowpc) / hist_scale;
-      if (bin_of_entry < bin_of_code)
+
+      if (r)
        {
-         DBG (SAMPLEDEBUG,
-              printf ("[scale_and_align_entries] pushing 0x%lx to 0x%lx\n",
-                      (unsigned long) sym->hist.scaled_addr,
-                      (unsigned long) (sym->hist.scaled_addr
-                                       + UNITS_TO_CODE)));
-         sym->hist.scaled_addr += UNITS_TO_CODE;
+         bin_of_entry = (sym->hist.scaled_addr - r->lowpc) / hist_scale;
+         bin_of_code = ((sym->hist.scaled_addr + UNITS_TO_CODE - r->lowpc)
+                    / hist_scale);
+         if (bin_of_entry < bin_of_code)
+           {
+             DBG (SAMPLEDEBUG,
+                  printf ("[scale_and_align_entries] pushing 0x%lx to 0x%lx\n",
+                          (unsigned long) sym->hist.scaled_addr,
+                          (unsigned long) (sym->hist.scaled_addr
+                                           + UNITS_TO_CODE)));
+             sym->hist.scaled_addr += UNITS_TO_CODE;
+           }
        }
     }
 }
 
 
 /* Assign samples to the symbol to which they belong.
-  
+
    Histogram bin I covers some address range [BIN_LOWPC,BIN_HIGH_PC)
    which may overlap one more symbol address ranges.  If a symbol
    overlaps with the bin's address range by O percent, then O percent
    of the bin's count is credited to that symbol.
-  
+
    There are three cases as to where BIN_LOW_PC and BIN_HIGH_PC can be
    with respect to the symbol's address range [SYM_LOW_PC,
    SYM_HIGH_PC) as shown in the following diagram.  OVERLAP computes
    the distance (in UNITs) between the arrows, the fraction of the
    sample that is to be credited to the symbol which starts at
    SYM_LOW_PC.
-  
-          sym_low_pc                                      sym_high_pc
-               |                                               |
-               v                                               v
-  
-               +-----------------------------------------------+
-               |                                               |
-          |  ->|    |<-         ->|         |<-         ->|    |<-  |
-          |         |             |         |             |         |
-          +---------+             +---------+             +---------+
-  
-          ^         ^             ^         ^             ^         ^
-          |         |             |         |             |         |
+
+         sym_low_pc                                      sym_high_pc
+              |                                               |
+              v                                               v
+
+              +-----------------------------------------------+
+              |                                               |
+         |  ->|    |<-         ->|         |<-         ->|    |<-  |
+         |         |             |         |             |         |
+         +---------+             +---------+             +---------+
+
+         ^         ^             ^         ^             ^         ^
+         |         |             |         |             |         |
      bin_low_pc bin_high_pc  bin_low_pc bin_high_pc  bin_low_pc bin_high_pc
-  
+
    For the VAX we assert that samples will never fall in the first two
    bytes of any routine, since that is the entry mask, thus we call
    scale_and_align_entries() to adjust the entry points if the entry
@@ -290,46 +358,46 @@ scale_and_align_entries ()
    four bytes of text space and never have any overlap (the two end
    cases, above).  */
 
-void
-DEFUN_VOID (hist_assign_samples)
+static void
+hist_assign_samples_1 (histogram *r)
 {
   bfd_vma bin_low_pc, bin_high_pc;
   bfd_vma sym_low_pc, sym_high_pc;
   bfd_vma overlap, addr;
-  int bin_count, i;
-  unsigned int j;
-  double time, credit;
+  unsigned int bin_count;
+  unsigned int i, j, k;
+  double count_time, credit;
 
-  /* Read samples and assign to symbols.  */
-  hist_scale = highpc - lowpc;
-  hist_scale /= hist_num_bins;
-  scale_and_align_entries ();
+  bfd_vma lowpc = r->lowpc / sizeof (UNIT);
 
   /* Iterate over all sample bins.  */
-  for (i = 0, j = 1; i < hist_num_bins; ++i)
+  for (i = 0, k = 1; i < r->num_bins; ++i)
     {
-      bin_count = hist_sample[i];
+      bin_count = r->sample[i];
       if (! bin_count)
        continue;
 
       bin_low_pc = lowpc + (bfd_vma) (hist_scale * i);
       bin_high_pc = lowpc + (bfd_vma) (hist_scale * (i + 1));
-      time = bin_count;
-      
+      count_time = bin_count;
+
       DBG (SAMPLEDEBUG,
           printf (
-      "[assign_samples] bin_low_pc=0x%lx, bin_high_pc=0x%lx, bin_count=%d\n",
+      "[assign_samples] bin_low_pc=0x%lx, bin_high_pc=0x%lx, bin_count=%u\n",
                    (unsigned long) (sizeof (UNIT) * bin_low_pc),
                    (unsigned long) (sizeof (UNIT) * bin_high_pc),
                    bin_count));
-      total_time += time;
+      total_time += count_time;
 
-      /* Credit all symbols that are covered by bin I.  */
-      for (j = j - 1; j < symtab.len; ++j)
+      /* Credit all symbols that are covered by bin I.
+
+         PR gprof/13325: Make sure that K does not get decremented
+        and J will never be less than 0.  */
+      for (j = k - 1; j < symtab.len; k = ++j)
        {
          sym_low_pc = symtab.base[j].hist.scaled_addr;
          sym_high_pc = symtab.base[j + 1].hist.scaled_addr;
-         
+
          /* If high end of bin is below entry address,
             go for next bin.  */
          if (bin_high_pc < sym_low_pc)
@@ -346,18 +414,18 @@ DEFUN_VOID (hist_assign_samples)
            {
              DBG (SAMPLEDEBUG,
                   printf (
-                           "[assign_samples] [0x%lx,0x%lx) %s gets %f ticks %ld overlap\n",
-                           (unsigned long) symtab.base[j].addr,
-                           (unsigned long) (sizeof (UNIT) * sym_high_pc),
-                           symtab.base[j].name, overlap * time / hist_scale,
-                           (long) overlap));
-             
+              "[assign_samples] [0x%lx,0x%lx) %s gets %f ticks %ld overlap\n",
+                          (unsigned long) symtab.base[j].addr,
+                          (unsigned long) (sizeof (UNIT) * sym_high_pc),
+                          symtab.base[j].name, overlap * count_time / hist_scale,
+                          (long) overlap));
+
              addr = symtab.base[j].addr;
-             credit = overlap * time / hist_scale;
-             
+             credit = overlap * count_time / hist_scale;
+
              /* Credit symbol if it appears in INCL_FLAT or that
-                table is empty and it does not appear it in
-                EXCL_FLAT.  */
+                table is empty and it does not appear it in
+                EXCL_FLAT.  */
              if (sym_lookup (&syms[INCL_FLAT], addr)
                  || (syms[INCL_FLAT].len == 0
                      && !sym_lookup (&syms[EXCL_FLAT], addr)))
@@ -371,16 +439,28 @@ DEFUN_VOID (hist_assign_samples)
            }
        }
     }
-  
+
   DBG (SAMPLEDEBUG, printf ("[assign_samples] total_time %f\n",
                            total_time));
 }
 
+/* Calls 'hist_assign_sampes_1' for all histogram records read so far. */
+void
+hist_assign_samples ()
+{
+  unsigned i;
+
+  scale_and_align_entries ();
+
+  for (i = 0; i < num_histograms; ++i)
+    hist_assign_samples_1 (&histograms[i]);
+  
+}
 
 /* Print header for flag histogram profile.  */
 
 static void
-DEFUN (print_header, (prefix), const char prefix)
+print_header (int prefix)
 {
   char unit[64];
 
@@ -389,7 +469,7 @@ DEFUN (print_header, (prefix), const char prefix)
   if (bsd_style_output)
     {
       printf (_("\ngranularity: each sample hit covers %ld byte(s)"),
-             (long) hist_scale * sizeof (UNIT));
+             (long) hist_scale * (long) sizeof (UNIT));
       if (total_time > 0.0)
        {
          printf (_(" for %.2f%% of %.2f %s\n\n"),
@@ -404,13 +484,14 @@ DEFUN (print_header, (prefix), const char prefix)
   if (total_time <= 0.0)
     {
       printf (_(" no time accumulated\n\n"));
-      
+
       /* This doesn't hurt since all the numerators will be zero.  */
       total_time = 1.0;
     }
 
   printf ("%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s  %-8.8s\n",
-         "%  ", _("cumulative"), _("self  "), "", _("self  "), _("total "), "");
+         "%  ", _("cumulative"), _("self  "), "", _("self  "), _("total "),
+         "");
   printf ("%5.5s %9.9s  %8.8s %8.8s %8.8s %8.8s  %-8.8s\n",
          _("time"), hist_dimension, hist_dimension, _("calls"), unit, unit,
          _("name"));
@@ -418,13 +499,13 @@ DEFUN (print_header, (prefix), const char prefix)
 
 
 static void
-DEFUN (print_line, (sym, scale), Sym * sym AND double scale)
+print_line (Sym *sym, double scale)
 {
   if (ignore_zeros && sym->ncalls == 0 && sym->hist.time == 0)
     return;
 
   accum_time += sym->hist.time;
-  
+
   if (bsd_style_output)
     printf ("%5.1f %10.2f %8.2f",
            total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
@@ -433,14 +514,14 @@ DEFUN (print_line, (sym, scale), Sym * sym AND double scale)
     printf ("%6.2f %9.2f %8.2f",
            total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
            accum_time / hz, sym->hist.time / hz);
-  
+
   if (sym->ncalls != 0)
     printf (" %8lu %8.2f %8.2f  ",
            sym->ncalls, scale * sym->hist.time / hz / sym->ncalls,
            scale * (sym->hist.time + sym->cg.child_time) / hz / sym->ncalls);
   else
     printf (" %8.8s %8.8s %8.8s  ", "", "", "");
-  
+
   if (bsd_style_output)
     print_name (sym);
   else
@@ -455,17 +536,17 @@ DEFUN (print_line, (sym, scale), Sym * sym AND double scale)
    lexicographic order of the function names.  */
 
 static int
-DEFUN (cmp_time, (lp, rp), const PTR lp AND const PTR rp)
+cmp_time (const PTR lp, const PTR rp)
 {
   const Sym *left = *(const Sym **) lp;
   const Sym *right = *(const Sym **) rp;
   double time_diff;
 
   time_diff = right->hist.time - left->hist.time;
-  
+
   if (time_diff > 0.0)
     return 1;
-  
+
   if (time_diff < 0.0)
     return -1;
 
@@ -482,12 +563,12 @@ DEFUN (cmp_time, (lp, rp), const PTR lp AND const PTR rp)
 /* Print the flat histogram profile.  */
 
 void
-DEFUN_VOID (hist_print)
+hist_print ()
 {
   Sym **time_sorted_syms, *top_dog, *sym;
-  unsigned int index;
-  int log_scale;
-  double top_time, time;
+  unsigned int sym_index;
+  unsigned log_scale;
+  double top_time;
   bfd_vma addr;
 
   if (first_output)
@@ -496,7 +577,7 @@ DEFUN_VOID (hist_print)
     printf ("\f\n");
 
   accum_time = 0.0;
-  
+
   if (bsd_style_output)
     {
       if (print_descriptions)
@@ -509,13 +590,13 @@ DEFUN_VOID (hist_print)
     {
       printf (_("Flat profile:\n"));
     }
-  
+
   /* Sort the symbol table by time (call-count and name as secondary
      and tertiary keys).  */
   time_sorted_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *));
-  
-  for (index = 0; index < symtab.len; ++index)
-    time_sorted_syms[index] = &symtab.base[index];
+
+  for (sym_index = 0; sym_index < symtab.len; ++sym_index)
+    time_sorted_syms[sym_index] = &symtab.base[sym_index];
 
   qsort (time_sorted_syms, symtab.len, sizeof (Sym *), cmp_time);
 
@@ -530,32 +611,35 @@ DEFUN_VOID (hist_print)
       log_scale = 0;
       top_dog = 0;
       top_time = 0.0;
-      
-      for (index = 0; index < symtab.len; ++index)
+
+      for (sym_index = 0; sym_index < symtab.len; ++sym_index)
        {
-         sym = time_sorted_syms[index];
-         
+         sym = time_sorted_syms[sym_index];
+
          if (sym->ncalls != 0)
            {
-             time = (sym->hist.time + sym->cg.child_time) / sym->ncalls;
-             
-             if (time > top_time)
+             double call_time;
+
+             call_time = (sym->hist.time + sym->cg.child_time) / sym->ncalls;
+
+             if (call_time > top_time)
                {
                  top_dog = sym;
-                 top_time = time;
+                 top_time = call_time;
                }
            }
        }
-      
+
       if (top_dog && top_dog->ncalls != 0 && top_time > 0.0)
        {
          top_time /= hz;
-         
-         while (SItab[log_scale].scale * top_time < 1000.0
-                && ((size_t) log_scale
-                    < sizeof (SItab) / sizeof (SItab[0]) - 1))
+
+         for (log_scale = 0; log_scale < ARRAY_SIZE (SItab); log_scale ++)
            {
-             ++log_scale;
+             double scaled_value = SItab[log_scale].scale * top_time;
+
+             if (scaled_value >= 1.0 && scaled_value < 1000.0) 
+               break;
            }
        }
     }
@@ -564,21 +648,108 @@ DEFUN_VOID (hist_print)
      may also want to support other (pseudo-)dimensions (such as
      I-cache misses etc.).  */
   print_header (SItab[log_scale].prefix);
-  
-  for (index = 0; index < symtab.len; ++index)
+
+  for (sym_index = 0; sym_index < symtab.len; ++sym_index)
     {
-      addr = time_sorted_syms[index]->addr;
-      
+      addr = time_sorted_syms[sym_index]->addr;
+
       /* Print symbol if its in INCL_FLAT table or that table
-        is empty and the symbol is not in EXCL_FLAT.  */
+       is empty and the symbol is not in EXCL_FLAT.  */
       if (sym_lookup (&syms[INCL_FLAT], addr)
          || (syms[INCL_FLAT].len == 0
              && !sym_lookup (&syms[EXCL_FLAT], addr)))
-       print_line (time_sorted_syms[index], SItab[log_scale].scale);
+       print_line (time_sorted_syms[sym_index], SItab[log_scale].scale);
     }
-  
+
   free (time_sorted_syms);
 
   if (print_descriptions && !bsd_style_output)
     flat_blurb (stdout);
 }
+
+int
+hist_check_address (unsigned address)
+{
+  unsigned i;
+
+  for (i = 0; i < num_histograms; ++i)
+    if (histograms[i].lowpc <= address && address < histograms[i].highpc)
+      return 1;
+
+  return 0;        
+}
+
+#if ! defined(min)
+#define min(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+#if ! defined(max)
+#define max(a,b) (((a)>(b)) ? (a) : (b))
+#endif
+
+void
+hist_clip_symbol_address (bfd_vma *p_lowpc, bfd_vma *p_highpc)
+{
+  unsigned i;
+  int found = 0;
+
+  if (num_histograms == 0)
+    {
+      *p_highpc = *p_lowpc;
+      return;
+    }
+
+  for (i = 0; i < num_histograms; ++i)
+    {
+      bfd_vma common_low, common_high;
+      common_low = max (histograms[i].lowpc, *p_lowpc);
+      common_high = min (histograms[i].highpc, *p_highpc);
+
+      if (common_low < common_high)
+       {
+         if (found)
+           {
+             fprintf (stderr,
+                      _("%s: found a symbol that covers "
+                        "several histogram records"),
+                        whoami);
+             done (1);
+           }
+
+         found = 1;
+         *p_lowpc = common_low;
+         *p_highpc = common_high;
+       }
+    }
+
+  if (!found)
+    *p_highpc = *p_lowpc;
+}
+
+/* Find and return exising histogram record having the same lowpc and
+   highpc as passed via the parameters.  Return NULL if nothing is found.
+   The return value is valid until any new histogram is read.  */
+static histogram *
+find_histogram (bfd_vma lowpc, bfd_vma highpc)
+{
+  unsigned i;
+  for (i = 0; i < num_histograms; ++i)
+    {
+      if (histograms[i].lowpc == lowpc && histograms[i].highpc == highpc)
+       return &histograms[i];
+    }
+  return 0;
+}
+
+/* Given a PC, return histogram record which address range include this PC.
+   Return NULL if there's no such record.  */
+static histogram *
+find_histogram_for_pc (bfd_vma pc)
+{
+  unsigned i;
+  for (i = 0; i < num_histograms; ++i)
+    {
+      if (histograms[i].lowpc <= pc && pc < histograms[i].highpc)
+       return &histograms[i];
+    }
+  return 0;  
+}