* elf64-ppc.c (dec_dynrel_count): Don't error when elf_gc_sweep_symbol
[external/binutils.git] / gprof / hist.c
index 0b2719b..91b0000 100644 (file)
-/*
- * Histogram related operations.
- */
-#include <stdio.h>
-#include "libiberty.h"
+/* hist.c  -  Histogram related operations.
+
+   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 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   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., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+\f
 #include "gprof.h"
-#include "core.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"
 
-/* declarations of automatically generated functions to output blurbs: */
-extern void flat_blurb PARAMS((FILE *fp));
+#define UNITS_TO_CODE (offset_to_code / sizeof(UNIT))
 
-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!) */
+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 (FILE * fp);
+
+static histogram *find_histogram (bfd_vma lowpc, bfd_vma highpc);
+static histogram *find_histogram_for_pc (bfd_vma pc);
+
+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 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
- * scale some of the values in the flat profile).
- */
-const struct {
+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 total_time;      /* Total time for all routines.  */
+
+/* Table of SI prefixes for powers of 10 (used to automatically
+   scale some of the values in the flat profile).  */
+const struct
+  {
     char prefix;
     double scale;
-} 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 */
+  }
+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 */
 };
 
-/*
- * Read the histogram from file IFP.  FILENAME is the name of IFP and
- * is provided for formatting error messages only.
- */
+/* 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.  
+
+   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)
+{
+  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_hist_scale = (double)((record->highpc - record->lowpc) / sizeof (UNIT)) 
+    / record->num_bins;
+
+  if (first)
+    {
+      /* 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;      
+    }
+  else
+    {
+      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
-DEFUN(hist_read_rec, (ifp, filename), FILE *ifp AND const char *filename)
+hist_read_rec (FILE * ifp, const char *filename)
 {
-    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) {
-       fprintf(stderr, "%s: %s: unexpected end of file\n",
-               whoami, filename);
-       done(1);
-    } /* if */
-
-    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;
-
-    if (!s_highpc) {
-
-       /* 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;
-       hz = profrate;
-    } /* if */
-
-    DBG(SAMPLEDEBUG,
-       printf("[hist_read_rec] n_lowpc 0x%lx n_highpc 0x%lx ncnt %d\n",
-              n_lowpc, n_highpc, ncnt);
-       printf("[hist_read_rec] s_lowpc 0x%lx s_highpc 0x%lx nsamples %d\n",
-              s_lowpc, s_highpc, hist_num_bins);
-       printf("[hist_read_rec]   lowpc 0x%lx   highpc 0x%lx\n",
-              lowpc, highpc));
-
-    if (n_lowpc != s_lowpc || n_highpc != s_highpc
-       || ncnt != hist_num_bins || hz != profrate)
+  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);
+
+  existing_record = find_histogram (n_record.lowpc, n_record.highpc);
+  if (existing_record)
     {
-       fprintf(stderr, "%s: `%s' is incompatible with first gmon file\n",
-               whoami, filename);
-       done(1);
-    } /* if */
-
-    if (!hist_sample) {
-       hist_sample = (int*)xmalloc(hist_num_bins * sizeof(hist_sample[0]));
-       memset(hist_sample, 0, hist_num_bins * sizeof(hist_sample[0]));
-    } /* if */
-
-    for (i = 0; i < hist_num_bins; ++i) {
-       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);
-           done(1);
-       } /* if */
-       hist_sample[i] += bfd_get_16(core_bfd, (bfd_byte*) &count[0]);
-    } /* for */
-} /* hist_read_rec */
-
-
-/*
- * Write execution histogram to file OFP.  FILENAME is the name
- * of OFP and is provided for formatting error-messages only.
- */
+      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.  */
+
+  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 %u of %u samples\n"),
+                  whoami, filename, i, record->num_bins);
+         done (1);
+       }
+      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 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: */
-
-    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)
+  UNIT count;
+  unsigned int i, r;
+
+  for (r = 0; r < num_histograms; ++r)
     {
-       perror(filename);
-       done(1);
-    } /* if */
-
-    for (i = 0; i < hist_num_bins; ++i) {
-       bfd_put_16(core_bfd, hist_sample[i], (bfd_byte*) &count[0]);
-       if (fwrite(&count[0], sizeof(count), 1, ofp) != 1) {
-           perror(filename);
-           done(1);
-       } /* if */
-    } /* for */
-} /* hist_write_hist */
-
-
-/*
- * 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
- * entry points over the procedure entry mask, if it turns out that
- * the entry point is in one bin and the code for a routine is in the
- * next bin.
- */
+      histogram *record = &histograms[r];
+
+      /* Write header.  */
+      
+      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
+   entry points over the procedure entry mask, if it turns out that
+   the entry point is in one bin and the code for a routine is in the
+   next bin.  */
+
 static void
