gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / gprof / hist.c
index a3f032907527e21c429c09ce8dfe526e7dabffe7..55dc2007c4a39be086d7e2b579b50c70ca90cd7a 100644 (file)
@@ -1,13 +1,12 @@
 /* hist.c  -  Histogram related operations.
 
-   Copyright 1999, 2000, 2001, 2002, 2004, 2005
-   Free Software Foundation, Inc.
+   Copyright (C) 1999-2020 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 "libiberty.h"
 #include "gprof.h"
+#include "libiberty.h"
 #include "search_list.h"
 #include "source.h"
 #include "symtab.h"
@@ -31,6 +30,9 @@
 #include "hist.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))
 
@@ -42,14 +44,14 @@ static int cmp_time (const PTR, const PTR);
 /* Declarations of automatically generated functions to output blurbs.  */
 extern void flat_blurb (FILE * fp);
 
-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.  */
-unsigned int hist_num_bins = 0;        /* Number of histogram samples.  */
-int *hist_sample = 0;          /* Histogram samples (shorts in the file!).  */
+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[16] = "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 total_time;      /* Total time for all routines.  */
@@ -76,23 +78,30 @@ SItab[] =
   { '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
-hist_read_rec (FILE * ifp, 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)
 {
-  bfd_vma n_lowpc, n_highpc;
-  unsigned int i, ncnt, profrate;
-  UNIT count;
-
-  if (gmon_io_read_vma (ifp, &n_lowpc)
-      || gmon_io_read_vma (ifp, &n_highpc)
-      || gmon_io_read_32 (ifp, &ncnt)
+  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, hist_dimension, 15)
-      || gmon_io_read (ifp, &hist_dimension_abbrev, 1))
+      || 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);
@@ -100,94 +109,178 @@ hist_read_rec (FILE * ifp, const char *filename)
       done (1);
     }
 
-  if (!s_highpc)
+  n_hist_scale = (double)((record->highpc - record->lowpc) / sizeof (UNIT))
+    / record->num_bins;
+
+  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 %u\n",
-              (unsigned long) n_lowpc, (unsigned long) n_highpc, ncnt);
-       printf ("[hist_read_rec] s_lowpc 0x%lx s_highpc 0x%lx nsamples %u\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 != (int) 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);
+       }
     }
+}
 
-  if (!hist_sample)
+/* 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);
+
+  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.  */
+
+  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 < hist_num_bins; ++i)
+  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, hist_num_bins);
+                  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) (n_lowpc + i * (n_highpc - n_lowpc) / ncnt),
-                  hist_sample[i]));
+                  (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
 hist_write_hist (FILE * ofp, const char *filename)
 {
   UNIT count;
-  unsigned int i;
+  unsigned int i, r;
 
-  /* Write header.  */
-
-  if (gmon_io_write_8 (ofp, GMON_TAG_TIME_HIST)
-      || gmon_io_write_vma (ofp, s_lowpc)
-      || gmon_io_write_vma (ofp, s_highpc)
-      || gmon_io_write_32 (ofp, hist_num_bins)
-      || gmon_io_write_32 (ofp, hz)
-      || gmon_io_write (ofp, hist_dimension, 15)
-      || gmon_io_write (ofp, &hist_dimension_abbrev, 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, (bfd_vma) 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
@@ -196,7 +289,7 @@ hist_write_hist (FILE * ofp, const char *filename)
    next bin.  */
 
 static void
-scale_and_align_entries ()
+scale_and_align_entries (void)
 {
   Sym *sym;
   bfd_vma bin_of_entry;
@@ -204,18 +297,24 @@ 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;
+           }
        }
     }
 }
@@ -258,31 +357,28 @@ scale_and_align_entries ()
    four bytes of text space and never have any overlap (the two end
    cases, above).  */
 
-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;
   unsigned int bin_count;
-  unsigned int i, j;
-  double time, credit;
+  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 (
@@ -290,10 +386,13 @@ hist_assign_samples ()
                    (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.
 
-      /* Credit all symbols that are covered by bin I.  */
-      for (j = j - 1; j < symtab.len; ++j)
+         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;
@@ -317,11 +416,11 @@ hist_assign_samples ()
               "[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,
+                          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
@@ -344,6 +443,18 @@ hist_assign_samples ()
                            total_time));
 }
 
+/* Calls 'hist_assign_sampes_1' for all histogram records read so far. */
+void
+hist_assign_samples (void)
+{
+  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.  */
 
@@ -357,7 +468,7 @@ print_header (int 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"),
@@ -451,12 +562,12 @@ cmp_time (const PTR lp, const PTR rp)
 /* Print the flat histogram profile.  */
 
 void
-hist_print ()
+hist_print (void)
 {
   Sym **time_sorted_syms, *top_dog, *sym;
-  unsigned int index;
+  unsigned int sym_index;
   unsigned log_scale;
-  double top_time, time;
+  double top_time;
   bfd_vma addr;
 
   if (first_output)
@@ -483,8 +594,8 @@ hist_print ()
      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);
 
@@ -500,18 +611,20 @@ hist_print ()
       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;
+             double call_time;
 
-             if (time > top_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;
                }
            }
        }
@@ -524,7 +637,7 @@ hist_print ()
            {
              double scaled_value = SItab[log_scale].scale * top_time;
 
-             if (scaled_value >= 1.0 && scaled_value < 1000.0) 
+             if (scaled_value >= 1.0 && scaled_value < 1000.0)
                break;
            }
        }
@@ -535,16 +648,16 @@ hist_print ()
      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.  */
       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);
@@ -552,3 +665,90 @@ hist_print ()
   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;
+}
This page took 0.031454 seconds and 4 git commands to generate.