hist.c revision 1.1 1 /* hist.c - Histogram related operations.
2
3 Copyright 1999, 2000, 2001, 2002, 2004, 2005, 2007, 2009
4 Free Software Foundation, Inc.
5
6 This file is part of GNU Binutils.
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
21 02110-1301, USA. */
22
23 #include "gprof.h"
25 #include "libiberty.h"
26 #include "search_list.h"
27 #include "source.h"
28 #include "symtab.h"
29 #include "corefile.h"
30 #include "gmon_io.h"
31 #include "gmon_out.h"
32 #include "hist.h"
33 #include "sym_ids.h"
34 #include "utils.h"
35 #include "math.h"
36 #include "stdio.h"
37 #include "stdlib.h"
38
39 #define UNITS_TO_CODE (offset_to_code / sizeof(UNIT))
40
41 static void scale_and_align_entries (void);
42 static void print_header (int);
43 static void print_line (Sym *, double);
44 static int cmp_time (const PTR, const PTR);
45
46 /* Declarations of automatically generated functions to output blurbs. */
47 extern void flat_blurb (FILE * fp);
48
49 static histogram *find_histogram (bfd_vma lowpc, bfd_vma highpc);
50 static histogram *find_histogram_for_pc (bfd_vma pc);
51
52 histogram * histograms;
53 unsigned num_histograms;
54 double hist_scale;
55 static char hist_dimension[16] = "seconds";
56 static char hist_dimension_abbrev = 's';
57
58 static double accum_time; /* Accumulated time so far for print_line(). */
59 static double total_time; /* Total time for all routines. */
60
61 /* Table of SI prefixes for powers of 10 (used to automatically
62 scale some of the values in the flat profile). */
63 const struct
64 {
65 char prefix;
66 double scale;
67 }
68 SItab[] =
69 {
70 { 'T', 1e-12 }, /* tera */
71 { 'G', 1e-09 }, /* giga */
72 { 'M', 1e-06 }, /* mega */
73 { 'K', 1e-03 }, /* kilo */
74 { ' ', 1e-00 },
75 { 'm', 1e+03 }, /* milli */
76 { 'u', 1e+06 }, /* micro */
77 { 'n', 1e+09 }, /* nano */
78 { 'p', 1e+12 }, /* pico */
79 { 'f', 1e+15 }, /* femto */
80 { 'a', 1e+18 } /* ato */
81 };
82
83 /* Reads just the header part of histogram record into
84 *RECORD from IFP. FILENAME is the name of IFP and
85 is provided for formatting error messages only.
86
87 If FIRST is non-zero, sets global variables HZ, HIST_DIMENSION,
88 HIST_DIMENSION_ABBREV, HIST_SCALE. If FIRST is zero, checks
89 that the new histogram is compatible with already-set values
90 of those variables and emits an error if that's not so. */
91 static void
92 read_histogram_header (histogram *record,
93 FILE *ifp, const char *filename,
94 int first)
95 {
96 unsigned int profrate;
97 char n_hist_dimension[15];
98 char n_hist_dimension_abbrev;
99 double n_hist_scale;
100
101 if (gmon_io_read_vma (ifp, &record->lowpc)
102 || gmon_io_read_vma (ifp, &record->highpc)
103 || gmon_io_read_32 (ifp, &record->num_bins)
104 || gmon_io_read_32 (ifp, &profrate)
105 || gmon_io_read (ifp, n_hist_dimension, 15)
106 || gmon_io_read (ifp, &n_hist_dimension_abbrev, 1))
107 {
108 fprintf (stderr, _("%s: %s: unexpected end of file\n"),
109 whoami, filename);
110
111 done (1);
112 }
113
114 n_hist_scale = (double)((record->highpc - record->lowpc) / sizeof (UNIT))
115 / record->num_bins;
116
117 if (first)
118 {
119 /* We don't try to veryfy profrate is the same for all histogram
120 records. If we have two histogram records for the same
121 address range and profiling samples is done as often
122 as possible as opposed on timer, then the actual profrate will
123 be slightly different. Most of the time the difference does not
124 matter and insisting that profiling rate is exactly the same
125 will only create inconvenient. */
126 hz = profrate;
127 memcpy (hist_dimension, n_hist_dimension, 15);
128 hist_dimension_abbrev = n_hist_dimension_abbrev;
129 hist_scale = n_hist_scale;
130 }
131 else
132 {
133 if (strncmp (n_hist_dimension, hist_dimension, 15) != 0)
134 {
135 fprintf (stderr,
136 _("%s: dimension unit changed between histogram records\n"
137 "%s: from '%s'\n"
138 "%s: to '%s'\n"),
139 whoami, whoami, hist_dimension, whoami, n_hist_dimension);
140 done (1);
141 }
142
143 if (n_hist_dimension_abbrev != hist_dimension_abbrev)
144 {
145 fprintf (stderr,
146 _("%s: dimension abbreviation changed between histogram records\n"
147 "%s: from '%c'\n"
148 "%s: to '%c'\n"),
149 whoami, whoami, hist_dimension_abbrev, whoami, n_hist_dimension_abbrev);
150 done (1);
151 }
152
153 /* The only reason we require the same scale for histograms is that
154 there's code (notably printing code), that prints units,
155 and it would be very confusing to have one unit mean different
156 things for different functions. */
157 if (fabs (hist_scale - n_hist_scale) > 0.000001)
158 {
159 fprintf (stderr,
160 _("%s: different scales in histogram records"),
161 whoami);
162 done (1);
163 }
164 }
165 }
166
167 /* Read the histogram from file IFP. FILENAME is the name of IFP and
168 is provided for formatting error messages only. */
169
170 void
171 hist_read_rec (FILE * ifp, const char *filename)
172 {
173 bfd_vma lowpc, highpc;
174 histogram n_record;
175 histogram *record, *existing_record;
176 unsigned i;
177
178 /* 1. Read the header and see if there's existing record for the
179 same address range and that there are no overlapping records. */
180 read_histogram_header (&n_record, ifp, filename, num_histograms == 0);
181
182 existing_record = find_histogram (n_record.lowpc, n_record.highpc);
183 if (existing_record)
184 {
185 record = existing_record;
186 }
187 else
188 {
189 /* If this record overlaps, but does not completely match an existing
190 record, it's an error. */
191 lowpc = n_record.lowpc;
192 highpc = n_record.highpc;
193 hist_clip_symbol_address (&lowpc, &highpc);
194 if (lowpc != highpc)
195 {
196 fprintf (stderr,
197 _("%s: overlapping histogram records\n"),
198 whoami);
199 done (1);
200 }
201
202 /* This is new record. Add it to global array and allocate space for
203 the samples. */
204 histograms = (struct histogram *)
205 xrealloc (histograms, sizeof (histogram) * (num_histograms + 1));
206 memcpy (histograms + num_histograms,
207 &n_record, sizeof (histogram));
208 record = &histograms[num_histograms];
209 ++num_histograms;
210
211 record->sample = (int *) xmalloc (record->num_bins
212 * sizeof (record->sample[0]));
213 memset (record->sample, 0, record->num_bins * sizeof (record->sample[0]));
214 }
215
216 /* 2. We have either a new record (with zeroed histogram data), or an existing
217 record with some data in the histogram already. Read new data into the
218 record, adding hit counts. */
219
220 DBG (SAMPLEDEBUG,
221 printf ("[hist_read_rec] n_lowpc 0x%lx n_highpc 0x%lx ncnt %u\n",
222 (unsigned long) record->lowpc, (unsigned long) record->highpc,
223 record->num_bins));
224
225 for (i = 0; i < record->num_bins; ++i)
226 {
227 UNIT count;
228 if (fread (&count[0], sizeof (count), 1, ifp) != 1)
229 {
230 fprintf (stderr,
231 _("%s: %s: unexpected EOF after reading %u of %u samples\n"),
232 whoami, filename, i, record->num_bins);
233 done (1);
234 }
235 record->sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) & count[0]);
236 DBG (SAMPLEDEBUG,
237 printf ("[hist_read_rec] 0x%lx: %u\n",
238 (unsigned long) (record->lowpc
239 + i * (record->highpc - record->lowpc)
240 / record->num_bins),
241 record->sample[i]));
242 }
243 }
244
245
246 /* Write all execution histograms file OFP. FILENAME is the name
247 of OFP and is provided for formatting error-messages only. */
248
249 void
250 hist_write_hist (FILE * ofp, const char *filename)
251 {
252 UNIT count;
253 unsigned int i, r;
254
255 for (r = 0; r < num_histograms; ++r)
256 {
257 histogram *record = &histograms[r];
258
259 /* Write header. */
260
261 if (gmon_io_write_8 (ofp, GMON_TAG_TIME_HIST)
262 || gmon_io_write_vma (ofp, record->lowpc)
263 || gmon_io_write_vma (ofp, record->highpc)
264 || gmon_io_write_32 (ofp, record->num_bins)
265 || gmon_io_write_32 (ofp, hz)
266 || gmon_io_write (ofp, hist_dimension, 15)
267 || gmon_io_write (ofp, &hist_dimension_abbrev, 1))
268 {
269 perror (filename);
270 done (1);
271 }
272
273 for (i = 0; i < record->num_bins; ++i)
274 {
275 bfd_put_16 (core_bfd, (bfd_vma) record->sample[i], (bfd_byte *) &count[0]);
276
277 if (fwrite (&count[0], sizeof (count), 1, ofp) != 1)
278 {
279 perror (filename);
280 done (1);
281 }
282 }
283 }
284 }
285
286 /* Calculate scaled entry point addresses (to save time in
287 hist_assign_samples), and, on architectures that have procedure
288 entry masks at the start of a function, possibly push the scaled
289 entry points over the procedure entry mask, if it turns out that
290 the entry point is in one bin and the code for a routine is in the
291 next bin. */
292
293 static void
294 scale_and_align_entries ()
295 {
296 Sym *sym;
297 bfd_vma bin_of_entry;
298 bfd_vma bin_of_code;
299
300 for (sym = symtab.base; sym < symtab.limit; sym++)
301 {
302 histogram *r = find_histogram_for_pc (sym->addr);
303
304 sym->hist.scaled_addr = sym->addr / sizeof (UNIT);
305
306 if (r)
307 {
308 bin_of_entry = (sym->hist.scaled_addr - r->lowpc) / hist_scale;
309 bin_of_code = ((sym->hist.scaled_addr + UNITS_TO_CODE - r->lowpc)
310 / hist_scale);
311 if (bin_of_entry < bin_of_code)
312 {
313 DBG (SAMPLEDEBUG,
314 printf ("[scale_and_align_entries] pushing 0x%lx to 0x%lx\n",
315 (unsigned long) sym->hist.scaled_addr,
316 (unsigned long) (sym->hist.scaled_addr
317 + UNITS_TO_CODE)));
318 sym->hist.scaled_addr += UNITS_TO_CODE;
319 }
320 }
321 }
322 }
323
324
325 /* Assign samples to the symbol to which they belong.
326
327 Histogram bin I covers some address range [BIN_LOWPC,BIN_HIGH_PC)
328 which may overlap one more symbol address ranges. If a symbol
329 overlaps with the bin's address range by O percent, then O percent
330 of the bin's count is credited to that symbol.
331
332 There are three cases as to where BIN_LOW_PC and BIN_HIGH_PC can be
333 with respect to the symbol's address range [SYM_LOW_PC,
334 SYM_HIGH_PC) as shown in the following diagram. OVERLAP computes
335 the distance (in UNITs) between the arrows, the fraction of the
336 sample that is to be credited to the symbol which starts at
337 SYM_LOW_PC.
338
339 sym_low_pc sym_high_pc
340 | |
341 v v
342
343 +-----------------------------------------------+
344 | |
345 | ->| |<- ->| |<- ->| |<- |
346 | | | | | |
347 +---------+ +---------+ +---------+
348
349 ^ ^ ^ ^ ^ ^
350 | | | | | |
351 bin_low_pc bin_high_pc bin_low_pc bin_high_pc bin_low_pc bin_high_pc
352
353 For the VAX we assert that samples will never fall in the first two
354 bytes of any routine, since that is the entry mask, thus we call
355 scale_and_align_entries() to adjust the entry points if the entry
356 mask falls in one bin but the code for the routine doesn't start
357 until the next bin. In conjunction with the alignment of routine
358 addresses, this should allow us to have only one sample for every
359 four bytes of text space and never have any overlap (the two end
360 cases, above). */
361
362 static void
363 hist_assign_samples_1 (histogram *r)
364 {
365 bfd_vma bin_low_pc, bin_high_pc;
366 bfd_vma sym_low_pc, sym_high_pc;
367 bfd_vma overlap, addr;
368 unsigned int bin_count;
369 unsigned int i, j, k;
370 double count_time, credit;
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_sampes_1' for all histogram records read so far. */
449 void
450 hist_assign_samples ()
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 PTR lp, const PTR 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 ()
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
575 if (first_output)
576 first_output = FALSE;
577 else
578 printf ("\f\n");
579
580 accum_time = 0.0;
581
582 if (bsd_style_output)
583 {
584 if (print_descriptions)
585 {
586 printf (_("\n\n\nflat profile:\n"));
587 flat_blurb (stdout);
588 }
589 }
590 else
591 {
592 printf (_("Flat profile:\n"));
593 }
594
595 /* Sort the symbol table by time (call-count and name as secondary
596 and tertiary keys). */
597 time_sorted_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *));
598
599 for (sym_index = 0; sym_index < symtab.len; ++sym_index)
600 time_sorted_syms[sym_index] = &symtab.base[sym_index];
601
602 qsort (time_sorted_syms, symtab.len, sizeof (Sym *), cmp_time);
603
604 if (bsd_style_output)
605 {
606 log_scale = 5; /* Milli-seconds is BSD-default. */
607 }
608 else
609 {
610 /* Search for symbol with highest per-call
611 execution time and scale accordingly. */
612 log_scale = 0;
613 top_dog = 0;
614 top_time = 0.0;
615
616 for (sym_index = 0; sym_index < symtab.len; ++sym_index)
617 {
618 sym = time_sorted_syms[sym_index];
619
620 if (sym->ncalls != 0)
621 {
622 double call_time;
623
624 call_time = (sym->hist.time + sym->cg.child_time) / sym->ncalls;
625
626 if (call_time > top_time)
627 {
628 top_dog = sym;
629 top_time = call_time;
630 }
631 }
632 }
633
634 if (top_dog && top_dog->ncalls != 0 && top_time > 0.0)
635 {
636 top_time /= hz;
637
638 for (log_scale = 0; log_scale < ARRAY_SIZE (SItab); log_scale ++)
639 {
640 double scaled_value = SItab[log_scale].scale * top_time;
641
642 if (scaled_value >= 1.0 && scaled_value < 1000.0)
643 break;
644 }
645 }
646 }
647
648 /* For now, the dimension is always seconds. In the future, we
649 may also want to support other (pseudo-)dimensions (such as
650 I-cache misses etc.). */
651 print_header (SItab[log_scale].prefix);
652
653 for (sym_index = 0; sym_index < symtab.len; ++sym_index)
654 {
655 addr = time_sorted_syms[sym_index]->addr;
656
657 /* Print symbol if its in INCL_FLAT table or that table
658 is empty and the symbol is not in EXCL_FLAT. */
659 if (sym_lookup (&syms[INCL_FLAT], addr)
660 || (syms[INCL_FLAT].len == 0
661 && !sym_lookup (&syms[EXCL_FLAT], addr)))
662 print_line (time_sorted_syms[sym_index], SItab[log_scale].scale);
663 }
664
665 free (time_sorted_syms);
666
667 if (print_descriptions && !bsd_style_output)
668 flat_blurb (stdout);
669 }
670
671 int
672 hist_check_address (unsigned address)
673 {
674 unsigned i;
675
676 for (i = 0; i < num_histograms; ++i)
677 if (histograms[i].lowpc <= address && address < histograms[i].highpc)
678 return 1;
679
680 return 0;
681 }
682
683 #if ! defined(min)
684 #define min(a,b) (((a)<(b)) ? (a) : (b))
685 #endif
686 #if ! defined(max)
687 #define max(a,b) (((a)>(b)) ? (a) : (b))
688 #endif
689
690 void
691 hist_clip_symbol_address (bfd_vma *p_lowpc, bfd_vma *p_highpc)
692 {
693 unsigned i;
694 int found = 0;
695
696 if (num_histograms == 0)
697 {
698 *p_highpc = *p_lowpc;
699 return;
700 }
701
702 for (i = 0; i < num_histograms; ++i)
703 {
704 bfd_vma common_low, common_high;
705 common_low = max (histograms[i].lowpc, *p_lowpc);
706 common_high = min (histograms[i].highpc, *p_highpc);
707
708 if (common_low < common_high)
709 {
710 if (found)
711 {
712 fprintf (stderr,
713 _("%s: found a symbol that covers "
714 "several histogram records"),
715 whoami);
716 done (1);
717 }
718
719 found = 1;
720 *p_lowpc = common_low;
721 *p_highpc = common_high;
722 }
723 }
724
725 if (!found)
726 *p_highpc = *p_lowpc;
727 }
728
729 /* Find and return exising histogram record having the same lowpc and
730 highpc as passed via the parameters. Return NULL if nothing is found.
731 The return value is valid until any new histogram is read. */
732 static histogram *
733 find_histogram (bfd_vma lowpc, bfd_vma highpc)
734 {
735 unsigned i;
736 for (i = 0; i < num_histograms; ++i)
737 {
738 if (histograms[i].lowpc == lowpc && histograms[i].highpc == highpc)
739 return &histograms[i];
740 }
741 return 0;
742 }
743
744 /* Given a PC, return histogram record which address range include this PC.
745 Return NULL if there's no such record. */
746 static histogram *
747 find_histogram_for_pc (bfd_vma pc)
748 {
749 unsigned i;
750 for (i = 0; i < num_histograms; ++i)
751 {
752 if (histograms[i].lowpc <= pc && pc < histograms[i].highpc)
753 return &histograms[i];
754 }
755 return 0;
756 }
757