-DEFUN_VOID(scale_and_align_entries)
+scale_and_align_entries ()
 {
-    Sym *sym;
-#if OFFSET_TO_CODE > 0
-    bfd_vma bin_of_entry;
-    bfd_vma bin_of_code;
-#endif
+  Sym *sym;
+  bfd_vma bin_of_entry;
+  bfd_vma bin_of_code;
 
-    for (sym = symtab.base; sym < symtab.limit; sym++) {
-       sym->hist.scaled_addr = sym->addr / sizeof(UNIT);
-#if OFFSET_TO_CODE > 0
-       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) {
-           DBG(SAMPLEDEBUG,
-               printf("[scale_and_align_entries] pushing 0x%lx to 0x%lx\n",
-                      sym->hist.scaled_addr, sym->aligned_addr + UNITS_TO_CODE));
-           sym->aligned_addr += UNITS_TO_CODE;
-       } /* if */
-#endif /* OFFSET_TO_CODE > 0 */
-    } /* for */
-} /* scale_and_align_entries */
-
-
-/*
- * 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
- *
- *            +-----------------------------------------------+
- *            |                                               |
- *       |  ->|    |<-         ->|         |<-         ->|    |<-  |
- *       |         |             |         |             |         |
- *       +---------+             +---------+             +---------+
- *
- *       ^         ^             ^         ^             ^         ^
- *       |         |             |         |             |         |
- *   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
- * mask falls in one bin but the code for the routine doesn't start
- * until the next bin.  In conjunction with the alignment of routine
- * addresses, this should allow us to have only one sample for every
- * four bytes of text space and never have any overlap (the two end
- * cases, above).
- */
-void
-DEFUN_VOID(hist_assign_samples)
+  for (sym = symtab.base; sym < symtab.limit; sym++)
+    {
+      histogram *r = find_histogram_for_pc (sym->addr);
+
+      sym->hist.scaled_addr = sym->addr / sizeof (UNIT);
+
+      if (r)
+       {
+         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
+
+              +-----------------------------------------------+
+              |                                               |
+         |  ->|    |<-         ->|         |<-         ->|    |<-  |
+         |         |             |         |             |         |
+         +---------+             +---------+             +---------+
+
+         ^         ^             ^         ^             ^         ^
+         |         |             |         |             |         |
+     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
+   mask falls in one bin but the code for the routine doesn't start
+   until the next bin.  In conjunction with the alignment of routine
+   addresses, this should allow us to have only one sample for every
+   four bytes of text space and never have any overlap (the two end
+   cases, above).  */
+
+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, j;
-    double time, credit;
-
-    /* read samples and assign to symbols: */
-    hist_scale = highpc - lowpc;
-    hist_scale /= hist_num_bins;
-    scale_and_align_entries();
-
-    /* iterate over all sample bins: */
-
-    for (i = 0, j = 1; i < hist_num_bins; ++i) {
-       bin_count = hist_sample[i];
-       if (!bin_count) {
+  bfd_vma bin_low_pc, bin_high_pc;
+  bfd_vma sym_low_pc, sym_high_pc;
+  bfd_vma overlap, addr;
+  unsigned int bin_count;
+  unsigned int i, j, k;
+  double count_time, credit;
+
+  bfd_vma lowpc = r->lowpc / sizeof (UNIT);
+
+  /* Iterate over all sample bins.  */
+  for (i = 0, k = 1; i < r->num_bins; ++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));
+      count_time = bin_count;
+
+      DBG (SAMPLEDEBUG,
+          printf (
+      "[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 += count_time;
+
+      /* 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)
+           break;
+
+         /* If low end of bin is above high end of symbol,
+            go for next symbol.  */
+         if (bin_low_pc >= sym_high_pc)
            continue;
-       } /* if */
-       bin_low_pc = lowpc + (bfd_vma)(hist_scale * i);
-       bin_high_pc = lowpc + (bfd_vma)(hist_scale * (i + 1));
-       time = bin_count;
-       DBG(SAMPLEDEBUG,
-           printf(
-"[assign_samples] bin_low_pc=0x%lx, bin_high_pc=0x%lx, bin_count=%d\n",
-                  sizeof(UNIT) * bin_low_pc, sizeof(UNIT) * bin_high_pc,
-                  bin_count));
-       total_time += time;
-
-       /* credit all symbols that are covered by bin I: */
-
-       for (j = j - 1; j < symtab.len; ++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) {
-               break;
-           } /* if */
-           /*
-            * If low end of bin is above high end of symbol, go for
-            * next symbol.
-            */
-           if (bin_low_pc >= sym_high_pc) {
-               continue;
-           } /* if */
-           overlap =
-             MIN(bin_high_pc, sym_high_pc) - MAX(bin_low_pc, sym_low_pc);
-           if (overlap > 0) {
-               DBG(SAMPLEDEBUG,
-                   printf(
-"[assign_samples] [0x%lx,0x%lx) %s gets %f ticks %ld overlap\n",
-                          symtab.base[j].addr, sizeof(UNIT) * sym_high_pc,
-                          symtab.base[j].name, overlap * time / hist_scale,
-                          overlap));
-               addr = symtab.base[j].addr;
-               credit = overlap * 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.
-                */
-               if (sym_lookup(&syms[INCL_FLAT], addr)
-                   || (syms[INCL_FLAT].len == 0
-                       && !sym_lookup(&syms[EXCL_FLAT], addr)))
+
+         overlap =
+           MIN (bin_high_pc, sym_high_pc) - MAX (bin_low_pc, sym_low_pc);
+         if (overlap > 0)
+           {
+             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 * count_time / hist_scale,
+                          (long) overlap));
+
+             addr = symtab.base[j].addr;
+             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.  */
+             if (sym_lookup (&syms[INCL_FLAT], addr)
+                 || (syms[INCL_FLAT].len == 0
+                     && !sym_lookup (&syms[EXCL_FLAT], addr)))
+               {
+                 symtab.base[j].hist.time += credit;
+               }
+             else
                {
-                   symtab.base[j].hist.time += credit;
-               } else {
-                   total_time -= credit;
-               } /* if */
-           } /* if */
-       } /* if */
-    } /* for */
-    DBG(SAMPLEDEBUG, printf("[assign_samples] total_time %f\n",
+                 total_time -= credit;
+               }
+           }
+       }
+    }
+
+  DBG (SAMPLEDEBUG, printf ("[assign_samples] total_time %f\n",
                            total_time));
