12159047fSniklas /* 22159047fSniklas * Histogram related operations. 32159047fSniklas */ 42159047fSniklas #include <stdio.h> 52159047fSniklas #include "libiberty.h" 62159047fSniklas #include "gprof.h" 72159047fSniklas #include "core.h" 82159047fSniklas #include "gmon_io.h" 92159047fSniklas #include "gmon_out.h" 102159047fSniklas #include "hist.h" 112159047fSniklas #include "symtab.h" 122159047fSniklas #include "sym_ids.h" 132159047fSniklas #include "utils.h" 142159047fSniklas 152159047fSniklas /* declarations of automatically generated functions to output blurbs: */ 162159047fSniklas extern void flat_blurb PARAMS ((FILE * fp)); 172159047fSniklas 182159047fSniklas bfd_vma s_lowpc; /* lowest address in .text */ 192159047fSniklas bfd_vma s_highpc = 0; /* highest address in .text */ 202159047fSniklas bfd_vma lowpc, highpc; /* same, but expressed in UNITs */ 212159047fSniklas int hist_num_bins = 0; /* number of histogram samples */ 222159047fSniklas int *hist_sample = 0; /* histogram samples (shorts in the file!) */ 232159047fSniklas double hist_scale; 242159047fSniklas char hist_dimension[sizeof (((struct gmon_hist_hdr *) 0)->dimen) + 1] = 252159047fSniklas "seconds"; 262159047fSniklas char hist_dimension_abbrev = 's'; 272159047fSniklas 282159047fSniklas static double accum_time; /* accumulated time so far for print_line() */ 292159047fSniklas static double total_time; /* total time for all routines */ 302159047fSniklas /* 312159047fSniklas * Table of SI prefixes for powers of 10 (used to automatically 322159047fSniklas * scale some of the values in the flat profile). 332159047fSniklas */ 342159047fSniklas const struct 352159047fSniklas { 362159047fSniklas char prefix; 372159047fSniklas double scale; 382159047fSniklas } 392159047fSniklas SItab[] = 402159047fSniklas { 412159047fSniklas { 422159047fSniklas 'T', 1e-12 432159047fSniklas } 442159047fSniklas , /* tera */ 452159047fSniklas { 462159047fSniklas 'G', 1e-09 472159047fSniklas } 482159047fSniklas , /* giga */ 492159047fSniklas { 502159047fSniklas 'M', 1e-06 512159047fSniklas } 522159047fSniklas , /* mega */ 532159047fSniklas { 542159047fSniklas 'K', 1e-03 552159047fSniklas } 562159047fSniklas , /* kilo */ 572159047fSniklas { 582159047fSniklas ' ', 1e-00 592159047fSniklas } 602159047fSniklas , 612159047fSniklas { 622159047fSniklas 'm', 1e+03 632159047fSniklas } 642159047fSniklas , /* milli */ 652159047fSniklas { 662159047fSniklas 'u', 1e+06 672159047fSniklas } 682159047fSniklas , /* micro */ 692159047fSniklas { 702159047fSniklas 'n', 1e+09 712159047fSniklas } 722159047fSniklas , /* nano */ 732159047fSniklas { 742159047fSniklas 'p', 1e+12 752159047fSniklas } 762159047fSniklas , /* pico */ 772159047fSniklas { 782159047fSniklas 'f', 1e+15 792159047fSniklas } 802159047fSniklas , /* femto */ 812159047fSniklas { 822159047fSniklas 'a', 1e+18 832159047fSniklas } 842159047fSniklas , /* ato */ 852159047fSniklas }; 862159047fSniklas 872159047fSniklas /* 882159047fSniklas * Read the histogram from file IFP. FILENAME is the name of IFP and 892159047fSniklas * is provided for formatting error messages only. 902159047fSniklas */ 912159047fSniklas void 922159047fSniklas DEFUN (hist_read_rec, (ifp, filename), FILE * ifp AND const char *filename) 932159047fSniklas { 942159047fSniklas struct gmon_hist_hdr hdr; 952159047fSniklas bfd_vma n_lowpc, n_highpc; 962159047fSniklas int i, ncnt, profrate; 972159047fSniklas UNIT count; 982159047fSniklas 992159047fSniklas if (fread (&hdr, sizeof (hdr), 1, ifp) != 1) 1002159047fSniklas { 1012159047fSniklas fprintf (stderr, "%s: %s: unexpected end of file\n", 1022159047fSniklas whoami, filename); 1032159047fSniklas done (1); 1042159047fSniklas } 1052159047fSniklas 1062159047fSniklas n_lowpc = (bfd_vma) get_vma (core_bfd, (bfd_byte *) hdr.low_pc); 1072159047fSniklas n_highpc = (bfd_vma) get_vma (core_bfd, (bfd_byte *) hdr.high_pc); 1082159047fSniklas ncnt = bfd_get_32 (core_bfd, (bfd_byte *) hdr.hist_size); 1092159047fSniklas profrate = bfd_get_32 (core_bfd, (bfd_byte *) hdr.prof_rate); 1102159047fSniklas strncpy (hist_dimension, hdr.dimen, sizeof (hdr.dimen)); 1112159047fSniklas hist_dimension[sizeof (hdr.dimen)] = '\0'; 1122159047fSniklas hist_dimension_abbrev = hdr.dimen_abbrev; 1132159047fSniklas 1142159047fSniklas if (!s_highpc) 1152159047fSniklas { 1162159047fSniklas 1172159047fSniklas /* this is the first histogram record: */ 1182159047fSniklas 1192159047fSniklas s_lowpc = n_lowpc; 1202159047fSniklas s_highpc = n_highpc; 1212159047fSniklas lowpc = (bfd_vma) n_lowpc / sizeof (UNIT); 1222159047fSniklas highpc = (bfd_vma) n_highpc / sizeof (UNIT); 1232159047fSniklas hist_num_bins = ncnt; 1242159047fSniklas hz = profrate; 1252159047fSniklas } 1262159047fSniklas 1272159047fSniklas DBG (SAMPLEDEBUG, 1282159047fSniklas printf ("[hist_read_rec] n_lowpc 0x%lx n_highpc 0x%lx ncnt %d\n", 1292159047fSniklas n_lowpc, n_highpc, ncnt); 1302159047fSniklas printf ("[hist_read_rec] s_lowpc 0x%lx s_highpc 0x%lx nsamples %d\n", 1312159047fSniklas s_lowpc, s_highpc, hist_num_bins); 1322159047fSniklas printf ("[hist_read_rec] lowpc 0x%lx highpc 0x%lx\n", 1332159047fSniklas lowpc, highpc)); 1342159047fSniklas 1352159047fSniklas if (n_lowpc != s_lowpc || n_highpc != s_highpc 1362159047fSniklas || ncnt != hist_num_bins || hz != profrate) 1372159047fSniklas { 1382159047fSniklas fprintf (stderr, "%s: `%s' is incompatible with first gmon file\n", 1392159047fSniklas whoami, filename); 1402159047fSniklas done (1); 1412159047fSniklas } 1422159047fSniklas 1432159047fSniklas if (!hist_sample) 1442159047fSniklas { 1452159047fSniklas hist_sample = (int *) xmalloc (hist_num_bins * sizeof (hist_sample[0])); 1462159047fSniklas memset (hist_sample, 0, hist_num_bins * sizeof (hist_sample[0])); 1472159047fSniklas } 1482159047fSniklas 1492159047fSniklas for (i = 0; i < hist_num_bins; ++i) 1502159047fSniklas { 1512159047fSniklas if (fread (&count[0], sizeof (count), 1, ifp) != 1) 1522159047fSniklas { 1532159047fSniklas fprintf (stderr, 1542159047fSniklas "%s: %s: unexpected EOF after reading %d of %d samples\n", 1552159047fSniklas whoami, filename, i, hist_num_bins); 1562159047fSniklas done (1); 1572159047fSniklas } 1582159047fSniklas hist_sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) & count[0]); 1592159047fSniklas } 1602159047fSniklas } 1612159047fSniklas 1622159047fSniklas 1632159047fSniklas /* 1642159047fSniklas * Write execution histogram to file OFP. FILENAME is the name 1652159047fSniklas * of OFP and is provided for formatting error-messages only. 1662159047fSniklas */ 1672159047fSniklas void 1682159047fSniklas DEFUN (hist_write_hist, (ofp, filename), FILE * ofp AND const char *filename) 1692159047fSniklas { 1702159047fSniklas struct gmon_hist_hdr hdr; 1712159047fSniklas unsigned char tag; 1722159047fSniklas UNIT count; 1732159047fSniklas int i; 1742159047fSniklas 1752159047fSniklas /* write header: */ 1762159047fSniklas 1772159047fSniklas tag = GMON_TAG_TIME_HIST; 1782159047fSniklas put_vma (core_bfd, s_lowpc, (bfd_byte *) hdr.low_pc); 1792159047fSniklas put_vma (core_bfd, s_highpc, (bfd_byte *) hdr.high_pc); 1802159047fSniklas bfd_put_32 (core_bfd, hist_num_bins, (bfd_byte *) hdr.hist_size); 1812159047fSniklas bfd_put_32 (core_bfd, hz, (bfd_byte *) hdr.prof_rate); 1822159047fSniklas strncpy (hdr.dimen, hist_dimension, sizeof (hdr.dimen)); 1832159047fSniklas hdr.dimen_abbrev = hist_dimension_abbrev; 1842159047fSniklas 1852159047fSniklas if (fwrite (&tag, sizeof (tag), 1, ofp) != 1 1862159047fSniklas || fwrite (&hdr, sizeof (hdr), 1, ofp) != 1) 1872159047fSniklas { 1882159047fSniklas perror (filename); 1892159047fSniklas done (1); 1902159047fSniklas } 1912159047fSniklas 1922159047fSniklas for (i = 0; i < hist_num_bins; ++i) 1932159047fSniklas { 1942159047fSniklas bfd_put_16 (core_bfd, hist_sample[i], (bfd_byte *) & count[0]); 1952159047fSniklas if (fwrite (&count[0], sizeof (count), 1, ofp) != 1) 1962159047fSniklas { 1972159047fSniklas perror (filename); 1982159047fSniklas done (1); 1992159047fSniklas } 2002159047fSniklas } 2012159047fSniklas } 2022159047fSniklas 2032159047fSniklas 2042159047fSniklas /* 2052159047fSniklas * Calculate scaled entry point addresses (to save time in 2062159047fSniklas * hist_assign_samples), and, on architectures that have procedure 2072159047fSniklas * entry masks at the start of a function, possibly push the scaled 2082159047fSniklas * entry points over the procedure entry mask, if it turns out that 2092159047fSniklas * the entry point is in one bin and the code for a routine is in the 2102159047fSniklas * next bin. 2112159047fSniklas */ 2122159047fSniklas static void 213*191aa565Sniklas scale_and_align_entries () 2142159047fSniklas { 2152159047fSniklas Sym *sym; 2162159047fSniklas bfd_vma bin_of_entry; 2172159047fSniklas bfd_vma bin_of_code; 2182159047fSniklas 2192159047fSniklas for (sym = symtab.base; sym < symtab.limit; sym++) 2202159047fSniklas { 2212159047fSniklas sym->hist.scaled_addr = sym->addr / sizeof (UNIT); 2222159047fSniklas bin_of_entry = (sym->hist.scaled_addr - lowpc) / hist_scale; 2232159047fSniklas bin_of_code = (sym->hist.scaled_addr + UNITS_TO_CODE - lowpc) / hist_scale; 2242159047fSniklas if (bin_of_entry < bin_of_code) 2252159047fSniklas { 2262159047fSniklas DBG (SAMPLEDEBUG, 2272159047fSniklas printf ("[scale_and_align_entries] pushing 0x%lx to 0x%lx\n", 228*191aa565Sniklas sym->hist.scaled_addr, 229*191aa565Sniklas sym->hist.scaled_addr + UNITS_TO_CODE)); 230*191aa565Sniklas sym->hist.scaled_addr += UNITS_TO_CODE; 2312159047fSniklas } 2322159047fSniklas } 2332159047fSniklas } 2342159047fSniklas 2352159047fSniklas 2362159047fSniklas /* 2372159047fSniklas * Assign samples to the symbol to which they belong. 2382159047fSniklas * 2392159047fSniklas * Histogram bin I covers some address range [BIN_LOWPC,BIN_HIGH_PC) 2402159047fSniklas * which may overlap one more symbol address ranges. If a symbol 2412159047fSniklas * overlaps with the bin's address range by O percent, then O percent 2422159047fSniklas * of the bin's count is credited to that symbol. 2432159047fSniklas * 2442159047fSniklas * There are three cases as to where BIN_LOW_PC and BIN_HIGH_PC can be 2452159047fSniklas * with respect to the symbol's address range [SYM_LOW_PC, 2462159047fSniklas * SYM_HIGH_PC) as shown in the following diagram. OVERLAP computes 2472159047fSniklas * the distance (in UNITs) between the arrows, the fraction of the 2482159047fSniklas * sample that is to be credited to the symbol which starts at 2492159047fSniklas * SYM_LOW_PC. 2502159047fSniklas * 2512159047fSniklas * sym_low_pc sym_high_pc 2522159047fSniklas * | | 2532159047fSniklas * v v 2542159047fSniklas * 2552159047fSniklas * +-----------------------------------------------+ 2562159047fSniklas * | | 2572159047fSniklas * | ->| |<- ->| |<- ->| |<- | 2582159047fSniklas * | | | | | | 2592159047fSniklas * +---------+ +---------+ +---------+ 2602159047fSniklas * 2612159047fSniklas * ^ ^ ^ ^ ^ ^ 2622159047fSniklas * | | | | | | 2632159047fSniklas * bin_low_pc bin_high_pc bin_low_pc bin_high_pc bin_low_pc bin_high_pc 2642159047fSniklas * 2652159047fSniklas * For the VAX we assert that samples will never fall in the first two 2662159047fSniklas * bytes of any routine, since that is the entry mask, thus we call 2672159047fSniklas * scale_and_align_entries() to adjust the entry points if the entry 2682159047fSniklas * mask falls in one bin but the code for the routine doesn't start 2692159047fSniklas * until the next bin. In conjunction with the alignment of routine 2702159047fSniklas * addresses, this should allow us to have only one sample for every 2712159047fSniklas * four bytes of text space and never have any overlap (the two end 2722159047fSniklas * cases, above). 2732159047fSniklas */ 2742159047fSniklas void 2752159047fSniklas DEFUN_VOID (hist_assign_samples) 2762159047fSniklas { 2772159047fSniklas bfd_vma bin_low_pc, bin_high_pc; 2782159047fSniklas bfd_vma sym_low_pc, sym_high_pc; 2792159047fSniklas bfd_vma overlap, addr; 2802159047fSniklas int bin_count, i, j; 2812159047fSniklas double time, credit; 2822159047fSniklas 2832159047fSniklas /* read samples and assign to symbols: */ 2842159047fSniklas hist_scale = highpc - lowpc; 2852159047fSniklas hist_scale /= hist_num_bins; 2862159047fSniklas scale_and_align_entries (); 2872159047fSniklas 2882159047fSniklas /* iterate over all sample bins: */ 2892159047fSniklas 2902159047fSniklas for (i = 0, j = 1; i < hist_num_bins; ++i) 2912159047fSniklas { 2922159047fSniklas bin_count = hist_sample[i]; 2932159047fSniklas if (!bin_count) 2942159047fSniklas { 2952159047fSniklas continue; 2962159047fSniklas } 2972159047fSniklas bin_low_pc = lowpc + (bfd_vma) (hist_scale * i); 2982159047fSniklas bin_high_pc = lowpc + (bfd_vma) (hist_scale * (i + 1)); 2992159047fSniklas time = bin_count; 3002159047fSniklas DBG (SAMPLEDEBUG, 3012159047fSniklas printf ( 3022159047fSniklas "[assign_samples] bin_low_pc=0x%lx, bin_high_pc=0x%lx, bin_count=%d\n", 3032159047fSniklas sizeof (UNIT) * bin_low_pc, sizeof (UNIT) * bin_high_pc, 3042159047fSniklas bin_count)); 3052159047fSniklas total_time += time; 3062159047fSniklas 3072159047fSniklas /* credit all symbols that are covered by bin I: */ 3082159047fSniklas 3092159047fSniklas for (j = j - 1; j < symtab.len; ++j) 3102159047fSniklas { 3112159047fSniklas sym_low_pc = symtab.base[j].hist.scaled_addr; 3122159047fSniklas sym_high_pc = symtab.base[j + 1].hist.scaled_addr; 3132159047fSniklas /* 3142159047fSniklas * If high end of bin is below entry address, go for next 3152159047fSniklas * bin: 3162159047fSniklas */ 3172159047fSniklas if (bin_high_pc < sym_low_pc) 3182159047fSniklas { 3192159047fSniklas break; 3202159047fSniklas } 3212159047fSniklas /* 3222159047fSniklas * If low end of bin is above high end of symbol, go for 3232159047fSniklas * next symbol. 3242159047fSniklas */ 3252159047fSniklas if (bin_low_pc >= sym_high_pc) 3262159047fSniklas { 3272159047fSniklas continue; 3282159047fSniklas } 3292159047fSniklas overlap = 3302159047fSniklas MIN (bin_high_pc, sym_high_pc) - MAX (bin_low_pc, sym_low_pc); 3312159047fSniklas if (overlap > 0) 3322159047fSniklas { 3332159047fSniklas DBG (SAMPLEDEBUG, 3342159047fSniklas printf ( 3352159047fSniklas "[assign_samples] [0x%lx,0x%lx) %s gets %f ticks %ld overlap\n", 3362159047fSniklas symtab.base[j].addr, sizeof (UNIT) * sym_high_pc, 3372159047fSniklas symtab.base[j].name, overlap * time / hist_scale, 3382159047fSniklas overlap)); 3392159047fSniklas addr = symtab.base[j].addr; 3402159047fSniklas credit = overlap * time / hist_scale; 3412159047fSniklas /* 3422159047fSniklas * Credit symbol if it appears in INCL_FLAT or that 3432159047fSniklas * table is empty and it does not appear it in 3442159047fSniklas * EXCL_FLAT. 3452159047fSniklas */ 3462159047fSniklas if (sym_lookup (&syms[INCL_FLAT], addr) 3472159047fSniklas || (syms[INCL_FLAT].len == 0 3482159047fSniklas && !sym_lookup (&syms[EXCL_FLAT], addr))) 3492159047fSniklas { 3502159047fSniklas symtab.base[j].hist.time += credit; 3512159047fSniklas } 3522159047fSniklas else 3532159047fSniklas { 3542159047fSniklas total_time -= credit; 3552159047fSniklas } 3562159047fSniklas } 3572159047fSniklas } 3582159047fSniklas } 3592159047fSniklas DBG (SAMPLEDEBUG, printf ("[assign_samples] total_time %f\n", 3602159047fSniklas total_time)); 3612159047fSniklas } 3622159047fSniklas 3632159047fSniklas 3642159047fSniklas /* 3652159047fSniklas * Print header for flag histogram profile: 3662159047fSniklas */ 3672159047fSniklas static void 3682159047fSniklas DEFUN (print_header, (prefix), const char prefix) 3692159047fSniklas { 3702159047fSniklas char unit[64]; 3712159047fSniklas 3722159047fSniklas sprintf (unit, "%c%c/call", prefix, hist_dimension_abbrev); 3732159047fSniklas 3742159047fSniklas if (bsd_style_output) 3752159047fSniklas { 3762159047fSniklas printf ("\ngranularity: each sample hit covers %ld byte(s)", 3772159047fSniklas (long) hist_scale * sizeof (UNIT)); 3782159047fSniklas if (total_time > 0.0) 3792159047fSniklas { 3802159047fSniklas printf (" for %.2f%% of %.2f %s\n\n", 3812159047fSniklas 100.0 / total_time, total_time / hz, hist_dimension); 3822159047fSniklas } 3832159047fSniklas } 3842159047fSniklas else 3852159047fSniklas { 3862159047fSniklas printf ("\nEach sample counts as %g %s.\n", 1.0 / hz, hist_dimension); 3872159047fSniklas } 3882159047fSniklas 3892159047fSniklas if (total_time <= 0.0) 3902159047fSniklas { 3912159047fSniklas printf (" no time accumulated\n\n"); 3922159047fSniklas /* this doesn't hurt since all the numerators will be zero: */ 3932159047fSniklas total_time = 1.0; 3942159047fSniklas } 3952159047fSniklas 3962159047fSniklas printf ("%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s %-8.8s\n", 3972159047fSniklas "% ", "cumulative", "self ", "", "self ", "total ", ""); 3982159047fSniklas printf ("%5.5s %9.9s %8.8s %8.8s %8.8s %8.8s %-8.8s\n", 3992159047fSniklas "time", hist_dimension, hist_dimension, "calls", unit, unit, 4002159047fSniklas "name"); 4012159047fSniklas } 4022159047fSniklas 4032159047fSniklas 4042159047fSniklas static void 4052159047fSniklas DEFUN (print_line, (sym, scale), Sym * sym AND double scale) 4062159047fSniklas { 4072159047fSniklas if (ignore_zeros && sym->ncalls == 0 && sym->hist.time == 0) 4082159047fSniklas { 4092159047fSniklas return; 4102159047fSniklas } 4112159047fSniklas 4122159047fSniklas accum_time += sym->hist.time; 4132159047fSniklas if (bsd_style_output) 4142159047fSniklas { 4152159047fSniklas printf ("%5.1f %10.2f %8.2f", 4162159047fSniklas total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0, 4172159047fSniklas accum_time / hz, sym->hist.time / hz); 4182159047fSniklas } 4192159047fSniklas else 4202159047fSniklas { 4212159047fSniklas printf ("%6.2f %9.2f %8.2f", 4222159047fSniklas total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0, 4232159047fSniklas accum_time / hz, sym->hist.time / hz); 4242159047fSniklas } 4252159047fSniklas if (sym->ncalls) 4262159047fSniklas { 4272159047fSniklas printf (" %8d %8.2f %8.2f ", 4282159047fSniklas sym->ncalls, scale * sym->hist.time / hz / sym->ncalls, 4292159047fSniklas scale * (sym->hist.time + sym->cg.child_time) / hz / sym->ncalls); 4302159047fSniklas } 4312159047fSniklas else 4322159047fSniklas { 4332159047fSniklas printf (" %8.8s %8.8s %8.8s ", "", "", ""); 4342159047fSniklas } 4352159047fSniklas if (bsd_style_output) 4362159047fSniklas { 4372159047fSniklas print_name (sym); 4382159047fSniklas } 4392159047fSniklas else 4402159047fSniklas { 4412159047fSniklas print_name_only (sym); 4422159047fSniklas } 4432159047fSniklas printf ("\n"); 4442159047fSniklas } 4452159047fSniklas 4462159047fSniklas 4472159047fSniklas /* 4482159047fSniklas * Compare LP and RP. The primary comparison key is execution time, 4492159047fSniklas * the secondary is number of invocation, and the tertiary is the 4502159047fSniklas * lexicographic order of the function names. 4512159047fSniklas */ 4522159047fSniklas static int 4532159047fSniklas DEFUN (cmp_time, (lp, rp), const PTR lp AND const PTR rp) 4542159047fSniklas { 4552159047fSniklas const Sym *left = *(const Sym **) lp; 4562159047fSniklas const Sym *right = *(const Sym **) rp; 4572159047fSniklas double time_diff; 4582159047fSniklas long call_diff; 4592159047fSniklas 4602159047fSniklas time_diff = right->hist.time - left->hist.time; 4612159047fSniklas if (time_diff > 0.0) 4622159047fSniklas { 4632159047fSniklas return 1; 4642159047fSniklas } 4652159047fSniklas if (time_diff < 0.0) 4662159047fSniklas { 4672159047fSniklas return -1; 4682159047fSniklas } 4692159047fSniklas 4702159047fSniklas call_diff = right->ncalls - left->ncalls; 4712159047fSniklas if (call_diff > 0) 4722159047fSniklas { 4732159047fSniklas return 1; 4742159047fSniklas } 4752159047fSniklas if (call_diff < 0) 4762159047fSniklas { 4772159047fSniklas return -1; 4782159047fSniklas } 4792159047fSniklas 4802159047fSniklas return strcmp (left->name, right->name); 4812159047fSniklas } 4822159047fSniklas 4832159047fSniklas 4842159047fSniklas /* 4852159047fSniklas * Print the flat histogram profile. 4862159047fSniklas */ 4872159047fSniklas void 4882159047fSniklas DEFUN_VOID (hist_print) 4892159047fSniklas { 4902159047fSniklas Sym **time_sorted_syms, *top_dog, *sym; 4912159047fSniklas int index, log_scale; 4922159047fSniklas double top_time, time; 4932159047fSniklas bfd_vma addr; 4942159047fSniklas 4952159047fSniklas if (first_output) 4962159047fSniklas { 4972159047fSniklas first_output = FALSE; 4982159047fSniklas } 4992159047fSniklas else 5002159047fSniklas { 5012159047fSniklas printf ("\f\n"); 5022159047fSniklas } 5032159047fSniklas 5042159047fSniklas accum_time = 0.0; 5052159047fSniklas if (bsd_style_output) 5062159047fSniklas { 5072159047fSniklas if (print_descriptions) 5082159047fSniklas { 5092159047fSniklas printf ("\n\n\nflat profile:\n"); 5102159047fSniklas flat_blurb (stdout); 5112159047fSniklas } 5122159047fSniklas } 5132159047fSniklas else 5142159047fSniklas { 5152159047fSniklas printf ("Flat profile:\n"); 5162159047fSniklas } 5172159047fSniklas /* 5182159047fSniklas * Sort the symbol table by time (call-count and name as secondary 5192159047fSniklas * and tertiary keys): 5202159047fSniklas */ 5212159047fSniklas time_sorted_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *)); 5222159047fSniklas for (index = 0; index < symtab.len; ++index) 5232159047fSniklas { 5242159047fSniklas time_sorted_syms[index] = &symtab.base[index]; 5252159047fSniklas } 5262159047fSniklas qsort (time_sorted_syms, symtab.len, sizeof (Sym *), cmp_time); 5272159047fSniklas 5282159047fSniklas if (bsd_style_output) 5292159047fSniklas { 5302159047fSniklas log_scale = 5; /* milli-seconds is BSD-default */ 5312159047fSniklas } 5322159047fSniklas else 5332159047fSniklas { 5342159047fSniklas /* 5352159047fSniklas * Search for symbol with highest per-call execution time and 5362159047fSniklas * scale accordingly: 5372159047fSniklas */ 5382159047fSniklas log_scale = 0; 5392159047fSniklas top_dog = 0; 5402159047fSniklas top_time = 0.0; 5412159047fSniklas for (index = 0; index < symtab.len; ++index) 5422159047fSniklas { 5432159047fSniklas sym = time_sorted_syms[index]; 5442159047fSniklas if (sym->ncalls) 5452159047fSniklas { 5462159047fSniklas time = (sym->hist.time + sym->cg.child_time) / sym->ncalls; 5472159047fSniklas if (time > top_time) 5482159047fSniklas { 5492159047fSniklas top_dog = sym; 5502159047fSniklas top_time = time; 5512159047fSniklas } 5522159047fSniklas } 5532159047fSniklas } 5542159047fSniklas if (top_dog && top_dog->ncalls && top_time > 0.0) 5552159047fSniklas { 5562159047fSniklas top_time /= hz; 5572159047fSniklas while (SItab[log_scale].scale * top_time < 1000.0 5582159047fSniklas && log_scale < sizeof (SItab) / sizeof (SItab[0]) - 1) 5592159047fSniklas { 5602159047fSniklas ++log_scale; 5612159047fSniklas } 5622159047fSniklas } 5632159047fSniklas } 5642159047fSniklas 5652159047fSniklas /* 5662159047fSniklas * For now, the dimension is always seconds. In the future, we 5672159047fSniklas * may also want to support other (pseudo-)dimensions (such as 5682159047fSniklas * I-cache misses etc.). 5692159047fSniklas */ 5702159047fSniklas print_header (SItab[log_scale].prefix); 5712159047fSniklas for (index = 0; index < symtab.len; ++index) 5722159047fSniklas { 5732159047fSniklas addr = time_sorted_syms[index]->addr; 5742159047fSniklas /* 5752159047fSniklas * Print symbol if its in INCL_FLAT table or that table 5762159047fSniklas * is empty and the symbol is not in EXCL_FLAT. 5772159047fSniklas */ 5782159047fSniklas if (sym_lookup (&syms[INCL_FLAT], addr) 5792159047fSniklas || (syms[INCL_FLAT].len == 0 5802159047fSniklas && !sym_lookup (&syms[EXCL_FLAT], addr))) 5812159047fSniklas { 5822159047fSniklas print_line (time_sorted_syms[index], SItab[log_scale].scale); 5832159047fSniklas } 5842159047fSniklas } 5852159047fSniklas free (time_sorted_syms); 5862159047fSniklas 5872159047fSniklas if (print_descriptions && !bsd_style_output) 5882159047fSniklas { 5892159047fSniklas flat_blurb (stdout); 5902159047fSniklas } 5912159047fSniklas } 592