175fd0b74Schristos /* hist.c - Histogram related operations.
275fd0b74Schristos
3*e992f068Schristos Copyright (C) 1999-2022 Free Software Foundation, Inc.
475fd0b74Schristos
575fd0b74Schristos This file is part of GNU Binutils.
675fd0b74Schristos
775fd0b74Schristos This program is free software; you can redistribute it and/or modify
875fd0b74Schristos it under the terms of the GNU General Public License as published by
975fd0b74Schristos the Free Software Foundation; either version 3 of the License, or
1075fd0b74Schristos (at your option) any later version.
1175fd0b74Schristos
1275fd0b74Schristos This program is distributed in the hope that it will be useful,
1375fd0b74Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
1475fd0b74Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1575fd0b74Schristos GNU General Public License for more details.
1675fd0b74Schristos
1775fd0b74Schristos You should have received a copy of the GNU General Public License
1875fd0b74Schristos along with this program; if not, write to the Free Software
1975fd0b74Schristos Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
2075fd0b74Schristos 02110-1301, USA. */
2175fd0b74Schristos
2275fd0b74Schristos #include "gprof.h"
2375fd0b74Schristos #include "libiberty.h"
2475fd0b74Schristos #include "search_list.h"
2575fd0b74Schristos #include "source.h"
2675fd0b74Schristos #include "symtab.h"
2775fd0b74Schristos #include "corefile.h"
2875fd0b74Schristos #include "gmon_io.h"
2975fd0b74Schristos #include "gmon_out.h"
3075fd0b74Schristos #include "hist.h"
3175fd0b74Schristos #include "sym_ids.h"
3275fd0b74Schristos #include "utils.h"
3375fd0b74Schristos #include "math.h"
3475fd0b74Schristos #include "stdio.h"
3575fd0b74Schristos #include "stdlib.h"
3675fd0b74Schristos
3775fd0b74Schristos #define UNITS_TO_CODE (offset_to_code / sizeof(UNIT))
3875fd0b74Schristos
3975fd0b74Schristos static void scale_and_align_entries (void);
4075fd0b74Schristos static void print_header (int);
4175fd0b74Schristos static void print_line (Sym *, double);
42*e992f068Schristos static int cmp_time (const void *, const void *);
4375fd0b74Schristos
4475fd0b74Schristos /* Declarations of automatically generated functions to output blurbs. */
4575fd0b74Schristos extern void flat_blurb (FILE * fp);
4675fd0b74Schristos
4775fd0b74Schristos static histogram *find_histogram (bfd_vma lowpc, bfd_vma highpc);
4875fd0b74Schristos static histogram *find_histogram_for_pc (bfd_vma pc);
4975fd0b74Schristos
5075fd0b74Schristos histogram * histograms;
5175fd0b74Schristos unsigned num_histograms;
5275fd0b74Schristos double hist_scale;
5375fd0b74Schristos static char hist_dimension[16] = "seconds";
5475fd0b74Schristos static char hist_dimension_abbrev = 's';
5575fd0b74Schristos
5675fd0b74Schristos static double accum_time; /* Accumulated time so far for print_line(). */
5775fd0b74Schristos static double total_time; /* Total time for all routines. */
5875fd0b74Schristos
5975fd0b74Schristos /* Table of SI prefixes for powers of 10 (used to automatically
6075fd0b74Schristos scale some of the values in the flat profile). */
6175fd0b74Schristos const struct
6275fd0b74Schristos {
6375fd0b74Schristos char prefix;
6475fd0b74Schristos double scale;
6575fd0b74Schristos }
6675fd0b74Schristos SItab[] =
6775fd0b74Schristos {
6875fd0b74Schristos { 'T', 1e-12 }, /* tera */
6975fd0b74Schristos { 'G', 1e-09 }, /* giga */
7075fd0b74Schristos { 'M', 1e-06 }, /* mega */
7175fd0b74Schristos { 'K', 1e-03 }, /* kilo */
7275fd0b74Schristos { ' ', 1e-00 },
7375fd0b74Schristos { 'm', 1e+03 }, /* milli */
7475fd0b74Schristos { 'u', 1e+06 }, /* micro */
7575fd0b74Schristos { 'n', 1e+09 }, /* nano */
7675fd0b74Schristos { 'p', 1e+12 }, /* pico */
7775fd0b74Schristos { 'f', 1e+15 }, /* femto */
7875fd0b74Schristos { 'a', 1e+18 } /* ato */
7975fd0b74Schristos };
8075fd0b74Schristos
8175fd0b74Schristos /* Reads just the header part of histogram record into
8275fd0b74Schristos *RECORD from IFP. FILENAME is the name of IFP and
8375fd0b74Schristos is provided for formatting error messages only.
8475fd0b74Schristos
8575fd0b74Schristos If FIRST is non-zero, sets global variables HZ, HIST_DIMENSION,
8675fd0b74Schristos HIST_DIMENSION_ABBREV, HIST_SCALE. If FIRST is zero, checks
8775fd0b74Schristos that the new histogram is compatible with already-set values
8875fd0b74Schristos of those variables and emits an error if that's not so. */
8975fd0b74Schristos static void
read_histogram_header(histogram * record,FILE * ifp,const char * filename,int first)9075fd0b74Schristos read_histogram_header (histogram *record,
9175fd0b74Schristos FILE *ifp, const char *filename,
9275fd0b74Schristos int first)
9375fd0b74Schristos {
9475fd0b74Schristos unsigned int profrate;
9575fd0b74Schristos char n_hist_dimension[15];
9675fd0b74Schristos char n_hist_dimension_abbrev;
9775fd0b74Schristos double n_hist_scale;
9875fd0b74Schristos
9975fd0b74Schristos if (gmon_io_read_vma (ifp, &record->lowpc)
10075fd0b74Schristos || gmon_io_read_vma (ifp, &record->highpc)
10175fd0b74Schristos || gmon_io_read_32 (ifp, &record->num_bins)
10275fd0b74Schristos || gmon_io_read_32 (ifp, &profrate)
10375fd0b74Schristos || gmon_io_read (ifp, n_hist_dimension, 15)
10475fd0b74Schristos || gmon_io_read (ifp, &n_hist_dimension_abbrev, 1))
10575fd0b74Schristos {
10675fd0b74Schristos fprintf (stderr, _("%s: %s: unexpected end of file\n"),
10775fd0b74Schristos whoami, filename);
10875fd0b74Schristos
10975fd0b74Schristos done (1);
11075fd0b74Schristos }
11175fd0b74Schristos
11275fd0b74Schristos n_hist_scale = (double)((record->highpc - record->lowpc) / sizeof (UNIT))
11375fd0b74Schristos / record->num_bins;
11475fd0b74Schristos
11575fd0b74Schristos if (first)
11675fd0b74Schristos {
11775fd0b74Schristos /* We don't try to veryfy profrate is the same for all histogram
11875fd0b74Schristos records. If we have two histogram records for the same
11975fd0b74Schristos address range and profiling samples is done as often
12075fd0b74Schristos as possible as opposed on timer, then the actual profrate will
12175fd0b74Schristos be slightly different. Most of the time the difference does not
12275fd0b74Schristos matter and insisting that profiling rate is exactly the same
12375fd0b74Schristos will only create inconvenient. */
12475fd0b74Schristos hz = profrate;
12575fd0b74Schristos memcpy (hist_dimension, n_hist_dimension, 15);
12675fd0b74Schristos hist_dimension_abbrev = n_hist_dimension_abbrev;
12775fd0b74Schristos hist_scale = n_hist_scale;
12875fd0b74Schristos }
12975fd0b74Schristos else
13075fd0b74Schristos {
13175fd0b74Schristos if (strncmp (n_hist_dimension, hist_dimension, 15) != 0)
13275fd0b74Schristos {
13375fd0b74Schristos fprintf (stderr,
13475fd0b74Schristos _("%s: dimension unit changed between histogram records\n"
13575fd0b74Schristos "%s: from '%s'\n"
13675fd0b74Schristos "%s: to '%s'\n"),
13775fd0b74Schristos whoami, whoami, hist_dimension, whoami, n_hist_dimension);
13875fd0b74Schristos done (1);
13975fd0b74Schristos }
14075fd0b74Schristos
14175fd0b74Schristos if (n_hist_dimension_abbrev != hist_dimension_abbrev)
14275fd0b74Schristos {
14375fd0b74Schristos fprintf (stderr,
14475fd0b74Schristos _("%s: dimension abbreviation changed between histogram records\n"
14575fd0b74Schristos "%s: from '%c'\n"
14675fd0b74Schristos "%s: to '%c'\n"),
14775fd0b74Schristos whoami, whoami, hist_dimension_abbrev, whoami, n_hist_dimension_abbrev);
14875fd0b74Schristos done (1);
14975fd0b74Schristos }
15075fd0b74Schristos
15175fd0b74Schristos /* The only reason we require the same scale for histograms is that
15275fd0b74Schristos there's code (notably printing code), that prints units,
15375fd0b74Schristos and it would be very confusing to have one unit mean different
15475fd0b74Schristos things for different functions. */
15575fd0b74Schristos if (fabs (hist_scale - n_hist_scale) > 0.000001)
15675fd0b74Schristos {
15775fd0b74Schristos fprintf (stderr,
15875fd0b74Schristos _("%s: different scales in histogram records"),
15975fd0b74Schristos whoami);
16075fd0b74Schristos done (1);
16175fd0b74Schristos }
16275fd0b74Schristos }
16375fd0b74Schristos }
16475fd0b74Schristos
16575fd0b74Schristos /* Read the histogram from file IFP. FILENAME is the name of IFP and
16675fd0b74Schristos is provided for formatting error messages only. */
16775fd0b74Schristos
16875fd0b74Schristos void
hist_read_rec(FILE * ifp,const char * filename)16975fd0b74Schristos hist_read_rec (FILE * ifp, const char *filename)
17075fd0b74Schristos {
17175fd0b74Schristos bfd_vma lowpc, highpc;
17275fd0b74Schristos histogram n_record;
17375fd0b74Schristos histogram *record, *existing_record;
17475fd0b74Schristos unsigned i;
17575fd0b74Schristos
17675fd0b74Schristos /* 1. Read the header and see if there's existing record for the
17775fd0b74Schristos same address range and that there are no overlapping records. */
17875fd0b74Schristos read_histogram_header (&n_record, ifp, filename, num_histograms == 0);
17975fd0b74Schristos
18075fd0b74Schristos existing_record = find_histogram (n_record.lowpc, n_record.highpc);
18175fd0b74Schristos if (existing_record)
18275fd0b74Schristos {
18375fd0b74Schristos record = existing_record;
18475fd0b74Schristos }
18575fd0b74Schristos else
18675fd0b74Schristos {
18775fd0b74Schristos /* If this record overlaps, but does not completely match an existing
18875fd0b74Schristos record, it's an error. */
18975fd0b74Schristos lowpc = n_record.lowpc;
19075fd0b74Schristos highpc = n_record.highpc;
19175fd0b74Schristos hist_clip_symbol_address (&lowpc, &highpc);
19275fd0b74Schristos if (lowpc != highpc)
19375fd0b74Schristos {
19475fd0b74Schristos fprintf (stderr,
19575fd0b74Schristos _("%s: overlapping histogram records\n"),
19675fd0b74Schristos whoami);
19775fd0b74Schristos done (1);
19875fd0b74Schristos }
19975fd0b74Schristos
20075fd0b74Schristos /* This is new record. Add it to global array and allocate space for
20175fd0b74Schristos the samples. */
20275fd0b74Schristos histograms = (struct histogram *)
20375fd0b74Schristos xrealloc (histograms, sizeof (histogram) * (num_histograms + 1));
20475fd0b74Schristos memcpy (histograms + num_histograms,
20575fd0b74Schristos &n_record, sizeof (histogram));
20675fd0b74Schristos record = &histograms[num_histograms];
20775fd0b74Schristos ++num_histograms;
20875fd0b74Schristos
20975fd0b74Schristos record->sample = (int *) xmalloc (record->num_bins
21075fd0b74Schristos * sizeof (record->sample[0]));
21175fd0b74Schristos memset (record->sample, 0, record->num_bins * sizeof (record->sample[0]));
21275fd0b74Schristos }
21375fd0b74Schristos
21475fd0b74Schristos /* 2. We have either a new record (with zeroed histogram data), or an existing
21575fd0b74Schristos record with some data in the histogram already. Read new data into the
21675fd0b74Schristos record, adding hit counts. */
21775fd0b74Schristos
21875fd0b74Schristos DBG (SAMPLEDEBUG,
21975fd0b74Schristos printf ("[hist_read_rec] n_lowpc 0x%lx n_highpc 0x%lx ncnt %u\n",
22075fd0b74Schristos (unsigned long) record->lowpc, (unsigned long) record->highpc,
22175fd0b74Schristos record->num_bins));
22275fd0b74Schristos
22375fd0b74Schristos for (i = 0; i < record->num_bins; ++i)
22475fd0b74Schristos {
22575fd0b74Schristos UNIT count;
22675fd0b74Schristos if (fread (&count[0], sizeof (count), 1, ifp) != 1)
22775fd0b74Schristos {
22875fd0b74Schristos fprintf (stderr,
22975fd0b74Schristos _("%s: %s: unexpected EOF after reading %u of %u samples\n"),
23075fd0b74Schristos whoami, filename, i, record->num_bins);
23175fd0b74Schristos done (1);
23275fd0b74Schristos }
23375fd0b74Schristos record->sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) & count[0]);
23475fd0b74Schristos DBG (SAMPLEDEBUG,
23575fd0b74Schristos printf ("[hist_read_rec] 0x%lx: %u\n",
23675fd0b74Schristos (unsigned long) (record->lowpc
23775fd0b74Schristos + i * (record->highpc - record->lowpc)
23875fd0b74Schristos / record->num_bins),
23975fd0b74Schristos record->sample[i]));
24075fd0b74Schristos }
24175fd0b74Schristos }
24275fd0b74Schristos
24375fd0b74Schristos
24475fd0b74Schristos /* Write all execution histograms file OFP. FILENAME is the name
24575fd0b74Schristos of OFP and is provided for formatting error-messages only. */
24675fd0b74Schristos
24775fd0b74Schristos void
hist_write_hist(FILE * ofp,const char * filename)24875fd0b74Schristos hist_write_hist (FILE * ofp, const char *filename)
24975fd0b74Schristos {
25075fd0b74Schristos UNIT count;
25175fd0b74Schristos unsigned int i, r;
25275fd0b74Schristos
25375fd0b74Schristos for (r = 0; r < num_histograms; ++r)
25475fd0b74Schristos {
25575fd0b74Schristos histogram *record = &histograms[r];
25675fd0b74Schristos
25775fd0b74Schristos /* Write header. */
25875fd0b74Schristos
25975fd0b74Schristos if (gmon_io_write_8 (ofp, GMON_TAG_TIME_HIST)
26075fd0b74Schristos || gmon_io_write_vma (ofp, record->lowpc)
26175fd0b74Schristos || gmon_io_write_vma (ofp, record->highpc)
26275fd0b74Schristos || gmon_io_write_32 (ofp, record->num_bins)
26375fd0b74Schristos || gmon_io_write_32 (ofp, hz)
26475fd0b74Schristos || gmon_io_write (ofp, hist_dimension, 15)
26575fd0b74Schristos || gmon_io_write (ofp, &hist_dimension_abbrev, 1))
26675fd0b74Schristos {
26775fd0b74Schristos perror (filename);
26875fd0b74Schristos done (1);
26975fd0b74Schristos }
27075fd0b74Schristos
27175fd0b74Schristos for (i = 0; i < record->num_bins; ++i)
27275fd0b74Schristos {
27375fd0b74Schristos bfd_put_16 (core_bfd, (bfd_vma) record->sample[i], (bfd_byte *) &count[0]);
27475fd0b74Schristos
27575fd0b74Schristos if (fwrite (&count[0], sizeof (count), 1, ofp) != 1)
27675fd0b74Schristos {
27775fd0b74Schristos perror (filename);
27875fd0b74Schristos done (1);
27975fd0b74Schristos }
28075fd0b74Schristos }
28175fd0b74Schristos }
28275fd0b74Schristos }
28375fd0b74Schristos
28475fd0b74Schristos /* Calculate scaled entry point addresses (to save time in
28575fd0b74Schristos hist_assign_samples), and, on architectures that have procedure
28675fd0b74Schristos entry masks at the start of a function, possibly push the scaled
28775fd0b74Schristos entry points over the procedure entry mask, if it turns out that
28875fd0b74Schristos the entry point is in one bin and the code for a routine is in the
28975fd0b74Schristos next bin. */
29075fd0b74Schristos
29175fd0b74Schristos static void
scale_and_align_entries(void)29275fd0b74Schristos scale_and_align_entries (void)
29375fd0b74Schristos {
29475fd0b74Schristos Sym *sym;
29575fd0b74Schristos bfd_vma bin_of_entry;
29675fd0b74Schristos bfd_vma bin_of_code;
29775fd0b74Schristos
29875fd0b74Schristos for (sym = symtab.base; sym < symtab.limit; sym++)
29975fd0b74Schristos {
30075fd0b74Schristos histogram *r = find_histogram_for_pc (sym->addr);
30175fd0b74Schristos
30275fd0b74Schristos sym->hist.scaled_addr = sym->addr / sizeof (UNIT);
30375fd0b74Schristos
30475fd0b74Schristos if (r)
30575fd0b74Schristos {
30675fd0b74Schristos bin_of_entry = (sym->hist.scaled_addr - r->lowpc) / hist_scale;
30775fd0b74Schristos bin_of_code = ((sym->hist.scaled_addr + UNITS_TO_CODE - r->lowpc)
30875fd0b74Schristos / hist_scale);
30975fd0b74Schristos if (bin_of_entry < bin_of_code)
31075fd0b74Schristos {
31175fd0b74Schristos DBG (SAMPLEDEBUG,
31275fd0b74Schristos printf ("[scale_and_align_entries] pushing 0x%lx to 0x%lx\n",
31375fd0b74Schristos (unsigned long) sym->hist.scaled_addr,
31475fd0b74Schristos (unsigned long) (sym->hist.scaled_addr
31575fd0b74Schristos + UNITS_TO_CODE)));
31675fd0b74Schristos sym->hist.scaled_addr += UNITS_TO_CODE;
31775fd0b74Schristos }
31875fd0b74Schristos }
31975fd0b74Schristos }
32075fd0b74Schristos }
32175fd0b74Schristos
32275fd0b74Schristos
32375fd0b74Schristos /* Assign samples to the symbol to which they belong.
32475fd0b74Schristos
32575fd0b74Schristos Histogram bin I covers some address range [BIN_LOWPC,BIN_HIGH_PC)
32675fd0b74Schristos which may overlap one more symbol address ranges. If a symbol
32775fd0b74Schristos overlaps with the bin's address range by O percent, then O percent
32875fd0b74Schristos of the bin's count is credited to that symbol.
32975fd0b74Schristos
33075fd0b74Schristos There are three cases as to where BIN_LOW_PC and BIN_HIGH_PC can be
33175fd0b74Schristos with respect to the symbol's address range [SYM_LOW_PC,
33275fd0b74Schristos SYM_HIGH_PC) as shown in the following diagram. OVERLAP computes
33375fd0b74Schristos the distance (in UNITs) between the arrows, the fraction of the
33475fd0b74Schristos sample that is to be credited to the symbol which starts at
33575fd0b74Schristos SYM_LOW_PC.
33675fd0b74Schristos
33775fd0b74Schristos sym_low_pc sym_high_pc
33875fd0b74Schristos | |
33975fd0b74Schristos v v
34075fd0b74Schristos
34175fd0b74Schristos +-----------------------------------------------+
34275fd0b74Schristos | |
34375fd0b74Schristos | ->| |<- ->| |<- ->| |<- |
34475fd0b74Schristos | | | | | |
34575fd0b74Schristos +---------+ +---------+ +---------+
34675fd0b74Schristos
34775fd0b74Schristos ^ ^ ^ ^ ^ ^
34875fd0b74Schristos | | | | | |
34975fd0b74Schristos bin_low_pc bin_high_pc bin_low_pc bin_high_pc bin_low_pc bin_high_pc
35075fd0b74Schristos
35175fd0b74Schristos For the VAX we assert that samples will never fall in the first two
35275fd0b74Schristos bytes of any routine, since that is the entry mask, thus we call
35375fd0b74Schristos scale_and_align_entries() to adjust the entry points if the entry
35475fd0b74Schristos mask falls in one bin but the code for the routine doesn't start
35575fd0b74Schristos until the next bin. In conjunction with the alignment of routine
35675fd0b74Schristos addresses, this should allow us to have only one sample for every
35775fd0b74Schristos four bytes of text space and never have any overlap (the two end
35875fd0b74Schristos cases, above). */
35975fd0b74Schristos
36075fd0b74Schristos static void
hist_assign_samples_1(histogram * r)36175fd0b74Schristos hist_assign_samples_1 (histogram *r)
36275fd0b74Schristos {
36375fd0b74Schristos bfd_vma bin_low_pc, bin_high_pc;
36475fd0b74Schristos bfd_vma sym_low_pc, sym_high_pc;
36575fd0b74Schristos bfd_vma overlap, addr;
36675fd0b74Schristos unsigned int bin_count;
36775fd0b74Schristos unsigned int i, j, k;
36875fd0b74Schristos double count_time, credit;
36975fd0b74Schristos
37075fd0b74Schristos bfd_vma lowpc = r->lowpc / sizeof (UNIT);
37175fd0b74Schristos
37275fd0b74Schristos /* Iterate over all sample bins. */
37375fd0b74Schristos for (i = 0, k = 1; i < r->num_bins; ++i)
37475fd0b74Schristos {
37575fd0b74Schristos bin_count = r->sample[i];
37675fd0b74Schristos if (! bin_count)
37775fd0b74Schristos continue;
37875fd0b74Schristos
37975fd0b74Schristos bin_low_pc = lowpc + (bfd_vma) (hist_scale * i);
38075fd0b74Schristos bin_high_pc = lowpc + (bfd_vma) (hist_scale * (i + 1));
38175fd0b74Schristos count_time = bin_count;
38275fd0b74Schristos
38375fd0b74Schristos DBG (SAMPLEDEBUG,
38475fd0b74Schristos printf (
38575fd0b74Schristos "[assign_samples] bin_low_pc=0x%lx, bin_high_pc=0x%lx, bin_count=%u\n",
38675fd0b74Schristos (unsigned long) (sizeof (UNIT) * bin_low_pc),
38775fd0b74Schristos (unsigned long) (sizeof (UNIT) * bin_high_pc),
38875fd0b74Schristos bin_count));
38975fd0b74Schristos total_time += count_time;
39075fd0b74Schristos
39175fd0b74Schristos /* Credit all symbols that are covered by bin I.
39275fd0b74Schristos
39375fd0b74Schristos PR gprof/13325: Make sure that K does not get decremented
39475fd0b74Schristos and J will never be less than 0. */
39575fd0b74Schristos for (j = k - 1; j < symtab.len; k = ++j)
39675fd0b74Schristos {
39775fd0b74Schristos sym_low_pc = symtab.base[j].hist.scaled_addr;
39875fd0b74Schristos sym_high_pc = symtab.base[j + 1].hist.scaled_addr;
39975fd0b74Schristos
40075fd0b74Schristos /* If high end of bin is below entry address,
40175fd0b74Schristos go for next bin. */
40275fd0b74Schristos if (bin_high_pc < sym_low_pc)
40375fd0b74Schristos break;
40475fd0b74Schristos
40575fd0b74Schristos /* If low end of bin is above high end of symbol,
40675fd0b74Schristos go for next symbol. */
40775fd0b74Schristos if (bin_low_pc >= sym_high_pc)
40875fd0b74Schristos continue;
40975fd0b74Schristos
41075fd0b74Schristos overlap =
41175fd0b74Schristos MIN (bin_high_pc, sym_high_pc) - MAX (bin_low_pc, sym_low_pc);
41275fd0b74Schristos if (overlap > 0)
41375fd0b74Schristos {
41475fd0b74Schristos DBG (SAMPLEDEBUG,
41575fd0b74Schristos printf (
41675fd0b74Schristos "[assign_samples] [0x%lx,0x%lx) %s gets %f ticks %ld overlap\n",
41775fd0b74Schristos (unsigned long) symtab.base[j].addr,
41875fd0b74Schristos (unsigned long) (sizeof (UNIT) * sym_high_pc),
41975fd0b74Schristos symtab.base[j].name, overlap * count_time / hist_scale,
42075fd0b74Schristos (long) overlap));
42175fd0b74Schristos
42275fd0b74Schristos addr = symtab.base[j].addr;
42375fd0b74Schristos credit = overlap * count_time / hist_scale;
42475fd0b74Schristos
42575fd0b74Schristos /* Credit symbol if it appears in INCL_FLAT or that
42675fd0b74Schristos table is empty and it does not appear it in
42775fd0b74Schristos EXCL_FLAT. */
42875fd0b74Schristos if (sym_lookup (&syms[INCL_FLAT], addr)
42975fd0b74Schristos || (syms[INCL_FLAT].len == 0
43075fd0b74Schristos && !sym_lookup (&syms[EXCL_FLAT], addr)))
43175fd0b74Schristos {
43275fd0b74Schristos symtab.base[j].hist.time += credit;
43375fd0b74Schristos }
43475fd0b74Schristos else
43575fd0b74Schristos {
43675fd0b74Schristos total_time -= credit;
43775fd0b74Schristos }
43875fd0b74Schristos }
43975fd0b74Schristos }
44075fd0b74Schristos }
44175fd0b74Schristos
44275fd0b74Schristos DBG (SAMPLEDEBUG, printf ("[assign_samples] total_time %f\n",
44375fd0b74Schristos total_time));
44475fd0b74Schristos }
44575fd0b74Schristos
44675fd0b74Schristos /* Calls 'hist_assign_sampes_1' for all histogram records read so far. */
44775fd0b74Schristos void
hist_assign_samples(void)44875fd0b74Schristos hist_assign_samples (void)
44975fd0b74Schristos {
45075fd0b74Schristos unsigned i;
45175fd0b74Schristos
45275fd0b74Schristos scale_and_align_entries ();
45375fd0b74Schristos
45475fd0b74Schristos for (i = 0; i < num_histograms; ++i)
45575fd0b74Schristos hist_assign_samples_1 (&histograms[i]);
45675fd0b74Schristos
45775fd0b74Schristos }
45875fd0b74Schristos
45975fd0b74Schristos /* Print header for flag histogram profile. */
46075fd0b74Schristos
46175fd0b74Schristos static void
print_header(int prefix)46275fd0b74Schristos print_header (int prefix)
46375fd0b74Schristos {
46475fd0b74Schristos char unit[64];
46575fd0b74Schristos
46675fd0b74Schristos sprintf (unit, _("%c%c/call"), prefix, hist_dimension_abbrev);
46775fd0b74Schristos
46875fd0b74Schristos if (bsd_style_output)
46975fd0b74Schristos {
47075fd0b74Schristos printf (_("\ngranularity: each sample hit covers %ld byte(s)"),
47175fd0b74Schristos (long) hist_scale * (long) sizeof (UNIT));
47275fd0b74Schristos if (total_time > 0.0)
47375fd0b74Schristos {
47475fd0b74Schristos printf (_(" for %.2f%% of %.2f %s\n\n"),
47575fd0b74Schristos 100.0 / total_time, total_time / hz, hist_dimension);
47675fd0b74Schristos }
47775fd0b74Schristos }
47875fd0b74Schristos else
47975fd0b74Schristos {
48075fd0b74Schristos printf (_("\nEach sample counts as %g %s.\n"), 1.0 / hz, hist_dimension);
48175fd0b74Schristos }
48275fd0b74Schristos
48375fd0b74Schristos if (total_time <= 0.0)
48475fd0b74Schristos {
48575fd0b74Schristos printf (_(" no time accumulated\n\n"));
48675fd0b74Schristos
48775fd0b74Schristos /* This doesn't hurt since all the numerators will be zero. */
48875fd0b74Schristos total_time = 1.0;
48975fd0b74Schristos }
49075fd0b74Schristos
49175fd0b74Schristos printf ("%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s %-8.8s\n",
49275fd0b74Schristos "% ", _("cumulative"), _("self "), "", _("self "), _("total "),
49375fd0b74Schristos "");
49475fd0b74Schristos printf ("%5.5s %9.9s %8.8s %8.8s %8.8s %8.8s %-8.8s\n",
49575fd0b74Schristos _("time"), hist_dimension, hist_dimension, _("calls"), unit, unit,
49675fd0b74Schristos _("name"));
49775fd0b74Schristos }
49875fd0b74Schristos
49975fd0b74Schristos
50075fd0b74Schristos static void
print_line(Sym * sym,double scale)50175fd0b74Schristos print_line (Sym *sym, double scale)
50275fd0b74Schristos {
50375fd0b74Schristos if (ignore_zeros && sym->ncalls == 0 && sym->hist.time == 0)
50475fd0b74Schristos return;
50575fd0b74Schristos
50675fd0b74Schristos accum_time += sym->hist.time;
50775fd0b74Schristos
50875fd0b74Schristos if (bsd_style_output)
50975fd0b74Schristos printf ("%5.1f %10.2f %8.2f",
51075fd0b74Schristos total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
51175fd0b74Schristos accum_time / hz, sym->hist.time / hz);
51275fd0b74Schristos else
51375fd0b74Schristos printf ("%6.2f %9.2f %8.2f",
51475fd0b74Schristos total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
51575fd0b74Schristos accum_time / hz, sym->hist.time / hz);
51675fd0b74Schristos
51775fd0b74Schristos if (sym->ncalls != 0)
51875fd0b74Schristos printf (" %8lu %8.2f %8.2f ",
51975fd0b74Schristos sym->ncalls, scale * sym->hist.time / hz / sym->ncalls,
52075fd0b74Schristos scale * (sym->hist.time + sym->cg.child_time) / hz / sym->ncalls);
52175fd0b74Schristos else
52275fd0b74Schristos printf (" %8.8s %8.8s %8.8s ", "", "", "");
52375fd0b74Schristos
52475fd0b74Schristos if (bsd_style_output)
52575fd0b74Schristos print_name (sym);
52675fd0b74Schristos else
52775fd0b74Schristos print_name_only (sym);
52875fd0b74Schristos
52975fd0b74Schristos printf ("\n");
53075fd0b74Schristos }
53175fd0b74Schristos
53275fd0b74Schristos
53375fd0b74Schristos /* Compare LP and RP. The primary comparison key is execution time,
53475fd0b74Schristos the secondary is number of invocation, and the tertiary is the
53575fd0b74Schristos lexicographic order of the function names. */
53675fd0b74Schristos
53775fd0b74Schristos static int
cmp_time(const void * lp,const void * rp)538*e992f068Schristos cmp_time (const void *lp, const void *rp)
53975fd0b74Schristos {
54075fd0b74Schristos const Sym *left = *(const Sym **) lp;
54175fd0b74Schristos const Sym *right = *(const Sym **) rp;
54275fd0b74Schristos double time_diff;
54375fd0b74Schristos
54475fd0b74Schristos time_diff = right->hist.time - left->hist.time;
54575fd0b74Schristos
54675fd0b74Schristos if (time_diff > 0.0)
54775fd0b74Schristos return 1;
54875fd0b74Schristos
54975fd0b74Schristos if (time_diff < 0.0)
55075fd0b74Schristos return -1;
55175fd0b74Schristos
55275fd0b74Schristos if (right->ncalls > left->ncalls)
55375fd0b74Schristos return 1;
55475fd0b74Schristos
55575fd0b74Schristos if (right->ncalls < left->ncalls)
55675fd0b74Schristos return -1;
55775fd0b74Schristos
55875fd0b74Schristos return strcmp (left->name, right->name);
55975fd0b74Schristos }
56075fd0b74Schristos
56175fd0b74Schristos
56275fd0b74Schristos /* Print the flat histogram profile. */
56375fd0b74Schristos
56475fd0b74Schristos void
hist_print(void)56575fd0b74Schristos hist_print (void)
56675fd0b74Schristos {
56775fd0b74Schristos Sym **time_sorted_syms, *top_dog, *sym;
56875fd0b74Schristos unsigned int sym_index;
56975fd0b74Schristos unsigned log_scale;
57075fd0b74Schristos double top_time;
57175fd0b74Schristos bfd_vma addr;
57275fd0b74Schristos
57375fd0b74Schristos if (first_output)
574*e992f068Schristos first_output = false;
57575fd0b74Schristos else
57675fd0b74Schristos printf ("\f\n");
57775fd0b74Schristos
57875fd0b74Schristos accum_time = 0.0;
57975fd0b74Schristos
58075fd0b74Schristos if (bsd_style_output)
58175fd0b74Schristos {
58275fd0b74Schristos if (print_descriptions)
58375fd0b74Schristos {
58475fd0b74Schristos printf (_("\n\n\nflat profile:\n"));
58575fd0b74Schristos flat_blurb (stdout);
58675fd0b74Schristos }
58775fd0b74Schristos }
58875fd0b74Schristos else
58975fd0b74Schristos {
59075fd0b74Schristos printf (_("Flat profile:\n"));
59175fd0b74Schristos }
59275fd0b74Schristos
59375fd0b74Schristos /* Sort the symbol table by time (call-count and name as secondary
59475fd0b74Schristos and tertiary keys). */
59575fd0b74Schristos time_sorted_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *));
59675fd0b74Schristos
59775fd0b74Schristos for (sym_index = 0; sym_index < symtab.len; ++sym_index)
59875fd0b74Schristos time_sorted_syms[sym_index] = &symtab.base[sym_index];
59975fd0b74Schristos
60075fd0b74Schristos qsort (time_sorted_syms, symtab.len, sizeof (Sym *), cmp_time);
60175fd0b74Schristos
60275fd0b74Schristos if (bsd_style_output)
60375fd0b74Schristos {
60475fd0b74Schristos log_scale = 5; /* Milli-seconds is BSD-default. */
60575fd0b74Schristos }
60675fd0b74Schristos else
60775fd0b74Schristos {
60875fd0b74Schristos /* Search for symbol with highest per-call
60975fd0b74Schristos execution time and scale accordingly. */
61075fd0b74Schristos log_scale = 0;
61175fd0b74Schristos top_dog = 0;
61275fd0b74Schristos top_time = 0.0;
61375fd0b74Schristos
61475fd0b74Schristos for (sym_index = 0; sym_index < symtab.len; ++sym_index)
61575fd0b74Schristos {
61675fd0b74Schristos sym = time_sorted_syms[sym_index];
61775fd0b74Schristos
61875fd0b74Schristos if (sym->ncalls != 0)
61975fd0b74Schristos {
62075fd0b74Schristos double call_time;
62175fd0b74Schristos
62275fd0b74Schristos call_time = (sym->hist.time + sym->cg.child_time) / sym->ncalls;
62375fd0b74Schristos
62475fd0b74Schristos if (call_time > top_time)
62575fd0b74Schristos {
62675fd0b74Schristos top_dog = sym;
62775fd0b74Schristos top_time = call_time;
62875fd0b74Schristos }
62975fd0b74Schristos }
63075fd0b74Schristos }
63175fd0b74Schristos
63275fd0b74Schristos if (top_dog && top_dog->ncalls != 0 && top_time > 0.0)
63375fd0b74Schristos {
63475fd0b74Schristos top_time /= hz;
63575fd0b74Schristos
63675fd0b74Schristos for (log_scale = 0; log_scale < ARRAY_SIZE (SItab); log_scale ++)
63775fd0b74Schristos {
63875fd0b74Schristos double scaled_value = SItab[log_scale].scale * top_time;
63975fd0b74Schristos
64075fd0b74Schristos if (scaled_value >= 1.0 && scaled_value < 1000.0)
64175fd0b74Schristos break;
64275fd0b74Schristos }
64375fd0b74Schristos }
64475fd0b74Schristos }
64575fd0b74Schristos
64675fd0b74Schristos /* For now, the dimension is always seconds. In the future, we
64775fd0b74Schristos may also want to support other (pseudo-)dimensions (such as
64875fd0b74Schristos I-cache misses etc.). */
64975fd0b74Schristos print_header (SItab[log_scale].prefix);
65075fd0b74Schristos
65175fd0b74Schristos for (sym_index = 0; sym_index < symtab.len; ++sym_index)
65275fd0b74Schristos {
65375fd0b74Schristos addr = time_sorted_syms[sym_index]->addr;
65475fd0b74Schristos
65575fd0b74Schristos /* Print symbol if its in INCL_FLAT table or that table
65675fd0b74Schristos is empty and the symbol is not in EXCL_FLAT. */
65775fd0b74Schristos if (sym_lookup (&syms[INCL_FLAT], addr)
65875fd0b74Schristos || (syms[INCL_FLAT].len == 0
65975fd0b74Schristos && !sym_lookup (&syms[EXCL_FLAT], addr)))
66075fd0b74Schristos print_line (time_sorted_syms[sym_index], SItab[log_scale].scale);
66175fd0b74Schristos }
66275fd0b74Schristos
66375fd0b74Schristos free (time_sorted_syms);
66475fd0b74Schristos
66575fd0b74Schristos if (print_descriptions && !bsd_style_output)
66675fd0b74Schristos flat_blurb (stdout);
66775fd0b74Schristos }
66875fd0b74Schristos
66975fd0b74Schristos int
hist_check_address(unsigned address)67075fd0b74Schristos hist_check_address (unsigned address)
67175fd0b74Schristos {
67275fd0b74Schristos unsigned i;
67375fd0b74Schristos
67475fd0b74Schristos for (i = 0; i < num_histograms; ++i)
67575fd0b74Schristos if (histograms[i].lowpc <= address && address < histograms[i].highpc)
67675fd0b74Schristos return 1;
67775fd0b74Schristos
67875fd0b74Schristos return 0;
67975fd0b74Schristos }
68075fd0b74Schristos
68175fd0b74Schristos #if ! defined(min)
68275fd0b74Schristos #define min(a,b) (((a)<(b)) ? (a) : (b))
68375fd0b74Schristos #endif
68475fd0b74Schristos #if ! defined(max)
68575fd0b74Schristos #define max(a,b) (((a)>(b)) ? (a) : (b))
68675fd0b74Schristos #endif
68775fd0b74Schristos
68875fd0b74Schristos void
hist_clip_symbol_address(bfd_vma * p_lowpc,bfd_vma * p_highpc)68975fd0b74Schristos hist_clip_symbol_address (bfd_vma *p_lowpc, bfd_vma *p_highpc)
69075fd0b74Schristos {
69175fd0b74Schristos unsigned i;
69275fd0b74Schristos int found = 0;
69375fd0b74Schristos
69475fd0b74Schristos if (num_histograms == 0)
69575fd0b74Schristos {
69675fd0b74Schristos *p_highpc = *p_lowpc;
69775fd0b74Schristos return;
69875fd0b74Schristos }
69975fd0b74Schristos
70075fd0b74Schristos for (i = 0; i < num_histograms; ++i)
70175fd0b74Schristos {
70275fd0b74Schristos bfd_vma common_low, common_high;
70375fd0b74Schristos common_low = max (histograms[i].lowpc, *p_lowpc);
70475fd0b74Schristos common_high = min (histograms[i].highpc, *p_highpc);
70575fd0b74Schristos
70675fd0b74Schristos if (common_low < common_high)
70775fd0b74Schristos {
70875fd0b74Schristos if (found)
70975fd0b74Schristos {
71075fd0b74Schristos fprintf (stderr,
71175fd0b74Schristos _("%s: found a symbol that covers "
71275fd0b74Schristos "several histogram records"),
71375fd0b74Schristos whoami);
71475fd0b74Schristos done (1);
71575fd0b74Schristos }
71675fd0b74Schristos
71775fd0b74Schristos found = 1;
71875fd0b74Schristos *p_lowpc = common_low;
71975fd0b74Schristos *p_highpc = common_high;
72075fd0b74Schristos }
72175fd0b74Schristos }
72275fd0b74Schristos
72375fd0b74Schristos if (!found)
72475fd0b74Schristos *p_highpc = *p_lowpc;
72575fd0b74Schristos }
72675fd0b74Schristos
72775fd0b74Schristos /* Find and return exising histogram record having the same lowpc and
72875fd0b74Schristos highpc as passed via the parameters. Return NULL if nothing is found.
72975fd0b74Schristos The return value is valid until any new histogram is read. */
73075fd0b74Schristos static histogram *
find_histogram(bfd_vma lowpc,bfd_vma highpc)73175fd0b74Schristos find_histogram (bfd_vma lowpc, bfd_vma highpc)
73275fd0b74Schristos {
73375fd0b74Schristos unsigned i;
73475fd0b74Schristos for (i = 0; i < num_histograms; ++i)
73575fd0b74Schristos {
73675fd0b74Schristos if (histograms[i].lowpc == lowpc && histograms[i].highpc == highpc)
73775fd0b74Schristos return &histograms[i];
73875fd0b74Schristos }
73975fd0b74Schristos return 0;
74075fd0b74Schristos }
74175fd0b74Schristos
74275fd0b74Schristos /* Given a PC, return histogram record which address range include this PC.
74375fd0b74Schristos Return NULL if there's no such record. */
74475fd0b74Schristos static histogram *
find_histogram_for_pc(bfd_vma pc)74575fd0b74Schristos find_histogram_for_pc (bfd_vma pc)
74675fd0b74Schristos {
74775fd0b74Schristos unsigned i;
74875fd0b74Schristos for (i = 0; i < num_histograms; ++i)
74975fd0b74Schristos {
75075fd0b74Schristos if (histograms[i].lowpc <= pc && pc < histograms[i].highpc)
75175fd0b74Schristos return &histograms[i];
75275fd0b74Schristos }
75375fd0b74Schristos return 0;
75475fd0b74Schristos }
755