-} /* hist_assign_samples */
+}
 
+/* 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.  */
 
-/*
- * Print header for flag histogram profile:
- */
 static void
-DEFUN(print_header, (prefix), const char prefix)
+print_header (int prefix)
 {
-    char unit[64];
-
-    sprintf(unit, "%c%c/call", prefix, hist_dimension_abbrev);
-
-    if (bsd_style_output) {
-       printf("\ngranularity: each sample hit covers %ld byte(s)",
-              (long) hist_scale * sizeof(UNIT));
-       if (total_time > 0.0) {
-           printf(" for %.2f%% of %.2f %s\n\n",
-                  100.0/total_time, total_time/hz, hist_dimension);
-       } /* if */
-    } else {
-       printf("\nEach sample counts as %g %s.\n", 1.0 / hz, hist_dimension);
-    } /* if */
-
-    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;
-    } /* if */
-
-    printf("%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s  %-8.8s\n",
-          "%  ", "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");
-} /* print_header */
+  char unit[64];
+
+  sprintf (unit, _("%c%c/call"), prefix, hist_dimension_abbrev);
+
+  if (bsd_style_output)
+    {
+      printf (_("\ngranularity: each sample hit covers %ld byte(s)"),
+             (long) hist_scale * (long) sizeof (UNIT));
+      if (total_time > 0.0)
+       {
+         printf (_(" for %.2f%% of %.2f %s\n\n"),
+                 100.0 / total_time, total_time / hz, hist_dimension);
+       }
+    }
+  else
+    {
+      printf (_("\nEach sample counts as %g %s.\n"), 1.0 / hz, hist_dimension);
+    }
+
+  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 "),
+         "");
+  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"));
+}
 
 
 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;
