1b55d4692Sfgsch /* hist.c - Histogram related operations.
2b55d4692Sfgsch
3*c074d1c9Sdrahn Copyright 2000, 2001, 2002 Free Software Foundation, Inc.
4b55d4692Sfgsch
5b55d4692Sfgsch This file is part of GNU Binutils.
6b55d4692Sfgsch
7b55d4692Sfgsch This program is free software; you can redistribute it and/or modify
8b55d4692Sfgsch it under the terms of the GNU General Public License as published by
9b55d4692Sfgsch the Free Software Foundation; either version 2 of the License, or
10b55d4692Sfgsch (at your option) any later version.
11b55d4692Sfgsch
12b55d4692Sfgsch This program is distributed in the hope that it will be useful,
13b55d4692Sfgsch but WITHOUT ANY WARRANTY; without even the implied warranty of
14b55d4692Sfgsch MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15b55d4692Sfgsch GNU General Public License for more details.
16b55d4692Sfgsch
17b55d4692Sfgsch You should have received a copy of the GNU General Public License
18b55d4692Sfgsch along with this program; if not, write to the Free Software
19b55d4692Sfgsch Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20b55d4692Sfgsch 02111-1307, USA. */
21b55d4692Sfgsch
222159047fSniklas #include "libiberty.h"
232159047fSniklas #include "gprof.h"
24*c074d1c9Sdrahn #include "search_list.h"
25*c074d1c9Sdrahn #include "source.h"
26*c074d1c9Sdrahn #include "symtab.h"
27b305b0f1Sespie #include "corefile.h"
282159047fSniklas #include "gmon_io.h"
292159047fSniklas #include "gmon_out.h"
302159047fSniklas #include "hist.h"
312159047fSniklas #include "sym_ids.h"
322159047fSniklas #include "utils.h"
332159047fSniklas
34b305b0f1Sespie #define UNITS_TO_CODE (offset_to_code / sizeof(UNIT))
35b305b0f1Sespie
36b305b0f1Sespie static void scale_and_align_entries PARAMS ((void));
37*c074d1c9Sdrahn static void print_header PARAMS ((int));
38*c074d1c9Sdrahn static void print_line PARAMS ((Sym *, double));
39*c074d1c9Sdrahn static int cmp_time PARAMS ((const PTR, const PTR));
40b305b0f1Sespie
41b55d4692Sfgsch /* Declarations of automatically generated functions to output blurbs. */
422159047fSniklas extern void flat_blurb PARAMS ((FILE * fp));
432159047fSniklas
44b55d4692Sfgsch bfd_vma s_lowpc; /* Lowest address in .text. */
45b55d4692Sfgsch bfd_vma s_highpc = 0; /* Highest address in .text. */
46b55d4692Sfgsch bfd_vma lowpc, highpc; /* Same, but expressed in UNITs. */
47b55d4692Sfgsch int hist_num_bins = 0; /* Number of histogram samples. */
48b55d4692Sfgsch int *hist_sample = 0; /* Histogram samples (shorts in the file!). */
492159047fSniklas double hist_scale;
50b55d4692Sfgsch char hist_dimension[16] = "seconds";
512159047fSniklas char hist_dimension_abbrev = 's';
522159047fSniklas
53b55d4692Sfgsch static double accum_time; /* Accumulated time so far for print_line(). */
54b55d4692Sfgsch static double total_time; /* Total time for all routines. */
55b55d4692Sfgsch
56b55d4692Sfgsch /* Table of SI prefixes for powers of 10 (used to automatically
57b55d4692Sfgsch scale some of the values in the flat profile). */
582159047fSniklas const struct
592159047fSniklas {
602159047fSniklas char prefix;
612159047fSniklas double scale;
622159047fSniklas }
632159047fSniklas SItab[] =
642159047fSniklas {
65b55d4692Sfgsch { 'T', 1e-12 }, /* tera */
66b55d4692Sfgsch { 'G', 1e-09 }, /* giga */
67b55d4692Sfgsch { 'M', 1e-06 }, /* mega */
68b55d4692Sfgsch { 'K', 1e-03 }, /* kilo */
69b55d4692Sfgsch { ' ', 1e-00 },
70b55d4692Sfgsch { 'm', 1e+03 }, /* milli */
71b55d4692Sfgsch { 'u', 1e+06 }, /* micro */
72b55d4692Sfgsch { 'n', 1e+09 }, /* nano */
73b55d4692Sfgsch { 'p', 1e+12 }, /* pico */
74b55d4692Sfgsch { 'f', 1e+15 }, /* femto */
75b55d4692Sfgsch { 'a', 1e+18 } /* ato */
762159047fSniklas };
772159047fSniklas
78b55d4692Sfgsch
79b55d4692Sfgsch /* Read the histogram from file IFP. FILENAME is the name of IFP and
80b55d4692Sfgsch is provided for formatting error messages only. */
81b55d4692Sfgsch
822159047fSniklas void
hist_read_rec(ifp,filename)83*c074d1c9Sdrahn hist_read_rec (ifp, filename)
84*c074d1c9Sdrahn FILE * ifp;
85*c074d1c9Sdrahn const char *filename;
862159047fSniklas {
872159047fSniklas bfd_vma n_lowpc, n_highpc;
882159047fSniklas int i, ncnt, profrate;
892159047fSniklas UNIT count;
902159047fSniklas
91b55d4692Sfgsch if (gmon_io_read_vma (ifp, &n_lowpc)
92b55d4692Sfgsch || gmon_io_read_vma (ifp, &n_highpc)
93b55d4692Sfgsch || gmon_io_read_32 (ifp, &ncnt)
94b55d4692Sfgsch || gmon_io_read_32 (ifp, &profrate)
95b55d4692Sfgsch || gmon_io_read (ifp, hist_dimension, 15)
96b55d4692Sfgsch || gmon_io_read (ifp, &hist_dimension_abbrev, 1))
972159047fSniklas {
98b305b0f1Sespie fprintf (stderr, _("%s: %s: unexpected end of file\n"),
992159047fSniklas whoami, filename);
100b55d4692Sfgsch
1012159047fSniklas done (1);
1022159047fSniklas }
1032159047fSniklas
1042159047fSniklas if (!s_highpc)
1052159047fSniklas {
106b55d4692Sfgsch /* This is the first histogram record. */
1072159047fSniklas s_lowpc = n_lowpc;
1082159047fSniklas s_highpc = n_highpc;
1092159047fSniklas lowpc = (bfd_vma) n_lowpc / sizeof (UNIT);
1102159047fSniklas highpc = (bfd_vma) n_highpc / sizeof (UNIT);
1112159047fSniklas hist_num_bins = ncnt;
1122159047fSniklas hz = profrate;
1132159047fSniklas }
1142159047fSniklas
1152159047fSniklas DBG (SAMPLEDEBUG,
1162159047fSniklas printf ("[hist_read_rec] n_lowpc 0x%lx n_highpc 0x%lx ncnt %d\n",
117b305b0f1Sespie (unsigned long) n_lowpc, (unsigned long) n_highpc, ncnt);
1182159047fSniklas printf ("[hist_read_rec] s_lowpc 0x%lx s_highpc 0x%lx nsamples %d\n",
119b305b0f1Sespie (unsigned long) s_lowpc, (unsigned long) s_highpc,
120b305b0f1Sespie hist_num_bins);
1212159047fSniklas printf ("[hist_read_rec] lowpc 0x%lx highpc 0x%lx\n",
122b305b0f1Sespie (unsigned long) lowpc, (unsigned long) highpc));
1232159047fSniklas
1242159047fSniklas if (n_lowpc != s_lowpc || n_highpc != s_highpc
1252159047fSniklas || ncnt != hist_num_bins || hz != profrate)
1262159047fSniklas {
127b305b0f1Sespie fprintf (stderr, _("%s: `%s' is incompatible with first gmon file\n"),
1282159047fSniklas whoami, filename);
1292159047fSniklas done (1);
1302159047fSniklas }
1312159047fSniklas
1322159047fSniklas if (!hist_sample)
1332159047fSniklas {
1342159047fSniklas hist_sample = (int *) xmalloc (hist_num_bins * sizeof (hist_sample[0]));
1352159047fSniklas memset (hist_sample, 0, hist_num_bins * sizeof (hist_sample[0]));
1362159047fSniklas }
1372159047fSniklas
1382159047fSniklas for (i = 0; i < hist_num_bins; ++i)
1392159047fSniklas {
1402159047fSniklas if (fread (&count[0], sizeof (count), 1, ifp) != 1)
1412159047fSniklas {
1422159047fSniklas fprintf (stderr,
143b305b0f1Sespie _("%s: %s: unexpected EOF after reading %d of %d samples\n"),
1442159047fSniklas whoami, filename, i, hist_num_bins);
1452159047fSniklas done (1);
1462159047fSniklas }
1472159047fSniklas hist_sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) & count[0]);
148b55d4692Sfgsch DBG (SAMPLEDEBUG,
149b55d4692Sfgsch printf ("[hist_read_rec] 0x%lx: %u\n",
150b55d4692Sfgsch (unsigned long) (n_lowpc + i * (n_highpc - n_lowpc) / ncnt),
151b55d4692Sfgsch hist_sample[i]));
1522159047fSniklas }
1532159047fSniklas }
1542159047fSniklas
1552159047fSniklas
156b55d4692Sfgsch /* Write execution histogram to file OFP. FILENAME is the name
157b55d4692Sfgsch of OFP and is provided for formatting error-messages only. */
158b55d4692Sfgsch
1592159047fSniklas void
hist_write_hist(ofp,filename)160*c074d1c9Sdrahn hist_write_hist (ofp, filename)
161*c074d1c9Sdrahn FILE * ofp;
162*c074d1c9Sdrahn const char *filename;
1632159047fSniklas {
1642159047fSniklas UNIT count;
1652159047fSniklas int i;
1662159047fSniklas
167b55d4692Sfgsch /* Write header. */
1682159047fSniklas
169b55d4692Sfgsch if (gmon_io_write_8 (ofp, GMON_TAG_TIME_HIST)
170b55d4692Sfgsch || gmon_io_write_vma (ofp, s_lowpc)
171b55d4692Sfgsch || gmon_io_write_vma (ofp, s_highpc)
172b55d4692Sfgsch || gmon_io_write_32 (ofp, hist_num_bins)
173b55d4692Sfgsch || gmon_io_write_32 (ofp, hz)
174b55d4692Sfgsch || gmon_io_write (ofp, hist_dimension, 15)
175b55d4692Sfgsch || gmon_io_write (ofp, &hist_dimension_abbrev, 1))
1762159047fSniklas {
1772159047fSniklas perror (filename);
1782159047fSniklas done (1);
1792159047fSniklas }
1802159047fSniklas
1812159047fSniklas for (i = 0; i < hist_num_bins; ++i)
1822159047fSniklas {
183*c074d1c9Sdrahn bfd_put_16 (core_bfd, (bfd_vma) hist_sample[i], (bfd_byte *) &count[0]);
184b55d4692Sfgsch
1852159047fSniklas if (fwrite (&count[0], sizeof (count), 1, ofp) != 1)
1862159047fSniklas {
1872159047fSniklas perror (filename);
1882159047fSniklas done (1);
1892159047fSniklas }
1902159047fSniklas }
1912159047fSniklas }
1922159047fSniklas
1932159047fSniklas
194b55d4692Sfgsch /* Calculate scaled entry point addresses (to save time in
195b55d4692Sfgsch hist_assign_samples), and, on architectures that have procedure
196b55d4692Sfgsch entry masks at the start of a function, possibly push the scaled
197b55d4692Sfgsch entry points over the procedure entry mask, if it turns out that
198b55d4692Sfgsch the entry point is in one bin and the code for a routine is in the
199b55d4692Sfgsch next bin. */
200b55d4692Sfgsch
2012159047fSniklas static void
scale_and_align_entries()202191aa565Sniklas scale_and_align_entries ()
2032159047fSniklas {
2042159047fSniklas Sym *sym;
2052159047fSniklas bfd_vma bin_of_entry;
2062159047fSniklas bfd_vma bin_of_code;
2072159047fSniklas
2082159047fSniklas for (sym = symtab.base; sym < symtab.limit; sym++)
2092159047fSniklas {
2102159047fSniklas sym->hist.scaled_addr = sym->addr / sizeof (UNIT);
2112159047fSniklas bin_of_entry = (sym->hist.scaled_addr - lowpc) / hist_scale;
212b55d4692Sfgsch bin_of_code = ((sym->hist.scaled_addr + UNITS_TO_CODE - lowpc)
213b55d4692Sfgsch / hist_scale);
2142159047fSniklas if (bin_of_entry < bin_of_code)
2152159047fSniklas {
2162159047fSniklas DBG (SAMPLEDEBUG,
2172159047fSniklas printf ("[scale_and_align_entries] pushing 0x%lx to 0x%lx\n",
218b305b0f1Sespie (unsigned long) sym->hist.scaled_addr,
219b305b0f1Sespie (unsigned long) (sym->hist.scaled_addr
220b305b0f1Sespie + UNITS_TO_CODE)));
221191aa565Sniklas sym->hist.scaled_addr += UNITS_TO_CODE;
2222159047fSniklas }
2232159047fSniklas }
2242159047fSniklas }
2252159047fSniklas
2262159047fSniklas
227b55d4692Sfgsch /* Assign samples to the symbol to which they belong.
228b55d4692Sfgsch
229b55d4692Sfgsch Histogram bin I covers some address range [BIN_LOWPC,BIN_HIGH_PC)
230b55d4692Sfgsch which may overlap one more symbol address ranges. If a symbol
231b55d4692Sfgsch overlaps with the bin's address range by O percent, then O percent
232b55d4692Sfgsch of the bin's count is credited to that symbol.
233b55d4692Sfgsch
234b55d4692Sfgsch There are three cases as to where BIN_LOW_PC and BIN_HIGH_PC can be
235b55d4692Sfgsch with respect to the symbol's address range [SYM_LOW_PC,
236b55d4692Sfgsch SYM_HIGH_PC) as shown in the following diagram. OVERLAP computes
237b55d4692Sfgsch the distance (in UNITs) between the arrows, the fraction of the
238b55d4692Sfgsch sample that is to be credited to the symbol which starts at
239b55d4692Sfgsch SYM_LOW_PC.
240b55d4692Sfgsch
241b55d4692Sfgsch sym_low_pc sym_high_pc
242b55d4692Sfgsch | |
243b55d4692Sfgsch v v
244b55d4692Sfgsch
245b55d4692Sfgsch +-----------------------------------------------+
246b55d4692Sfgsch | |
247b55d4692Sfgsch | ->| |<- ->| |<- ->| |<- |
248b55d4692Sfgsch | | | | | |
249b55d4692Sfgsch +---------+ +---------+ +---------+
250b55d4692Sfgsch
251b55d4692Sfgsch ^ ^ ^ ^ ^ ^
252b55d4692Sfgsch | | | | | |
253b55d4692Sfgsch bin_low_pc bin_high_pc bin_low_pc bin_high_pc bin_low_pc bin_high_pc
254b55d4692Sfgsch
255b55d4692Sfgsch For the VAX we assert that samples will never fall in the first two
256b55d4692Sfgsch bytes of any routine, since that is the entry mask, thus we call
257b55d4692Sfgsch scale_and_align_entries() to adjust the entry points if the entry
258b55d4692Sfgsch mask falls in one bin but the code for the routine doesn't start
259b55d4692Sfgsch until the next bin. In conjunction with the alignment of routine
260b55d4692Sfgsch addresses, this should allow us to have only one sample for every
261b55d4692Sfgsch four bytes of text space and never have any overlap (the two end
262b55d4692Sfgsch cases, above). */
263b55d4692Sfgsch
2642159047fSniklas void
hist_assign_samples()265*c074d1c9Sdrahn hist_assign_samples ()
2662159047fSniklas {
2672159047fSniklas bfd_vma bin_low_pc, bin_high_pc;
2682159047fSniklas bfd_vma sym_low_pc, sym_high_pc;
2692159047fSniklas bfd_vma overlap, addr;
270b305b0f1Sespie int bin_count, i;
271b305b0f1Sespie unsigned int j;
2722159047fSniklas double time, credit;
2732159047fSniklas
274b55d4692Sfgsch /* Read samples and assign to symbols. */
2752159047fSniklas hist_scale = highpc - lowpc;
2762159047fSniklas hist_scale /= hist_num_bins;
2772159047fSniklas scale_and_align_entries ();
2782159047fSniklas
279b55d4692Sfgsch /* Iterate over all sample bins. */
2802159047fSniklas for (i = 0, j = 1; i < hist_num_bins; ++i)
2812159047fSniklas {
2822159047fSniklas bin_count = hist_sample[i];
2832159047fSniklas if (! bin_count)
2842159047fSniklas continue;
285b55d4692Sfgsch
2862159047fSniklas bin_low_pc = lowpc + (bfd_vma) (hist_scale * i);
2872159047fSniklas bin_high_pc = lowpc + (bfd_vma) (hist_scale * (i + 1));
2882159047fSniklas time = bin_count;
289b55d4692Sfgsch
2902159047fSniklas DBG (SAMPLEDEBUG,
2912159047fSniklas printf (
2922159047fSniklas "[assign_samples] bin_low_pc=0x%lx, bin_high_pc=0x%lx, bin_count=%d\n",
293b305b0f1Sespie (unsigned long) (sizeof (UNIT) * bin_low_pc),
294b305b0f1Sespie (unsigned long) (sizeof (UNIT) * bin_high_pc),
2952159047fSniklas bin_count));
2962159047fSniklas total_time += time;
2972159047fSniklas
298b55d4692Sfgsch /* Credit all symbols that are covered by bin I. */
2992159047fSniklas for (j = j - 1; j < symtab.len; ++j)
3002159047fSniklas {
3012159047fSniklas sym_low_pc = symtab.base[j].hist.scaled_addr;
3022159047fSniklas sym_high_pc = symtab.base[j + 1].hist.scaled_addr;
303b55d4692Sfgsch
304b55d4692Sfgsch /* If high end of bin is below entry address,
305b55d4692Sfgsch go for next bin. */
3062159047fSniklas if (bin_high_pc < sym_low_pc)
3072159047fSniklas break;
308b55d4692Sfgsch
309b55d4692Sfgsch /* If low end of bin is above high end of symbol,
310b55d4692Sfgsch go for next symbol. */
3112159047fSniklas if (bin_low_pc >= sym_high_pc)
3122159047fSniklas continue;
313b55d4692Sfgsch
3142159047fSniklas overlap =
3152159047fSniklas MIN (bin_high_pc, sym_high_pc) - MAX (bin_low_pc, sym_low_pc);
3162159047fSniklas if (overlap > 0)
3172159047fSniklas {
3182159047fSniklas DBG (SAMPLEDEBUG,
3192159047fSniklas printf (
3202159047fSniklas "[assign_samples] [0x%lx,0x%lx) %s gets %f ticks %ld overlap\n",
321b305b0f1Sespie (unsigned long) symtab.base[j].addr,
322b305b0f1Sespie (unsigned long) (sizeof (UNIT) * sym_high_pc),
3232159047fSniklas symtab.base[j].name, overlap * time / hist_scale,
324b305b0f1Sespie (long) overlap));
325b55d4692Sfgsch
3262159047fSniklas addr = symtab.base[j].addr;
3272159047fSniklas credit = overlap * time / hist_scale;
328b55d4692Sfgsch
329b55d4692Sfgsch /* Credit symbol if it appears in INCL_FLAT or that
330b55d4692Sfgsch table is empty and it does not appear it in
331b55d4692Sfgsch EXCL_FLAT. */
3322159047fSniklas if (sym_lookup (&syms[INCL_FLAT], addr)
3332159047fSniklas || (syms[INCL_FLAT].len == 0
3342159047fSniklas && !sym_lookup (&syms[EXCL_FLAT], addr)))
3352159047fSniklas {
3362159047fSniklas symtab.base[j].hist.time += credit;
3372159047fSniklas }
3382159047fSniklas else
3392159047fSniklas {
3402159047fSniklas total_time -= credit;
3412159047fSniklas }
3422159047fSniklas }
3432159047fSniklas }
3442159047fSniklas }
345b55d4692Sfgsch
3462159047fSniklas DBG (SAMPLEDEBUG, printf ("[assign_samples] total_time %f\n",
3472159047fSniklas total_time));
3482159047fSniklas }
3492159047fSniklas
3502159047fSniklas
351b55d4692Sfgsch /* Print header for flag histogram profile. */
352b55d4692Sfgsch
3532159047fSniklas static void
print_header(prefix)354*c074d1c9Sdrahn print_header (prefix)
355*c074d1c9Sdrahn int prefix;
3562159047fSniklas {
3572159047fSniklas char unit[64];
3582159047fSniklas
359b305b0f1Sespie sprintf (unit, _("%c%c/call"), prefix, hist_dimension_abbrev);
3602159047fSniklas
3612159047fSniklas if (bsd_style_output)
3622159047fSniklas {
363b305b0f1Sespie printf (_("\ngranularity: each sample hit covers %ld byte(s)"),
3642159047fSniklas (long) hist_scale * sizeof (UNIT));
3652159047fSniklas if (total_time > 0.0)
3662159047fSniklas {
367b305b0f1Sespie printf (_(" for %.2f%% of %.2f %s\n\n"),
3682159047fSniklas 100.0 / total_time, total_time / hz, hist_dimension);
3692159047fSniklas }
3702159047fSniklas }
3712159047fSniklas else
3722159047fSniklas {
373b305b0f1Sespie printf (_("\nEach sample counts as %g %s.\n"), 1.0 / hz, hist_dimension);
3742159047fSniklas }
3752159047fSniklas
3762159047fSniklas if (total_time <= 0.0)
3772159047fSniklas {
378b305b0f1Sespie printf (_(" no time accumulated\n\n"));
379b55d4692Sfgsch
380b55d4692Sfgsch /* This doesn't hurt since all the numerators will be zero. */
3812159047fSniklas total_time = 1.0;
3822159047fSniklas }
3832159047fSniklas
3842159047fSniklas printf ("%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s %-8.8s\n",
385b55d4692Sfgsch "% ", _("cumulative"), _("self "), "", _("self "), _("total "),
386b55d4692Sfgsch "");
3872159047fSniklas printf ("%5.5s %9.9s %8.8s %8.8s %8.8s %8.8s %-8.8s\n",
388b305b0f1Sespie _("time"), hist_dimension, hist_dimension, _("calls"), unit, unit,
389b305b0f1Sespie _("name"));
3902159047fSniklas }
3912159047fSniklas
3922159047fSniklas
3932159047fSniklas static void
print_line(sym,scale)394*c074d1c9Sdrahn print_line (sym, scale)
395*c074d1c9Sdrahn Sym *sym;
396*c074d1c9Sdrahn double scale;
3972159047fSniklas {
3982159047fSniklas if (ignore_zeros && sym->ncalls == 0 && sym->hist.time == 0)
3992159047fSniklas return;
4002159047fSniklas
4012159047fSniklas accum_time += sym->hist.time;
402b55d4692Sfgsch
4032159047fSniklas if (bsd_style_output)
4042159047fSniklas printf ("%5.1f %10.2f %8.2f",
4052159047fSniklas total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
4062159047fSniklas accum_time / hz, sym->hist.time / hz);
4072159047fSniklas else
4082159047fSniklas printf ("%6.2f %9.2f %8.2f",
4092159047fSniklas total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
4102159047fSniklas accum_time / hz, sym->hist.time / hz);
411b55d4692Sfgsch
412b305b0f1Sespie if (sym->ncalls != 0)
413b305b0f1Sespie printf (" %8lu %8.2f %8.2f ",
4142159047fSniklas sym->ncalls, scale * sym->hist.time / hz / sym->ncalls,
4152159047fSniklas scale * (sym->hist.time + sym->cg.child_time) / hz / sym->ncalls);
4162159047fSniklas else
4172159047fSniklas printf (" %8.8s %8.8s %8.8s ", "", "", "");
418b55d4692Sfgsch
4192159047fSniklas if (bsd_style_output)
4202159047fSniklas print_name (sym);
4212159047fSniklas else
4222159047fSniklas print_name_only (sym);
423b55d4692Sfgsch
4242159047fSniklas printf ("\n");
4252159047fSniklas }
4262159047fSniklas
4272159047fSniklas
428b55d4692Sfgsch /* Compare LP and RP. The primary comparison key is execution time,
429b55d4692Sfgsch the secondary is number of invocation, and the tertiary is the
430b55d4692Sfgsch lexicographic order of the function names. */
431b55d4692Sfgsch
4322159047fSniklas static int
cmp_time(lp,rp)433*c074d1c9Sdrahn cmp_time (lp, rp)
434*c074d1c9Sdrahn const PTR lp;
435*c074d1c9Sdrahn const PTR rp;
4362159047fSniklas {
4372159047fSniklas const Sym *left = *(const Sym **) lp;
4382159047fSniklas const Sym *right = *(const Sym **) rp;
4392159047fSniklas double time_diff;
4402159047fSniklas
4412159047fSniklas time_diff = right->hist.time - left->hist.time;
442b55d4692Sfgsch
4432159047fSniklas if (time_diff > 0.0)
4442159047fSniklas return 1;
445b55d4692Sfgsch
4462159047fSniklas if (time_diff < 0.0)
4472159047fSniklas return -1;
4482159047fSniklas
449b305b0f1Sespie if (right->ncalls > left->ncalls)
4502159047fSniklas return 1;
451b55d4692Sfgsch
452b305b0f1Sespie if (right->ncalls < left->ncalls)
4532159047fSniklas return -1;
4542159047fSniklas
4552159047fSniklas return strcmp (left->name, right->name);
4562159047fSniklas }
4572159047fSniklas
4582159047fSniklas
459b55d4692Sfgsch /* Print the flat histogram profile. */
460b55d4692Sfgsch
4612159047fSniklas void
hist_print()462*c074d1c9Sdrahn hist_print ()
4632159047fSniklas {
4642159047fSniklas Sym **time_sorted_syms, *top_dog, *sym;
465b305b0f1Sespie unsigned int index;
466*c074d1c9Sdrahn unsigned log_scale;
4672159047fSniklas double top_time, time;
4682159047fSniklas bfd_vma addr;
4692159047fSniklas
4702159047fSniklas if (first_output)
4712159047fSniklas first_output = FALSE;
4722159047fSniklas else
4732159047fSniklas printf ("\f\n");
4742159047fSniklas
4752159047fSniklas accum_time = 0.0;
476b55d4692Sfgsch
4772159047fSniklas if (bsd_style_output)
4782159047fSniklas {
4792159047fSniklas if (print_descriptions)
4802159047fSniklas {
481b305b0f1Sespie printf (_("\n\n\nflat profile:\n"));
4822159047fSniklas flat_blurb (stdout);
4832159047fSniklas }
4842159047fSniklas }
4852159047fSniklas else
4862159047fSniklas {
487b305b0f1Sespie printf (_("Flat profile:\n"));
4882159047fSniklas }
489b55d4692Sfgsch
490b55d4692Sfgsch /* Sort the symbol table by time (call-count and name as secondary
491b55d4692Sfgsch and tertiary keys). */
4922159047fSniklas time_sorted_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *));
493b55d4692Sfgsch
4942159047fSniklas for (index = 0; index < symtab.len; ++index)
4952159047fSniklas time_sorted_syms[index] = &symtab.base[index];
496b55d4692Sfgsch
4972159047fSniklas qsort (time_sorted_syms, symtab.len, sizeof (Sym *), cmp_time);
4982159047fSniklas
4992159047fSniklas if (bsd_style_output)
5002159047fSniklas {
501b55d4692Sfgsch log_scale = 5; /* Milli-seconds is BSD-default. */
5022159047fSniklas }
5032159047fSniklas else
5042159047fSniklas {
505b55d4692Sfgsch /* Search for symbol with highest per-call
506b55d4692Sfgsch execution time and scale accordingly. */
5072159047fSniklas log_scale = 0;
5082159047fSniklas top_dog = 0;
5092159047fSniklas top_time = 0.0;
510b55d4692Sfgsch
5112159047fSniklas for (index = 0; index < symtab.len; ++index)
5122159047fSniklas {
5132159047fSniklas sym = time_sorted_syms[index];
514b55d4692Sfgsch
515b305b0f1Sespie if (sym->ncalls != 0)
5162159047fSniklas {
5172159047fSniklas time = (sym->hist.time + sym->cg.child_time) / sym->ncalls;
518b55d4692Sfgsch
5192159047fSniklas if (time > top_time)
5202159047fSniklas {
5212159047fSniklas top_dog = sym;
5222159047fSniklas top_time = time;
5232159047fSniklas }
5242159047fSniklas }
5252159047fSniklas }
526b55d4692Sfgsch
527b305b0f1Sespie if (top_dog && top_dog->ncalls != 0 && top_time > 0.0)
5282159047fSniklas {
5292159047fSniklas top_time /= hz;
530b55d4692Sfgsch
531*c074d1c9Sdrahn for (log_scale = 0; log_scale < ARRAY_SIZE (SItab); log_scale ++)
5322159047fSniklas {
533*c074d1c9Sdrahn double scaled_value = SItab[log_scale].scale * top_time;
534*c074d1c9Sdrahn
535*c074d1c9Sdrahn if (scaled_value >= 1.0 && scaled_value < 1000.0)
536*c074d1c9Sdrahn break;
5372159047fSniklas }
5382159047fSniklas }
5392159047fSniklas }
5402159047fSniklas
541b55d4692Sfgsch /* For now, the dimension is always seconds. In the future, we
542b55d4692Sfgsch may also want to support other (pseudo-)dimensions (such as
543b55d4692Sfgsch I-cache misses etc.). */
5442159047fSniklas print_header (SItab[log_scale].prefix);
545b55d4692Sfgsch
5462159047fSniklas for (index = 0; index < symtab.len; ++index)
5472159047fSniklas {
5482159047fSniklas addr = time_sorted_syms[index]->addr;
549b55d4692Sfgsch
550b55d4692Sfgsch /* Print symbol if its in INCL_FLAT table or that table
551b55d4692Sfgsch is empty and the symbol is not in EXCL_FLAT. */
5522159047fSniklas if (sym_lookup (&syms[INCL_FLAT], addr)
5532159047fSniklas || (syms[INCL_FLAT].len == 0
5542159047fSniklas && !sym_lookup (&syms[EXCL_FLAT], addr)))
5552159047fSniklas print_line (time_sorted_syms[index], SItab[log_scale].scale);
5562159047fSniklas }
557b55d4692Sfgsch
5582159047fSniklas free (time_sorted_syms);
5592159047fSniklas
5602159047fSniklas if (print_descriptions && !bsd_style_output)
5612159047fSniklas flat_blurb (stdout);
5622159047fSniklas }
563