1 /* hist.c - Histogram related operations. 2 3 Copyright (C) 1999-2026 Free Software Foundation, Inc. 4 5 This file is part of GNU Binutils. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 20 02110-1301, USA. */ 21 22 #include "gprof.h" 23 #include "libiberty.h" 24 #include "search_list.h" 25 #include "source.h" 26 #include "symtab.h" 27 #include "corefile.h" 28 #include "gmon_io.h" 29 #include "gmon_out.h" 30 #include "hist.h" 31 #include "sym_ids.h" 32 #include "utils.h" 33 #include "math.h" 34 #include "stdio.h" 35 #include "stdlib.h" 36 37 #define UNITS_TO_CODE (offset_to_code / sizeof(UNIT)) 38 39 static void scale_and_align_entries (void); 40 static void print_header (int); 41 static void print_line (Sym *, double); 42 static int cmp_time (const void *, const void *); 43 44 /* Declarations of automatically generated functions to output blurbs. */ 45 extern void flat_blurb (FILE * fp); 46 47 static histogram *find_histogram (bfd_vma lowpc, bfd_vma highpc); 48 static histogram *find_histogram_for_pc (bfd_vma pc); 49 50 histogram * histograms; 51 unsigned num_histograms; 52 double hist_scale; 53 static char hist_dimension[16] = "seconds"; 54 static char hist_dimension_abbrev = 's'; 55 56 static double accum_time; /* Accumulated time so far for print_line(). */ 57 static double total_time; /* Total time for all routines. */ 58 59 /* Table of SI prefixes for powers of 10 (used to automatically 60 scale some of the values in the flat profile). */ 61 const struct 62 { 63 char prefix; 64 double scale; 65 } 66 SItab[] = 67 { 68 { 'T', 1e-12 }, /* tera */ 69 { 'G', 1e-09 }, /* giga */ 70 { 'M', 1e-06 }, /* mega */ 71 { 'K', 1e-03 }, /* kilo */ 72 { ' ', 1e-00 }, 73 { 'm', 1e+03 }, /* milli */ 74 { 'u', 1e+06 }, /* micro */ 75 { 'n', 1e+09 }, /* nano */ 76 { 'p', 1e+12 }, /* pico */ 77 { 'f', 1e+15 }, /* femto */ 78 { 'a', 1e+18 } /* ato */ 79 }; 80 81 /* Reads just the header part of histogram record into 82 *RECORD from IFP. FILENAME is the name of IFP and 83 is provided for formatting error messages only. 84 85 If FIRST is non-zero, sets global variables HZ, HIST_DIMENSION, 86 HIST_DIMENSION_ABBREV, HIST_SCALE. If FIRST is zero, checks 87 that the new histogram is compatible with already-set values 88 of those variables and emits an error if that's not so. */ 89 static void 90 read_histogram_header (histogram *record, 91 FILE *ifp, const char *filename, 92 int first) 93 { 94 unsigned int profrate; 95 char n_hist_dimension[15]; 96 char n_hist_dimension_abbrev; 97 double n_hist_scale; 98 99 if (gmon_io_read_vma (ifp, &record->lowpc) 100 || gmon_io_read_vma (ifp, &record->highpc) 101 || gmon_io_read_32 (ifp, &record->num_bins) 102 || gmon_io_read_32 (ifp, &profrate) 103 || gmon_io_read (ifp, n_hist_dimension, 15) 104 || gmon_io_read (ifp, &n_hist_dimension_abbrev, 1)) 105 { 106 fprintf (stderr, _("%s: %s: unexpected end of file\n"), 107 whoami, filename); 108 109 done (1); 110 } 111 112 n_hist_scale = (double)(record->highpc - record->lowpc) / sizeof (UNIT) 113 / record->num_bins; 114 115 if (first) 116 { 117 /* We don't try to verify profrate is the same for all histogram 118 records. If we have two histogram records for the same 119 address range and profiling samples is done as often 120 as possible as opposed on timer, then the actual profrate will 121 be slightly different. Most of the time the difference does not 122 matter and insisting that profiling rate is exactly the same 123 will only create inconvenient. */ 124 hz = profrate; 125 memcpy (hist_dimension, n_hist_dimension, 15); 126 hist_dimension_abbrev = n_hist_dimension_abbrev; 127 hist_scale = n_hist_scale; 128 } 129 else 130 { 131 if (strncmp (n_hist_dimension, hist_dimension, 15) != 0) 132 { 133 fprintf (stderr, 134 _("%s: dimension unit changed between histogram records\n" 135 "%s: from '%s'\n" 136 "%s: to '%s'\n"), 137 whoami, whoami, hist_dimension, whoami, n_hist_dimension); 138 done (1); 139 } 140 141 if (n_hist_dimension_abbrev != hist_dimension_abbrev) 142 { 143 fprintf (stderr, 144 _("%s: dimension abbreviation changed between histogram records\n" 145 "%s: from '%c'\n" 146 "%s: to '%c'\n"), 147 whoami, whoami, hist_dimension_abbrev, whoami, n_hist_dimension_abbrev); 148 done (1); 149 } 150 151 /* The only reason we require the same scale for histograms is that 152 there's code (notably printing code), that prints units, 153 and it would be very confusing to have one unit mean different 154 things for different functions. */ 155 if (fabs (hist_scale - n_hist_scale) > 0.000001) 156 { 157 fprintf (stderr, 158 _("%s: different scales in histogram records: %f != %f\n"), 159 whoami, hist_scale, n_hist_scale); 160 done (1); 161 } 162 } 163 } 164 165 /* Read the histogram from file IFP. FILENAME is the name of IFP and 166 is provided for formatting error messages only. */ 167 168 void 169 hist_read_rec (FILE * ifp, const char *filename) 170 { 171 bfd_vma lowpc, highpc; 172 histogram n_record; 173 histogram *record, *existing_record; 174 unsigned i; 175 176 /* 1. Read the header and see if there's existing record for the 177 same address range and that there are no overlapping records. */ 178 read_histogram_header (&n_record, ifp, filename, num_histograms == 0); 179 180 existing_record = find_histogram (n_record.lowpc, n_record.highpc); 181 if (existing_record) 182 { 183 record = existing_record; 184 } 185 else 186 { 187 /* If this record overlaps, but does not completely match an existing 188 record, it's an error. */ 189 lowpc = n_record.lowpc; 190 highpc = n_record.highpc; 191 hist_clip_symbol_address (&lowpc, &highpc); 192 if (lowpc != highpc) 193 { 194 fprintf (stderr, 195 _("%s: overlapping histogram records\n"), 196 whoami); 197 done (1); 198 } 199 200 /* This is new record. Add it to global array and allocate space for 201 the samples. */ 202 histograms = (struct histogram *) 203 xrealloc (histograms, sizeof (histogram) * (num_histograms + 1)); 204 memcpy (histograms + num_histograms, 205 &n_record, sizeof (histogram)); 206 record = &histograms[num_histograms]; 207 ++num_histograms; 208 209 record->sample = (int *) xmalloc (record->num_bins 210 * sizeof (record->sample[0])); 211 memset (record->sample, 0, record->num_bins * sizeof (record->sample[0])); 212 } 213 214 /* 2. We have either a new record (with zeroed histogram data), or an existing 215 record with some data in the histogram already. Read new data into the 216 record, adding hit counts. */ 217 218 DBG (SAMPLEDEBUG, 219 printf ("[hist_read_rec] n_lowpc 0x%lx n_highpc 0x%lx ncnt %u\n", 220 (unsigned long) record->lowpc, (unsigned long) record->highpc, 221 record->num_bins)); 222 223 for (i = 0; i < record->num_bins; ++i) 224 { 225 UNIT count; 226 if (fread (&count[0], sizeof (count), 1, ifp) != 1) 227 { 228 fprintf (stderr, 229 _("%s: %s: unexpected EOF after reading %u of %u samples\n"), 230 whoami, filename, i, record->num_bins); 231 done (1); 232 } 233 record->sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) & count[0]); 234 DBG (SAMPLEDEBUG, 235 printf ("[hist_read_rec] 0x%lx: %u\n", 236 (unsigned long) (record->lowpc 237 + i * (record->highpc - record->lowpc) 238 / record->num_bins), 239 record->sample[i])); 240 } 241 } 242 243 244 /* Write all execution histograms file OFP. FILENAME is the name 245 of OFP and is provided for formatting error-messages only. */ 246 247 void 248 hist_write_hist (FILE * ofp, const char *filename) 249 { 250 UNIT count; 251 unsigned int i, r; 252 253 for (r = 0; r < num_histograms; ++r) 254 { 255 histogram *record = &histograms[r]; 256 257 /* Write header. */ 258 259 if (gmon_io_write_8 (ofp, GMON_TAG_TIME_HIST) 260 || gmon_io_write_vma (ofp, record->lowpc) 261 || gmon_io_write_vma (ofp, record->highpc) 262 || gmon_io_write_32 (ofp, record->num_bins) 263 || gmon_io_write_32 (ofp, hz) 264 || gmon_io_write (ofp, hist_dimension, 15) 265 || gmon_io_write (ofp, &hist_dimension_abbrev, 1)) 266 { 267 perror (filename); 268 done (1); 269 } 270 271 for (i = 0; i < record->num_bins; ++i) 272 { 273 bfd_put_16 (core_bfd, (bfd_vma) record->sample[i], (bfd_byte *) &count[0]); 274 275 if (fwrite (&count[0], sizeof (count), 1, ofp) != 1) 276 { 277 perror (filename); 278 done (1); 279 } 280 } 281 } 282 } 283 284 /* Calculate scaled entry point addresses (to save time in 285 hist_assign_samples), and, on architectures that have procedure 286 entry masks at the start of a function, possibly push the scaled 287 entry points over the procedure entry mask, if it turns out that 288 the entry point is in one bin and the code for a routine is in the 289 next bin. */ 290 291 static void 292 scale_and_align_entries (void) 293 { 294 Sym *sym; 295 bfd_vma bin_of_entry; 296 bfd_vma bin_of_code; 297 Sym_Table *symtab = get_symtab (); 298 299 for (sym = symtab->base; sym < symtab->limit; sym++) 300 { 301 histogram *r = find_histogram_for_pc (sym->addr); 302 303 sym->hist.scaled_addr = sym->addr / sizeof (UNIT); 304 305 if (r) 306 { 307 bin_of_entry = (sym->hist.scaled_addr - r->lowpc) / hist_scale; 308 bin_of_code = ((sym->hist.scaled_addr + UNITS_TO_CODE - r->lowpc) 309 / hist_scale); 310 if (bin_of_entry < bin_of_code) 311 { 312 DBG (SAMPLEDEBUG, 313 printf ("[scale_and_align_entries] pushing 0x%lx to 0x%lx\n", 314 (unsigned long) sym->hist.scaled_addr, 315 (unsigned long) (sym->hist.scaled_addr 316 + UNITS_TO_CODE))); 317 sym->hist.scaled_addr += UNITS_TO_CODE; 318 } 319 } 320 } 321 } 322 323 324 /* Assign samples to the symbol to which they belong. 325 326 Histogram bin I covers some address range [BIN_LOWPC,BIN_HIGH_PC) 327 which may overlap one more symbol address ranges. If a symbol 328 overlaps with the bin's address range by O percent, then O percent 329 of the bin's count is credited to that symbol. 330 331 There are three cases as to where BIN_LOW_PC and BIN_HIGH_PC can be 332 with respect to the symbol's address range [SYM_LOW_PC, 333 SYM_HIGH_PC) as shown in the following diagram. OVERLAP computes 334 the distance (in UNITs) between the arrows, the fraction of the 335 sample that is to be credited to the symbol which starts at 336 SYM_LOW_PC. 337 338 sym_low_pc sym_high_pc 339 | | 340 v v 341 342 +-----------------------------------------------+ 343 | | 344 | ->| |<- ->| |<- ->| |<- | 345 | | | | | | 346 +---------+ +---------+ +---------+ 347 348 ^ ^ ^ ^ ^ ^ 349 | | | | | | 350 bin_low_pc bin_high_pc bin_low_pc bin_high_pc bin_low_pc bin_high_pc 351 352 For the VAX we assert that samples will never fall in the first two 353 bytes of any routine, since that is the entry mask, thus we call 354 scale_and_align_entries() to adjust the entry points if the entry 355 mask falls in one bin but the code for the routine doesn't start 356 until the next bin. In conjunction with the alignment of routine 357 addresses, this should allow us to have only one sample for every 358 four bytes of text space and never have any overlap (the two end 359 cases, above). */ 360 361 static void 362 hist_assign_samples_1 (histogram *r) 363 { 364 bfd_vma bin_low_pc, bin_high_pc; 365 bfd_vma sym_low_pc, sym_high_pc; 366 bfd_vma overlap, addr; 367 unsigned int bin_count; 368 unsigned int i, j, k; 369 double count_time, credit; 370 Sym_Table *symtab = get_symtab (); 371 372 bfd_vma lowpc = r->lowpc / sizeof (UNIT); 373 374 /* Iterate over all sample bins. */ 375 for (i = 0, k = 1; i < r->num_bins; ++i) 376 { 377 bin_count = r->sample[i]; 378 if (! bin_count) 379 continue; 380 381 bin_low_pc = lowpc + (bfd_vma) (hist_scale * i); 382 bin_high_pc = lowpc + (bfd_vma) (hist_scale * (i + 1)); 383 count_time = bin_count; 384 385 DBG (SAMPLEDEBUG, 386 printf ( 387 "[assign_samples] bin_low_pc=0x%lx, bin_high_pc=0x%lx, bin_count=%u\n", 388 (unsigned long) (sizeof (UNIT) * bin_low_pc), 389 (unsigned long) (sizeof (UNIT) * bin_high_pc), 390 bin_count)); 391 total_time += count_time; 392 393 /* Credit all symbols that are covered by bin I. 394 395 PR gprof/13325: Make sure that K does not get decremented 396 and J will never be less than 0. */ 397 for (j = k - 1; j < symtab->len; k = ++j) 398 { 399 sym_low_pc = symtab->base[j].hist.scaled_addr; 400 sym_high_pc = symtab->base[j + 1].hist.scaled_addr; 401 402 /* If high end of bin is below entry address, 403 go for next bin. */ 404 if (bin_high_pc < sym_low_pc) 405 break; 406 407 /* If low end of bin is above high end of symbol, 408 go for next symbol. */ 409 if (bin_low_pc >= sym_high_pc) 410 continue; 411 412 overlap = 413 MIN (bin_high_pc, sym_high_pc) - MAX (bin_low_pc, sym_low_pc); 414 if (overlap > 0) 415 { 416 DBG (SAMPLEDEBUG, 417 printf ( 418 "[assign_samples] [0x%lx,0x%lx) %s gets %f ticks %ld overlap\n", 419 (unsigned long) symtab->base[j].addr, 420 (unsigned long) (sizeof (UNIT) * sym_high_pc), 421 symtab->base[j].name, overlap * count_time / hist_scale, 422 (long) overlap)); 423 424 addr = symtab->base[j].addr; 425 credit = overlap * count_time / hist_scale; 426 427 /* Credit symbol if it appears in INCL_FLAT or that 428 table is empty and it does not appear it in 429 EXCL_FLAT. */ 430 if (sym_lookup (&syms[INCL_FLAT], addr) 431 || (syms[INCL_FLAT].len == 0 432 && !sym_lookup (&syms[EXCL_FLAT], addr))) 433 { 434 symtab->base[j].hist.time += credit; 435 } 436 else 437 { 438 total_time -= credit; 439 } 440 } 441 } 442 } 443 444 DBG (SAMPLEDEBUG, printf ("[assign_samples] total_time %f\n", 445 total_time)); 446 } 447 448 /* Calls 'hist_assign_samples_1' for all histogram records read so far. */ 449 void 450 hist_assign_samples (void) 451 { 452 unsigned i; 453 454 scale_and_align_entries (); 455 456 for (i = 0; i < num_histograms; ++i) 457 hist_assign_samples_1 (&histograms[i]); 458 459 } 460 461 /* Print header for flag histogram profile. */ 462 463 static void 464 print_header (int prefix) 465 { 466 char unit[64]; 467 468 sprintf (unit, _("%c%c/call"), prefix, hist_dimension_abbrev); 469 470 if (bsd_style_output) 471 { 472 printf (_("\ngranularity: each sample hit covers %ld byte(s)"), 473 (long) hist_scale * (long) sizeof (UNIT)); 474 if (total_time > 0.0) 475 { 476 printf (_(" for %.2f%% of %.2f %s\n\n"), 477 100.0 / total_time, total_time / hz, hist_dimension); 478 } 479 } 480 else 481 { 482 printf (_("\nEach sample counts as %g %s.\n"), 1.0 / hz, hist_dimension); 483 } 484 485 if (total_time <= 0.0) 486 { 487 printf (_(" no time accumulated\n\n")); 488 489 /* This doesn't hurt since all the numerators will be zero. */ 490 total_time = 1.0; 491 } 492 493 printf ("%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s %-8.8s\n", 494 "% ", _("cumulative"), _("self "), "", _("self "), _("total "), 495 ""); 496 printf ("%5.5s %9.9s %8.8s %8.8s %8.8s %8.8s %-8.8s\n", 497 _("time"), hist_dimension, hist_dimension, _("calls"), unit, unit, 498 _("name")); 499 } 500 501 502 static void 503 print_line (Sym *sym, double scale) 504 { 505 if (ignore_zeros && sym->ncalls == 0 && sym->hist.time == 0) 506 return; 507 508 accum_time += sym->hist.time; 509 510 if (bsd_style_output) 511 printf ("%5.1f %10.2f %8.2f", 512 total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0, 513 accum_time / hz, sym->hist.time / hz); 514 else 515 printf ("%6.2f %9.2f %8.2f", 516 total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0, 517 accum_time / hz, sym->hist.time / hz); 518 519 if (sym->ncalls != 0) 520 printf (" %8lu %8.2f %8.2f ", 521 sym->ncalls, scale * sym->hist.time / hz / sym->ncalls, 522 scale * (sym->hist.time + sym->cg.child_time) / hz / sym->ncalls); 523 else 524 printf (" %8.8s %8.8s %8.8s ", "", "", ""); 525 526 if (bsd_style_output) 527 print_name (sym); 528 else 529 print_name_only (sym); 530 531 printf ("\n"); 532 } 533 534 535 /* Compare LP and RP. The primary comparison key is execution time, 536 the secondary is number of invocation, and the tertiary is the 537 lexicographic order of the function names. */ 538 539 static int 540 cmp_time (const void *lp, const void *rp) 541 { 542 const Sym *left = *(const Sym **) lp; 543 const Sym *right = *(const Sym **) rp; 544 double time_diff; 545 546 time_diff = right->hist.time - left->hist.time; 547 548 if (time_diff > 0.0) 549 return 1; 550 551 if (time_diff < 0.0) 552 return -1; 553 554 if (right->ncalls > left->ncalls) 555 return 1; 556 557 if (right->ncalls < left->ncalls) 558 return -1; 559 560 return strcmp (left->name, right->name); 561 } 562 563 564 /* Print the flat histogram profile. */ 565 566 void 567 hist_print (void) 568 { 569 Sym **time_sorted_syms, *top_dog, *sym; 570 unsigned int sym_index; 571 unsigned log_scale; 572 double top_time; 573 bfd_vma addr; 574 Sym_Table *symtab = get_symtab (); 575 576 if (first_output) 577 first_output = false; 578 else 579 printf ("\f\n"); 580 581 accum_time = 0.0; 582 583 if (bsd_style_output) 584 { 585 if (print_descriptions) 586 { 587 printf (_("\n\n\nflat profile:\n")); 588 flat_blurb (stdout); 589 } 590 } 591 else 592 { 593 printf (_("Flat profile:\n")); 594 } 595 596 /* Sort the symbol table by time (call-count and name as secondary 597 and tertiary keys). */ 598 time_sorted_syms = (Sym **) xmalloc (symtab->len * sizeof (Sym *)); 599 600 for (sym_index = 0; sym_index < symtab->len; ++sym_index) 601 time_sorted_syms[sym_index] = &symtab->base[sym_index]; 602 603 qsort (time_sorted_syms, symtab->len, sizeof (Sym *), cmp_time); 604 605 if (bsd_style_output) 606 { 607 log_scale = 5; /* Milli-seconds is BSD-default. */ 608 } 609 else 610 { 611 /* Search for symbol with highest per-call 612 execution time and scale accordingly. */ 613 log_scale = 0; 614 top_dog = 0; 615 top_time = 0.0; 616 617 for (sym_index = 0; sym_index < symtab->len; ++sym_index) 618 { 619 sym = time_sorted_syms[sym_index]; 620 621 if (sym->ncalls != 0) 622 { 623 double call_time; 624 625 call_time = (sym->hist.time + sym->cg.child_time) / sym->ncalls; 626 627 if (call_time > top_time) 628 { 629 top_dog = sym; 630 top_time = call_time; 631 } 632 } 633 } 634 635 if (top_dog && top_dog->ncalls != 0 && top_time > 0.0) 636 { 637 top_time /= hz; 638 639 for (log_scale = 0; log_scale < ARRAY_SIZE (SItab); log_scale ++) 640 { 641 double scaled_value = SItab[log_scale].scale * top_time; 642 643 if (scaled_value >= 1.0 && scaled_value < 1000.0) 644 break; 645 } 646 } 647 } 648 649 /* For now, the dimension is always seconds. In the future, we 650 may also want to support other (pseudo-)dimensions (such as 651 I-cache misses etc.). */ 652 print_header (SItab[log_scale].prefix); 653 654 for (sym_index = 0; sym_index < symtab->len; ++sym_index) 655 { 656 addr = time_sorted_syms[sym_index]->addr; 657 658 /* Print symbol if its in INCL_FLAT table or that table 659 is empty and the symbol is not in EXCL_FLAT. */ 660 if (sym_lookup (&syms[INCL_FLAT], addr) 661 || (syms[INCL_FLAT].len == 0 662 && !sym_lookup (&syms[EXCL_FLAT], addr))) 663 print_line (time_sorted_syms[sym_index], SItab[log_scale].scale); 664 } 665 666 free (time_sorted_syms); 667 668 if (print_descriptions && !bsd_style_output) 669 flat_blurb (stdout); 670 } 671 672 int 673 hist_check_address (unsigned address) 674 { 675 unsigned i; 676 677 for (i = 0; i < num_histograms; ++i) 678 if (histograms[i].lowpc <= address && address < histograms[i].highpc) 679 return 1; 680 681 return 0; 682 } 683 684 #if ! defined(min) 685 #define min(a,b) (((a)<(b)) ? (a) : (b)) 686 #endif 687 #if ! defined(max) 688 #define max(a,b) (((a)>(b)) ? (a) : (b)) 689 #endif 690 691 void 692 hist_clip_symbol_address (bfd_vma *p_lowpc, bfd_vma *p_highpc) 693 { 694 unsigned i; 695 int found = 0; 696 697 if (num_histograms == 0) 698 { 699 *p_highpc = *p_lowpc; 700 return; 701 } 702 703 for (i = 0; i < num_histograms; ++i) 704 { 705 bfd_vma common_low, common_high; 706 common_low = max (histograms[i].lowpc, *p_lowpc); 707 common_high = min (histograms[i].highpc, *p_highpc); 708 709 if (common_low < common_high) 710 { 711 if (found) 712 { 713 fprintf (stderr, 714 _("%s: found a symbol that covers " 715 "several histogram records"), 716 whoami); 717 done (1); 718 } 719 720 found = 1; 721 *p_lowpc = common_low; 722 *p_highpc = common_high; 723 } 724 } 725 726 if (!found) 727 *p_highpc = *p_lowpc; 728 } 729 730 /* Find and return existing histogram record having the same lowpc and 731 highpc as passed via the parameters. Return NULL if nothing is found. 732 The return value is valid until any new histogram is read. */ 733 static histogram * 734 find_histogram (bfd_vma lowpc, bfd_vma highpc) 735 { 736 unsigned i; 737 for (i = 0; i < num_histograms; ++i) 738 { 739 if (histograms[i].lowpc == lowpc && histograms[i].highpc == highpc) 740 return &histograms[i]; 741 } 742 return 0; 743 } 744 745 /* Given a PC, return histogram record which address range include this PC. 746 Return NULL if there's no such record. */ 747 static histogram * 748 find_histogram_for_pc (bfd_vma pc) 749 { 750 unsigned i; 751 for (i = 0; i < num_histograms; ++i) 752 { 753 if (histograms[i].lowpc <= pc && pc < histograms[i].highpc) 754 return &histograms[i]; 755 } 756 return 0; 757 } 758