-    } /* if */
-
-    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,
-              accum_time / hz, sym->hist.time / hz);
-    } else {
-       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 */
-    if (sym->ncalls) {
-       printf(" %8d %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 */
-    if (bsd_style_output) {
-       print_name(sym);
-    } else {
-       print_name_only(sym);
-    } /* if */
-    printf("\n");
-} /* print_line */
-
-
-/*
- * Compare LP and RP.  The primary comparison key is execution time,
- * the secondary is number of invocation, and the tertiary is the
- * lexicographic order of the function names.
- */
+  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,
+           accum_time / hz, sym->hist.time / hz);
+  else
+    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
+    print_name_only (sym);
+
+  printf ("\n");
+}
+
+
+/* Compare LP and RP.  The primary comparison key is execution time,
+   the secondary is number of invocation, and the tertiary is the
+   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;
-    long call_diff;
-
-    time_diff = right->hist.time - left->hist.time;
-    if (time_diff > 0.0) {
-       return 1;
-    } /* if */
-    if (time_diff < 0.0) {
-       return -1;
-    } /* if */
-
-    call_diff = right->ncalls - left->ncalls;
-    if (call_diff > 0) {
-       return 1;
-    } /* if */
-    if (call_diff < 0) {
-       return -1;
-    } /* if */
-
-    return strcmp(left->name, right->name);
-} /* cmp_time */
-
-
-/*
- * Print the flat histogram profile.
- */
+  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;
+
+  if (right->ncalls > left->ncalls)
+    return 1;
+
+  if (right->ncalls < left->ncalls)
+    return -1;
+
+  return strcmp (left->name, right->name);
+}
+
+
+/* Print the flat histogram profile.  */
+
 void
