]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgcc/libgcov-util.c
Add gcov-tool: an offline gcda profile processing tool Support.
[thirdparty/gcc.git] / libgcc / libgcov-util.c
1 /* Utility functions for reading gcda files into in-memory
2 gcov_info structures and offline profile processing. */
3 /* Copyright (C) 2014 Free Software Foundation, Inc.
4 Contributed by Rong Xu <xur@google.com>.
5
6 This file is part of GCC.
7
8 GCC is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 3, or (at your option) any later
11 version.
12
13 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
21
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. */
26
27
28 #define IN_GCOV_TOOL 1
29
30 #include "libgcov.h"
31 #include "intl.h"
32 #include "diagnostic.h"
33 #include "version.h"
34 #include "demangle.h"
35
36 /* Borrowed from basic-block.h. */
37 #define RDIV(X,Y) (((X) + (Y) / 2) / (Y))
38
39 extern gcov_position_t gcov_position();
40 extern int gcov_is_error();
41 extern gcov_unsigned_t gcov_max_filename;
42
43 /* Verbose mode for debug. */
44 static int verbose;
45
46 /* Set verbose flag. */
47 void gcov_set_verbose (void)
48 {
49 verbose = 1;
50 }
51
52 /* The following part is to read Gcda and reconstruct GCOV_INFO. */
53
54 #include "obstack.h"
55 #include <unistd.h>
56 #include <ftw.h>
57
58 static void tag_function (unsigned, unsigned);
59 static void tag_blocks (unsigned, unsigned);
60 static void tag_arcs (unsigned, unsigned);
61 static void tag_lines (unsigned, unsigned);
62 static void tag_counters (unsigned, unsigned);
63 static void tag_summary (unsigned, unsigned);
64
65 /* The gcov_info for the first module. */
66 static struct gcov_info *curr_gcov_info;
67 /* The gcov_info being processed. */
68 static struct gcov_info *gcov_info_head;
69 /* This variable contains all the functions in current module. */
70 static struct obstack fn_info;
71 /* The function being processed. */
72 static struct gcov_fn_info *curr_fn_info;
73 /* The number of functions seen so far. */
74 static unsigned num_fn_info;
75 /* This variable contains all the counters for current module. */
76 static int k_ctrs_mask[GCOV_COUNTERS];
77 /* The kind of counters that have been seen. */
78 static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS];
79 /* Number of kind of counters that have been seen. */
80 static int k_ctrs_types;
81 /* The longest length of all the filenames. */
82 static int max_filename_len;
83
84 /* Merge functions for counters. */
85 #define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) __gcov_merge ## FN_TYPE,
86 static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = {
87 #include "gcov-counter.def"
88 };
89 #undef DEF_GCOV_COUNTER
90
91 /* Set the ctrs field in gcov_fn_info object FN_INFO. */
92
93 static void
94 set_fn_ctrs (struct gcov_fn_info *fn_info)
95 {
96 int j = 0, i;
97
98 for (i = 0; i < GCOV_COUNTERS; i++)
99 {
100 if (k_ctrs_mask[i] == 0)
101 continue;
102 fn_info->ctrs[j].num = k_ctrs[i].num;
103 fn_info->ctrs[j].values = k_ctrs[i].values;
104 j++;
105 }
106 if (k_ctrs_types == 0)
107 k_ctrs_types = j;
108 else
109 gcc_assert (j == k_ctrs_types);
110 }
111
112 /* For each tag in gcda file, we have an entry here.
113 TAG is the tag value; NAME is the tag name; and
114 PROC is the handler function. */
115
116 typedef struct tag_format
117 {
118 unsigned tag;
119 char const *name;
120 void (*proc) (unsigned, unsigned);
121 } tag_format_t;
122
123 /* Handler table for various Tags. */
124
125 static const tag_format_t tag_table[] =
126 {
127 {0, "NOP", NULL},
128 {0, "UNKNOWN", NULL},
129 {0, "COUNTERS", tag_counters},
130 {GCOV_TAG_FUNCTION, "FUNCTION", tag_function},
131 {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks},
132 {GCOV_TAG_ARCS, "ARCS", tag_arcs},
133 {GCOV_TAG_LINES, "LINES", tag_lines},
134 {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
135 {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
136 {0, NULL, NULL}
137 };
138
139 /* Handler for reading function tag. */
140
141 static void
142 tag_function (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
143 {
144 int i;
145
146 /* write out previous fn_info. */
147 if (num_fn_info)
148 {
149 set_fn_ctrs (curr_fn_info);
150 obstack_ptr_grow (&fn_info, curr_fn_info);
151 }
152
153 /* Here we over allocate a bit, using GCOV_COUNTERS instead of the actual active
154 counter types. */
155 curr_fn_info = (struct gcov_fn_info *) xcalloc (sizeof (struct gcov_fn_info)
156 + GCOV_COUNTERS * sizeof (struct gcov_ctr_info), 1);
157
158 for (i = 0; i < GCOV_COUNTERS; i++)
159 k_ctrs[i].num = 0;
160 k_ctrs_types = 0;
161
162 curr_fn_info->key = curr_gcov_info;
163 curr_fn_info->ident = gcov_read_unsigned ();
164 curr_fn_info->lineno_checksum = gcov_read_unsigned ();
165 curr_fn_info->cfg_checksum = gcov_read_unsigned ();
166 num_fn_info++;
167
168 if (verbose)
169 fnotice (stdout, "tag one function id=%d\n", curr_fn_info->ident);
170 }
171
172 /* Handler for reading block tag. */
173
174 static void
175 tag_blocks (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
176 {
177 /* TBD: gcov-tool currently does not handle gcno files. Assert here. */
178 gcc_unreachable ();
179 }
180
181 /* Handler for reading flow arc tag. */
182
183 static void
184 tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
185 {
186 /* TBD: gcov-tool currently does not handle gcno files. Assert here. */
187 gcc_unreachable ();
188 }
189
190 /* Handler for reading line tag. */
191
192 static void
193 tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
194 {
195 /* TBD: gcov-tool currently does not handle gcno files. Assert here. */
196 gcc_unreachable ();
197 }
198
199 /* Handler for reading counters array tag with value as TAG and length of LENGTH. */
200
201 static void
202 tag_counters (unsigned tag, unsigned length)
203 {
204 unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
205 gcov_type *values;
206 unsigned ix;
207 unsigned tag_ix;
208
209 tag_ix = GCOV_COUNTER_FOR_TAG (tag);
210 gcc_assert (tag_ix < GCOV_COUNTERS);
211 k_ctrs_mask [tag_ix] = 1;
212 gcc_assert (k_ctrs[tag_ix].num == 0);
213 k_ctrs[tag_ix].num = n_counts;
214
215 k_ctrs[tag_ix].values = values = (gcov_type *) xmalloc (n_counts * sizeof (gcov_type));
216 gcc_assert (values);
217
218 for (ix = 0; ix != n_counts; ix++)
219 values[ix] = gcov_read_counter ();
220 }
221
222 /* Handler for reading summary tag. */
223
224 static void
225 tag_summary (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
226 {
227 struct gcov_summary summary;
228
229 gcov_read_summary (&summary);
230 }
231
232 /* This function is called at the end of reading a gcda file.
233 It flushes the contents in curr_fn_info to gcov_info object OBJ_INFO. */
234
235 static void
236 read_gcda_finalize (struct gcov_info *obj_info)
237 {
238 int i;
239
240 set_fn_ctrs (curr_fn_info);
241 obstack_ptr_grow (&fn_info, curr_fn_info);
242
243 /* We set the following fields: merge, n_functions, and functions. */
244 obj_info->n_functions = num_fn_info;
245 obj_info->functions = (const struct gcov_fn_info**) obstack_finish (&fn_info);
246
247 /* wrap all the counter array. */
248 for (i=0; i< GCOV_COUNTERS; i++)
249 {
250 if (k_ctrs_mask[i])
251 obj_info->merge[i] = ctr_merge_functions[i];
252 }
253 }
254
255 /* Read the content of a gcda file FILENAME, and return a gcov_info data structure.
256 Program level summary CURRENT_SUMMARY will also be updated. */
257
258 static struct gcov_info *
259 read_gcda_file (const char *filename)
260 {
261 unsigned tags[4];
262 unsigned depth = 0;
263 unsigned magic, version;
264 struct gcov_info *obj_info;
265 int i;
266
267 for (i=0; i< GCOV_COUNTERS; i++)
268 k_ctrs_mask[i] = 0;
269 k_ctrs_types = 0;
270
271 if (!gcov_open (filename))
272 {
273 fnotice (stderr, "%s:cannot open\n", filename);
274 return NULL;
275 }
276
277 /* Read magic. */
278 magic = gcov_read_unsigned ();
279 if (magic != GCOV_DATA_MAGIC)
280 {
281 fnotice (stderr, "%s:not a gcov data file\n", filename);
282 gcov_close ();
283 return NULL;
284 }
285
286 /* Read version. */
287 version = gcov_read_unsigned ();
288 if (version != GCOV_VERSION)
289 {
290 fnotice (stderr, "%s:incorrect gcov version %d vs %d \n", filename, version, GCOV_VERSION);
291 gcov_close ();
292 return NULL;
293 }
294
295 /* Instantiate a gcov_info object. */
296 curr_gcov_info = obj_info = (struct gcov_info *) xcalloc (sizeof (struct gcov_info) +
297 sizeof (struct gcov_ctr_info) * GCOV_COUNTERS, 1);
298
299 obj_info->version = version;
300 obstack_init (&fn_info);
301 num_fn_info = 0;
302 curr_fn_info = 0;
303 {
304 char *str_dup = (char*) xmalloc (strlen (filename) + 1);
305 int len;
306
307 strcpy (str_dup, filename);
308 obj_info->filename = str_dup;
309 if ((len = strlen (filename)) > max_filename_len)
310 max_filename_len = len;
311 }
312
313 /* Read stamp. */
314 obj_info->stamp = gcov_read_unsigned ();
315
316 while (1)
317 {
318 gcov_position_t base;
319 unsigned tag, length;
320 tag_format_t const *format;
321 unsigned tag_depth;
322 int error;
323 unsigned mask;
324
325 tag = gcov_read_unsigned ();
326 if (!tag)
327 break;
328 length = gcov_read_unsigned ();
329 base = gcov_position ();
330 mask = GCOV_TAG_MASK (tag) >> 1;
331 for (tag_depth = 4; mask; mask >>= 8)
332 {
333 if (((mask & 0xff) != 0xff))
334 {
335 warning (0, "%s:tag `%x' is invalid\n", filename, tag);
336 break;
337 }
338 tag_depth--;
339 }
340 for (format = tag_table; format->name; format++)
341 if (format->tag == tag)
342 goto found;
343 format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1];
344 found:;
345 if (tag)
346 {
347 if (depth && depth < tag_depth)
348 {
349 if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag))
350 warning (0, "%s:tag `%x' is incorrectly nested\n",
351 filename, tag);
352 }
353 depth = tag_depth;
354 tags[depth - 1] = tag;
355 }
356
357 if (format->proc)
358 {
359 unsigned long actual_length;
360
361 (*format->proc) (tag, length);
362
363 actual_length = gcov_position () - base;
364 if (actual_length > length)
365 warning (0, "%s:record size mismatch %lu bytes overread\n",
366 filename, actual_length - length);
367 else if (length > actual_length)
368 warning (0, "%s:record size mismatch %lu bytes unread\n",
369 filename, length - actual_length);
370 }
371
372 gcov_sync (base, length);
373 if ((error = gcov_is_error ()))
374 {
375 warning (0, error < 0 ? "%s:counter overflow at %lu\n" :
376 "%s:read error at %lu\n", filename,
377 (long unsigned) gcov_position ());
378 break;
379 }
380 }
381
382 read_gcda_finalize (obj_info);
383 gcov_close ();
384
385 return obj_info;
386 }
387
388 /* This will be called by ftw(). It opens and read a gcda file FILENAME.
389 Return a non-zero value to stop the tree walk. */
390
391 static int
392 ftw_read_file (const char *filename,
393 const struct stat *status ATTRIBUTE_UNUSED,
394 int type)
395 {
396 int filename_len;
397 int suffix_len;
398 struct gcov_info *obj_info;
399
400 /* Only read regular files. */
401 if (type != FTW_F)
402 return 0;
403
404 filename_len = strlen (filename);
405 suffix_len = strlen (GCOV_DATA_SUFFIX);
406
407 if (filename_len <= suffix_len)
408 return 0;
409
410 if (strcmp(filename + filename_len - suffix_len, GCOV_DATA_SUFFIX))
411 return 0;
412
413 if (verbose)
414 fnotice (stderr, "reading file: %s\n", filename);
415
416 obj_info = read_gcda_file (filename);
417 if (!obj_info)
418 return 0;
419
420 obj_info->next = gcov_info_head;
421 gcov_info_head = obj_info;
422
423 return 0;
424 }
425
426 /* Initializer for reading a profile dir. */
427
428 static inline void
429 read_profile_dir_init (void)
430 {
431 gcov_info_head = 0;
432 }
433
434 /* Driver for read a profile directory and convert into gcov_info list in memory.
435 Return NULL on error,
436 Return the head of gcov_info list on success.
437 Note the file static variable GCOV_MAX_FILENAME is also set. */
438
439 struct gcov_info *
440 gcov_read_profile_dir (const char* dir_name, int recompute_summary ATTRIBUTE_UNUSED)
441 {
442 char *pwd;
443 int ret;
444
445 read_profile_dir_init ();
446
447 if (access (dir_name, R_OK) != 0)
448 {
449 fnotice (stderr, "cannot access directory %s\n", dir_name);
450 return NULL;
451 }
452 pwd = getcwd (NULL, 0);
453 gcc_assert (pwd);
454 ret = chdir (dir_name);
455 if (ret !=0)
456 {
457 fnotice (stderr, "%s is not a directory\n", dir_name);
458 return NULL;
459 }
460 ftw (".", ftw_read_file, 50);
461 ret = chdir (pwd);
462 free (pwd);
463
464
465 /* gcov_max_filename is defined in libgcov.c that records the
466 max filename len. We need to set it here to allocate the
467 array for dumping. */
468 gcov_max_filename = max_filename_len;
469
470 return gcov_info_head;;
471 }
472
473 /* This part of the code is to merge profile counters. These
474 variables are set in merge_wrapper and to be used by
475 global function gcov_read_counter_mem() and gcov_get_merge_weight. */
476
477 /* We save the counter value address to this variable. */
478 static gcov_type *gcov_value_buf;
479
480 /* The number of counter values to be read by current merging. */
481 static gcov_unsigned_t gcov_value_buf_size;
482
483 /* The index of counter values being read. */
484 static gcov_unsigned_t gcov_value_buf_pos;
485
486 /* The weight of current merging. */
487 static unsigned gcov_merge_weight;
488
489 /* Read a counter value from gcov_value_buf array. */
490
491 gcov_type
492 gcov_read_counter_mem (void)
493 {
494 gcov_type ret;
495 gcc_assert (gcov_value_buf_pos < gcov_value_buf_size);
496 ret = *(gcov_value_buf + gcov_value_buf_pos);
497 ++gcov_value_buf_pos;
498 return ret;
499 }
500
501 /* Return the recorded merge weight. */
502
503 unsigned
504 gcov_get_merge_weight (void)
505 {
506 return gcov_merge_weight;
507 }
508
509 /* A wrapper function for merge functions. It sets up the
510 value buffer and weights and then calls the merge function. */
511
512 static void
513 merge_wrapper (gcov_merge_fn f, gcov_type *v1, gcov_unsigned_t n,
514 gcov_type *v2, unsigned w)
515 {
516 gcov_value_buf = v2;
517 gcov_value_buf_pos = 0;
518 gcov_value_buf_size = n;
519 gcov_merge_weight = w;
520 (*f) (v1, n);
521 }
522
523 /* Offline tool to manipulate profile data.
524 This tool targets on matched profiles. But it has some tolerance on
525 unmatched profiles.
526 When merging p1 to p2 (p2 is the dst),
527 * m.gcda in p1 but not in p2: append m.gcda to p2 with specified weight;
528 emit warning
529 * m.gcda in p2 but not in p1: keep m.gcda in p2 and multiply by
530 specified weight; emit warning.
531 * m.gcda in both p1 and p2:
532 ** p1->m.gcda->f checksum matches p2->m.gcda->f: simple merge.
533 ** p1->m.gcda->f checksum does not matches p2->m.gcda->f: keep
534 p2->m.gcda->f and
535 drop p1->m.gcda->f. A warning is emitted. */
536
537 /* Add INFO2's counter to INFO1, multiplying by weight W. */
538
539 static int
540 gcov_merge (struct gcov_info *info1, struct gcov_info *info2, int w)
541 {
542 unsigned f_ix;
543 unsigned n_functions = info1->n_functions;
544 int has_mismatch = 0;
545
546 gcc_assert (info2->n_functions == n_functions);
547 for (f_ix = 0; f_ix < n_functions; f_ix++)
548 {
549 unsigned t_ix;
550 const struct gcov_fn_info *gfi_ptr1 = info1->functions[f_ix];
551 const struct gcov_fn_info *gfi_ptr2 = info2->functions[f_ix];
552 const struct gcov_ctr_info *ci_ptr1, *ci_ptr2;
553
554 if (!gfi_ptr1 || gfi_ptr1->key != info1)
555 continue;
556 if (!gfi_ptr2 || gfi_ptr2->key != info2)
557 continue;
558
559 if (gfi_ptr1->cfg_checksum != gfi_ptr2->cfg_checksum)
560 {
561 fnotice (stderr, "in %s, cfg_checksum mismatch, skipping\n",
562 info1->filename);
563 has_mismatch = 1;
564 continue;
565 }
566 ci_ptr1 = gfi_ptr1->ctrs;
567 ci_ptr2 = gfi_ptr2->ctrs;
568 for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
569 {
570 gcov_merge_fn merge1 = info1->merge[t_ix];
571 gcov_merge_fn merge2 = info2->merge[t_ix];
572
573 gcc_assert (merge1 == merge2);
574 if (!merge1)
575 continue;
576 gcc_assert (ci_ptr1->num == ci_ptr2->num);
577 merge_wrapper (merge1, ci_ptr1->values, ci_ptr1->num, ci_ptr2->values, w);
578 ci_ptr1++;
579 ci_ptr2++;
580 }
581 }
582
583 return has_mismatch;
584 }
585
586 /* Find and return the match gcov_info object for INFO from ARRAY.
587 SIZE is the length of ARRAY.
588 Return NULL if there is no match. */
589
590 static struct gcov_info *
591 find_match_gcov_info (struct gcov_info **array, int size, struct gcov_info *info)
592 {
593 struct gcov_info *gi_ptr;
594 struct gcov_info *ret = NULL;
595 int i;
596
597 for (i = 0; i < size; i++)
598 {
599 gi_ptr = array[i];
600 if (gi_ptr == 0)
601 continue;
602 if (!strcmp (gi_ptr->filename, info->filename))
603 {
604 ret = gi_ptr;
605 array[i] = 0;
606 break;
607 }
608 }
609
610 if (ret && ret->n_functions != info->n_functions)
611 {
612 fnotice (stderr, "mismatched profiles in %s (%d functions"
613 " vs %d functions)\n",
614 ret->filename,
615 ret->n_functions,
616 info->n_functions);
617 ret = NULL;
618 }
619 return ret;
620 }
621
622 /* Merge the list of gcov_info objects from SRC_PROFILE to TGT_PROFILE.
623 Return 0 on success: without mismatch.
624 Reutrn 1 on error. */
625
626 int
627 gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile,
628 int w1, int w2)
629 {
630 struct gcov_info *gi_ptr;
631 struct gcov_info **tgt_infos;
632 struct gcov_info *tgt_tail;
633 struct gcov_info **in_src_not_tgt;
634 unsigned tgt_cnt = 0, src_cnt = 0;
635 unsigned unmatch_info_cnt = 0;
636 unsigned int i;
637
638 for (gi_ptr = tgt_profile; gi_ptr; gi_ptr = gi_ptr->next)
639 tgt_cnt++;
640 for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next)
641 src_cnt++;
642 tgt_infos = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *)
643 * tgt_cnt);
644 gcc_assert (tgt_infos);
645 in_src_not_tgt = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *)
646 * src_cnt);
647 gcc_assert (in_src_not_tgt);
648
649 for (gi_ptr = tgt_profile, i = 0; gi_ptr; gi_ptr = gi_ptr->next, i++)
650 tgt_infos[i] = gi_ptr;
651
652 tgt_tail = tgt_infos[tgt_cnt - 1];
653
654 /* First pass on tgt_profile, we multiply w1 to all counters. */
655 if (w1 > 1)
656 {
657 for (i = 0; i < tgt_cnt; i++)
658 gcov_merge (tgt_infos[i], tgt_infos[i], w1-1);
659 }
660
661 /* Second pass, add src_profile to the tgt_profile. */
662 for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next)
663 {
664 struct gcov_info *gi_ptr1;
665
666 gi_ptr1 = find_match_gcov_info (tgt_infos, tgt_cnt, gi_ptr);
667 if (gi_ptr1 == NULL)
668 {
669 in_src_not_tgt[unmatch_info_cnt++] = gi_ptr;
670 continue;
671 }
672 gcov_merge (gi_ptr1, gi_ptr, w2);
673 }
674
675 /* For modules in src but not in tgt. We adjust the counter and append. */
676 for (i = 0; i < unmatch_info_cnt; i++)
677 {
678 gi_ptr = in_src_not_tgt[i];
679 gcov_merge (gi_ptr, gi_ptr, w2 - 1);
680 tgt_tail->next = gi_ptr;
681 tgt_tail = gi_ptr;
682 }
683
684 return 0;
685 }
686
687 typedef gcov_type (*counter_op_fn) (gcov_type, void*, void*);
688
689 /* Performing FN upon arc counters. */
690
691 static void
692 __gcov_add_counter_op (gcov_type *counters, unsigned n_counters,
693 counter_op_fn fn, void *data1, void *data2)
694 {
695 for (; n_counters; counters++, n_counters--)
696 {
697 gcov_type val = *counters;
698 *counters = fn(val, data1, data2);
699 }
700 }
701
702 /* Performing FN upon ior counters. */
703
704 static void
705 __gcov_ior_counter_op (gcov_type *counters ATTRIBUTE_UNUSED,
706 unsigned n_counters ATTRIBUTE_UNUSED,
707 counter_op_fn fn ATTRIBUTE_UNUSED,
708 void *data1 ATTRIBUTE_UNUSED,
709 void *data2 ATTRIBUTE_UNUSED)
710 {
711 /* Do nothing. */
712 }
713
714 /* Performing FN upon time-profile counters. */
715
716 static void
717 __gcov_time_profile_counter_op (gcov_type *counters ATTRIBUTE_UNUSED,
718 unsigned n_counters ATTRIBUTE_UNUSED,
719 counter_op_fn fn ATTRIBUTE_UNUSED,
720 void *data1 ATTRIBUTE_UNUSED,
721 void *data2 ATTRIBUTE_UNUSED)
722 {
723 /* Do nothing. */
724 }
725
726 /* Performaing FN upon delta counters. */
727
728 static void
729 __gcov_delta_counter_op (gcov_type *counters, unsigned n_counters,
730 counter_op_fn fn, void *data1, void *data2)
731 {
732 unsigned i, n_measures;
733
734 gcc_assert (!(n_counters % 4));
735 n_measures = n_counters / 4;
736 for (i = 0; i < n_measures; i++, counters += 4)
737 {
738 counters[2] = fn (counters[2], data1, data2);
739 counters[3] = fn (counters[3], data1, data2);
740 }
741 }
742
743 /* Performing FN upon single counters. */
744
745 static void
746 __gcov_single_counter_op (gcov_type *counters, unsigned n_counters,
747 counter_op_fn fn, void *data1, void *data2)
748 {
749 unsigned i, n_measures;
750
751 gcc_assert (!(n_counters % 3));
752 n_measures = n_counters / 3;
753 for (i = 0; i < n_measures; i++, counters += 3)
754 {
755 counters[1] = fn (counters[1], data1, data2);
756 counters[2] = fn (counters[2], data1, data2);
757 }
758 }
759
760 /* Scaling the counter value V by multiplying *(float*) DATA1. */
761
762 static gcov_type
763 fp_scale (gcov_type v, void *data1, void *data2 ATTRIBUTE_UNUSED)
764 {
765 float f = *(float *) data1;
766 return (gcov_type) (v * f);
767 }
768
769 /* Scaling the counter value V by multiplying DATA2/DATA1. */
770
771 static gcov_type
772 int_scale (gcov_type v, void *data1, void *data2)
773 {
774 int n = *(int *) data1;
775 int d = *(int *) data2;
776 return (gcov_type) ( RDIV (v,d) * n);
777 }
778
779 /* Type of function used to process counters. */
780 typedef void (*gcov_counter_fn) (gcov_type *, gcov_unsigned_t,
781 counter_op_fn, void *, void *);
782
783 /* Function array to process profile counters. */
784 #define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) \
785 __gcov ## FN_TYPE ## _counter_op,
786 static gcov_counter_fn ctr_functions[GCOV_COUNTERS] = {
787 #include "gcov-counter.def"
788 };
789 #undef DEF_GCOV_COUNTER
790
791 /* Driver for scaling profile counters. */
792
793 int
794 gcov_profile_scale (struct gcov_info *profile, float scale_factor, int n, int d)
795 {
796 struct gcov_info *gi_ptr;
797 unsigned f_ix;
798
799 if (verbose)
800 fnotice (stdout, "scale_factor is %f or %d/%d\n", scale_factor, n, d);
801
802 /* Scaling the counters. */
803 for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
804 for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
805 {
806 unsigned t_ix;
807 const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
808 const struct gcov_ctr_info *ci_ptr;
809
810 if (!gfi_ptr || gfi_ptr->key != gi_ptr)
811 continue;
812
813 ci_ptr = gfi_ptr->ctrs;
814 for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
815 {
816 gcov_merge_fn merge = gi_ptr->merge[t_ix];
817
818 if (!merge)
819 continue;
820 if (d == 0)
821 (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num,
822 fp_scale, &scale_factor, NULL);
823 else
824 (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num,
825 int_scale, &n, &d);
826 ci_ptr++;
827 }
828 }
829
830 return 0;
831 }
832
833 /* Driver to normalize profile counters. */
834
835 int
836 gcov_profile_normalize (struct gcov_info *profile, gcov_type max_val)
837 {
838 struct gcov_info *gi_ptr;
839 gcov_type curr_max_val = 0;
840 unsigned f_ix;
841 unsigned int i;
842 float scale_factor;
843
844 /* Find the largest count value. */
845 for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
846 for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
847 {
848 unsigned t_ix;
849 const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
850 const struct gcov_ctr_info *ci_ptr;
851
852 if (!gfi_ptr || gfi_ptr->key != gi_ptr)
853 continue;
854
855 ci_ptr = gfi_ptr->ctrs;
856 for (t_ix = 0; t_ix < 1; t_ix++)
857 {
858 for (i = 0; i < ci_ptr->num; i++)
859 if (ci_ptr->values[i] > curr_max_val)
860 curr_max_val = ci_ptr->values[i];
861 ci_ptr++;
862 }
863 }
864
865 scale_factor = (float)max_val / curr_max_val;
866 if (verbose)
867 fnotice (stdout, "max_val is %lld\n", (long long) curr_max_val);
868
869 return gcov_profile_scale (profile, scale_factor, 0, 0);
870 }