-DEFUN_VOID(hist_print)
+hist_print ()
 {
-    Sym **time_sorted_syms, *top_dog, *sym;
-    int index, log_scale;
-    double top_time, time;
-    bfd_vma addr;
-
-    if (first_output) {
-       first_output = FALSE;
-    } else {
-       printf("\f\n");
-    } /* if */
-
-    accum_time = 0.0;
-    if (bsd_style_output) {
-       if (print_descriptions) {
-           printf("\n\n\nflat profile:\n");
-           flat_blurb(stdout);
-       } /* if */
-    } else {
-       printf ("Flat profile:\n");
-    } /* if */
-    /*
-     * 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 */
-    qsort(time_sorted_syms, symtab.len, sizeof(Sym *), cmp_time);
-
-    if (bsd_style_output) {
-       log_scale = 5;          /* milli-seconds is BSD-default */
-    } else {
-       /*
-        * Search for symbol with highest per-call execution time and
-        * scale accordingly:
-        */
-       log_scale = 0;
-       top_dog = 0;
-       top_time = 0.0;
-       for (index = 0; index < symtab.len; ++index) {
-           sym = time_sorted_syms[index];
-           if (sym->ncalls) {
-               time = (sym->hist.time + sym->cg.child_time) / sym->ncalls;
-               if (time > top_time) {
-                   top_dog = sym;
-                   top_time = time;
-               } /* if */
-           } /* if */
-       } /* for */
-       if (top_dog && top_dog->ncalls && top_time > 0.0) {
-           top_time /= hz;
-           while (SItab[log_scale].scale * top_time < 1000.0
-                  && log_scale < sizeof(SItab)/sizeof(SItab[0]) - 1)
+  Sym **time_sorted_syms, *top_dog, *sym;
+  unsigned int sym_index;
+  unsigned log_scale;
+  double top_time;
+  bfd_vma addr;
+
+  if (first_output)
+    first_output = FALSE;
+  else
+    printf ("\f\n");
+
+  accum_time = 0.0;
+
+  if (bsd_style_output)
+    {
+      if (print_descriptions)
+       {
+         printf (_("\n\n\nflat profile:\n"));
+         flat_blurb (stdout);
+       }
+    }
+  else
+    {
+      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 (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);
+
+  if (bsd_style_output)
+    {
+      log_scale = 5;           /* Milli-seconds is BSD-default.  */
+    }
+  else
+    {
+      /* Search for symbol with highest per-call
+        execution time and scale accordingly.  */
+      log_scale = 0;
+      top_dog = 0;
+      top_time = 0.0;
+
+      for (sym_index = 0; sym_index < symtab.len; ++sym_index)
+       {
+         sym = time_sorted_syms[sym_index];
+
+         if (sym->ncalls != 0)
            {
-               ++log_scale;
-           } /* while */
-       } /* if */
-    } /* if */
-
-    /*
-     * For now, the dimension is always seconds.  In the future, we
-     * 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) {
-       addr = time_sorted_syms[index]->addr;
-       /*
-        * Print symbol if its in INCL_FLAT table or that table
-        * 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)))
+             double call_time;
+
+             call_time = (sym->hist.time + sym->cg.child_time) / sym->ncalls;
+
+             if (call_time > top_time)
+               {
+                 top_dog = sym;
+                 top_time = call_time;
+               }
+           }
+       }
+
+      if (top_dog && top_dog->ncalls != 0 && top_time > 0.0)
        {
-           print_line(time_sorted_syms[index], SItab[log_scale].scale);
-       } /* if */
-    } /* for */
-    free(time_sorted_syms);
+         top_time /= hz;
+
+         for (log_scale = 0; log_scale < ARRAY_SIZE (SItab); log_scale ++)
+           {
+             double scaled_value = SItab[log_scale].scale * top_time;
+
+             if (scaled_value >= 1.0 && scaled_value < 1000.0) 
+               break;
+           }
+       }
+    }
+
+  /* For now, the dimension is always seconds.  In the future, we
+     may also want to support other (pseudo-)dimensions (such as
+     I-cache misses etc.).  */
+  print_header (SItab[log_scale].prefix);
+
+  for (sym_index = 0; sym_index < symtab.len; ++sym_index)
+    {
+      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.  */
+      if (sym_lookup (&syms[INCL_FLAT], addr)
+         || (syms[INCL_FLAT].len == 0
+             && !sym_lookup (&syms[EXCL_FLAT], addr)))
+       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;
 
-    if (print_descriptions && !bsd_style_output) {
-       flat_blurb(stdout);
-    } /* if */
-} /* hist_print */
+  for (i = 0; i < num_histograms; ++i)
+    if (histograms[i].lowpc <= address && address < histograms[i].highpc)
+      return 1;
 
-                       /*** end of hist.c ***/
+  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;  
+}