On Tue, Sep 16, 2014 at 8:07 AM, Xinliang David Li <davi...@google.com> wrote: > The zero_counts array is passed to gcov_build_callgraph but not used > until the dyn-cgraph is initialized. We should avoid increasing > runtime memory overhead by not creating it if possible.
We could delay creation a little bit, until the callgraph has been initialized. But I am not sure this gains us much as it doesn't avoid having the lifetime of the array overlap with the lifetime of the callgraph. And unless fixups have been explicitly disabled or there are no comdats we will need to allocate it (so likely in almost all cases). However, I can reduce the required overhead by using an array of chars instead of ints. I measured the number of bytes in the array for a big app and it was 5.6M with the int array and 1.5M with the char array. Does this seem reasonable? To reduce it further I could encode as a bitvector as we do in the gcda file. Teresa > > David > > On Tue, Sep 16, 2014 at 7:57 AM, Teresa Johnson <tejohn...@google.com> wrote: >> On Mon, Sep 15, 2014 at 9:29 PM, Xinliang David Li <davi...@google.com> >> wrote: >>> Is it necessary to declare zero_counts array at all? Can a flag field >>> be added to dyn_cgraph_node structure to indicate if it is fixed up? >> >> The zero_counts array is used to pass info back to the caller in >> libgcov-driver.cc (dyn_cgraph_node), which is where it is allocated. >> That routine does not have access to the dyn-ipa cgraph. >> >> Teresa >> >>> >>> David >>> >>> On Fri, Sep 12, 2014 at 4:31 PM, Teresa Johnson <tejohn...@google.com> >>> wrote: >>>> This patch addresses issues when running gcov-tool after performing >>>> COMDAT fixup during dyn-ipa. Functions that were previously all zero >>>> counts are marked, and the counts are discarded when being read in >>>> by gcov-tool before recalculating module groups and summary info. >>>> >>>> While here, cleaned up the gcov-tool output (remove an overly-verbose >>>> output, >>>> make all output consistently go to stderr). >>>> >>>> Passes regression tests and manual tests. Ok for google branches? >>>> >>>> 2014-09-12 Teresa Johnson <tejohn...@google.com> >>>> >>>> * gcc/coverage.c (read_counts_file): Handle new section. >>>> * gcc/gcov.c (read_count_file): Ditto. >>>> * gcc/gcov-dump.c (dump_gcov_file): Ditto. >>>> (tag_function): Ditto. >>>> (tag_zero_fixup): New function. >>>> * gcc/gcov-io.c (gcov_read_comdat_zero_fixup): Ditto. >>>> * gcc/gcov-io.h (gcov_read_comdat_zero_fixup): Ditto. >>>> * libgcc/dyn-ipa.c (struct checksum_alias): Change flag to pointer. >>>> (new_checksum_alias): Ditto. >>>> (cfg_checksum_insert): Ditto. >>>> (checksum_set_insert): Ditto. >>>> (gcov_build_callgraph): New parameter. >>>> (gcov_collect_imported_modules): Add assert for duplicate gcda >>>> reads. >>>> (gcov_fixup_counters_checksum): Change flag to pointer to flag, >>>> set it. >>>> (__gcov_compute_module_groups): New parameter. >>>> * libgcc/libgcov-driver.c (set_gcov_fn_fixed_up): New function. >>>> (get_gcov_fn_fixed_up): Ditto. >>>> (gcov_exit_merge_gcda): Handle new section. >>>> (gcov_write_comdat_zero_fixup): Ditto. >>>> (gcov_write_build_info): Ditto. >>>> (gcov_write_comdat_zero_fixup): New function. >>>> (gcov_write_func_counters): Fix indent. >>>> (gcov_dump_module_info): Write new flag section. >>>> * libgcc/libgcov.h (gcov_get_counter): Clear fixed-up counters. >>>> (gcov_get_counter_target): Ditto. >>>> * libgcc/libgcov-util.c (tag_function): Annotate fixed-up >>>> functions, >>>> remove overly verbose output. >>>> (tag_counters): Clear fixed-up counters. >>>> (lipo_process_substitute_string_1): Send all verbose output to >>>> stderr. >>>> (tag_zero_fixup): New function. >>>> (read_gcda_file): Deallocate flag array. >>>> (gcov_profile_scale): Send all verbose output to stderr. >>>> (gcov_profile_normalize): Ditto. >>>> >>>> Index: gcc/coverage.c >>>> =================================================================== >>>> --- gcc/coverage.c (revision 215230) >>>> +++ gcc/coverage.c (working copy) >>>> @@ -820,6 +820,14 @@ read_counts_file (const char *da_file_name, unsign >>>> free (build_info_strings[i]); >>>> free (build_info_strings); >>>> } >>>> + else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP) >>>> + { >>>> + /* Zero-profile fixup flags are not used by the compiler, read >>>> and >>>> + ignore. */ >>>> + gcov_unsigned_t num_fn; >>>> + int *zero_fixup_flags = gcov_read_comdat_zero_fixup >>>> (length, &num_fn); >>>> + free (zero_fixup_flags); >>>> + } >>>> else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident) >>>> { >>>> counts_entry_t **slot, *entry, elt; >>>> Index: gcc/gcov.c >>>> =================================================================== >>>> --- gcc/gcov.c (revision 215230) >>>> +++ gcc/gcov.c (working copy) >>>> @@ -1441,6 +1441,12 @@ read_count_file (function_t *fns) >>>> free (build_info_strings[i]); >>>> free (build_info_strings); >>>> } >>>> + else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP) >>>> + { >>>> + gcov_unsigned_t num_fn; >>>> + int *zero_fixup_flags = gcov_read_comdat_zero_fixup >>>> (length, &num_fn); >>>> + free (zero_fixup_flags); >>>> + } >>>> else if (tag == GCOV_TAG_FUNCTION && !length) >>>> ; /* placeholder */ >>>> else if (tag == GCOV_TAG_FUNCTION && length == >>>> GCOV_TAG_FUNCTION_LENGTH) >>>> Index: gcc/gcov-dump.c >>>> =================================================================== >>>> --- gcc/gcov-dump.c (revision 215230) >>>> +++ gcc/gcov-dump.c (working copy) >>>> @@ -42,6 +42,7 @@ static void tag_summary (const char *, unsigned, u >>>> static void tag_module_info (const char *, unsigned, unsigned); >>>> static void dump_working_sets (const char *filename ATTRIBUTE_UNUSED, >>>> const struct gcov_ctr_summary *summary); >>>> +static void tag_zero_fixup (const char *, unsigned, unsigned); >>>> static void tag_build_info (const char *, unsigned, unsigned); >>>> extern int main (int, char **); >>>> >>>> @@ -57,6 +58,9 @@ static int flag_dump_positions = 0; >>>> static int flag_dump_aux_modules_only = 0; >>>> static int flag_dump_working_sets = 0; >>>> >>>> +static unsigned num_fn_info; >>>> +static int *zero_fixup_flags = NULL; >>>> + >>>> static const struct option options[] = >>>> { >>>> { "help", no_argument, NULL, 'h' }, >>>> @@ -79,6 +83,7 @@ static const tag_format_t tag_table[] = >>>> {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, >>>> {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, >>>> {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info}, >>>> + {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup}, >>>> {GCOV_TAG_BUILD_INFO, "BUILD INFO", tag_build_info}, >>>> {0, NULL, NULL} >>>> }; >>>> @@ -274,6 +279,8 @@ dump_gcov_file (const char *filename) >>>> printf ("%s:stamp %lu\n", filename, (unsigned long)stamp); >>>> } >>>> >>>> + num_fn_info = 0; >>>> + >>>> while (1) >>>> { >>>> gcov_position_t base, position = gcov_position (); >>>> @@ -341,6 +348,7 @@ dump_gcov_file (const char *filename) >>>> break; >>>> } >>>> } >>>> + free (zero_fixup_flags); >>>> gcov_close (); >>>> } >>>> >>>> @@ -354,7 +362,9 @@ tag_function (const char *filename ATTRIBUTE_UNUSE >>>> printf (" placeholder"); >>>> else >>>> { >>>> - printf (" ident=%u", gcov_read_unsigned ()); >>>> + int had_fixup = zero_fixup_flags && zero_fixup_flags[num_fn_info]; >>>> + printf (" ident=%u%s", gcov_read_unsigned (), >>>> + had_fixup ? " (Was 0-count COMDAT)" : ""); >>>> printf (", lineno_checksum=0x%08x", gcov_read_unsigned ()); >>>> printf (", cfg_checksum=0x%08x", gcov_read_unsigned ()); >>>> >>>> @@ -369,6 +379,7 @@ tag_function (const char *filename ATTRIBUTE_UNUSE >>>> printf (":%u", gcov_read_unsigned ()); >>>> } >>>> } >>>> + num_fn_info++; >>>> } >>>> >>>> static void >>>> @@ -600,6 +611,32 @@ tag_module_info (const char *filename ATTRIBUTE_UN >>>> } >>>> >>>> static void >>>> +tag_zero_fixup (const char *filename, >>>> + unsigned tag ATTRIBUTE_UNUSED, unsigned length) >>>> +{ >>>> + gcov_unsigned_t num_fns = 0; >>>> + zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns); >>>> + if (!zero_fixup_flags) >>>> + { >>>> + printf ("%s:error reading zero fixup flags\n", filename); >>>> + return; >>>> + } >>>> + printf (" num_fns=%u", num_fns); >>>> + for (unsigned i = 0; i < num_fns; i++) >>>> + { >>>> + if (!(i % 32)) >>>> + { >>>> + printf ("\n"); >>>> + print_prefix (filename, 0, 0); >>>> + printf ("\t\t"); >>>> + } >>>> + if (!(i % 8)) >>>> + printf ("%s%4u:", (i%32)?" ":"", i); >>>> + printf ("%u", zero_fixup_flags[i]); >>>> + } >>>> +} >>>> + >>>> +static void >>>> tag_build_info (const char *filename, >>>> unsigned tag ATTRIBUTE_UNUSED, unsigned length) >>>> { >>>> Index: gcc/gcov-io.c >>>> =================================================================== >>>> --- gcc/gcov-io.c (revision 215230) >>>> +++ gcc/gcov-io.c (working copy) >>>> @@ -691,6 +691,36 @@ gcov_read_summary (struct gcov_summary *summary) >>>> } >>>> } >>>> >>>> +/* Read LENGTH words (unsigned type) from a zero profile fixup record >>>> with the >>>> + number of function flags saved in NUM_FNS. Returns the int flag >>>> array, which >>>> + should be deallocated by caller, or NULL on error. */ >>>> + >>>> +GCOV_LINKAGE int * >>>> +gcov_read_comdat_zero_fixup (gcov_unsigned_t length, >>>> + gcov_unsigned_t *num_fns) >>>> +{ >>>> + unsigned ix, f_ix; >>>> + gcov_unsigned_t num = gcov_read_unsigned (); >>>> + /* The length consists of 1 word to hold the number of functions, >>>> + plus enough 32-bit words to hold 1 bit/function. */ >>>> + gcc_assert ((num + 31) / 32 + 1 == length); >>>> + int *zero_fixup_flags = (int *) xcalloc (num, sizeof (int)); >>>> + for (ix = 0; ix < length - 1; ix++) >>>> + { >>>> + gcov_unsigned_t bitvector = gcov_read_unsigned (); >>>> + f_ix = ix * 32; >>>> + while (bitvector) >>>> + { >>>> + if (bitvector & 0x1) >>>> + zero_fixup_flags[f_ix] = 1; >>>> + f_ix++; >>>> + bitvector >>= 1; >>>> + } >>>> + } >>>> + *num_fns = num; >>>> + return zero_fixup_flags; >>>> +} >>>> + >>>> /* Read NUM_STRINGS strings (as an unsigned array) in STRING_ARRAY, and >>>> return >>>> the number of words read. */ >>>> >>>> Index: gcc/gcov-io.h >>>> =================================================================== >>>> --- gcc/gcov-io.h (revision 215230) >>>> +++ gcc/gcov-io.h (working copy) >>>> @@ -129,7 +129,7 @@ see the files COPYING3 and COPYING.RUNTIME respect >>>> blocks they are for. >>>> >>>> The data file contains the following records. >>>> - data: {unit summary:program* build_info function-data*}* >>>> + data: {unit summary:program* build_info zero_fixup >>>> function-data*}* >>>> unit: header int32:checksum >>>> function-data: announce_function present counts >>>> announce_function: header int32:ident >>>> @@ -142,6 +142,7 @@ see the files COPYING3 and COPYING.RUNTIME respect >>>> histogram: {int32:bitvector}8 histogram-buckets* >>>> histogram-buckets: int32:num int64:min int64:sum >>>> build_info: string:info* >>>> + zero_fixup: int32:num int32:bitvector* >>>> >>>> The ANNOUNCE_FUNCTION record is the same as that in the note file, >>>> but without the source location. The COUNTS gives the >>>> @@ -158,6 +159,12 @@ see the files COPYING3 and COPYING.RUNTIME respect >>>> build. For example, it can be used to include source revision >>>> information that is useful in diagnosing profile mis-matches. >>>> >>>> + ZERO_FIXUP record contains a count of functions in the gcda file >>>> + and an array of bitvectors indexed by the function index's in the >>>> + function-data section. Each bit flags whether the function was a >>>> + COMDAT that had all-zero profiles that was fixed up by dyn-ipa >>>> + using profiles from functions with matching checksums in other modules. >>>> + >>>> This file is included by both the compiler, gcov tools and the >>>> runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to >>>> distinguish which case is which. If IN_LIBGCOV is nonzero, >>>> @@ -261,6 +268,9 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne >>>> #define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2) >>>> #define GCOV_TAG_OBJECT_SUMMARY ((gcov_unsigned_t)0xa1000000) /* >>>> Obsolete */ >>>> #define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000) >>>> +#define GCOV_TAG_COMDAT_ZERO_FIXUP ((gcov_unsigned_t)0xa9000000) >>>> +/* Ceiling divide by 32 bit word size, plus one word to hold NUM. */ >>>> +#define GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH(NUM) (1 + (NUM + 31) / 32) >>>> #define GCOV_TAG_SUMMARY_LENGTH(NUM) \ >>>> (1 + GCOV_COUNTERS_SUMMABLE * (10 + 3 * 2) + (NUM) * 5) >>>> #define GCOV_TAG_BUILD_INFO ((gcov_unsigned_t)0xa7000000) >>>> @@ -441,6 +451,9 @@ GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDE >>>> GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN; >>>> GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN; >>>> GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) >>>> ATTRIBUTE_HIDDEN; >>>> +GCOV_LINKAGE int *gcov_read_comdat_zero_fixup (gcov_unsigned_t, >>>> + gcov_unsigned_t *) >>>> + ATTRIBUTE_HIDDEN; >>>> GCOV_LINKAGE char **gcov_read_build_info (gcov_unsigned_t, >>>> gcov_unsigned_t *) >>>> ATTRIBUTE_HIDDEN; >>>> GCOV_LINKAGE const char *gcov_read_string (void); >>>> Index: libgcc/dyn-ipa.c >>>> =================================================================== >>>> --- libgcc/dyn-ipa.c (revision 215230) >>>> +++ libgcc/dyn-ipa.c (working copy) >>>> @@ -107,8 +107,9 @@ struct checksum_alias >>>> struct checksum_alias *next_alias; >>>> gcov_type guid; >>>> const struct gcov_fn_info *fi_ptr; >>>> - /* Does this function have all-zero arc counts? */ >>>> - int zero_counts; >>>> + /* Non-NULL pointer to flag if this function has all-zero arc counts, >>>> to be >>>> + set if we perform fixup. */ >>>> + int *zero_count_fixup; >>>> }; >>>> >>>> /* Module info is stored in dyn_caph->sup_modules >>>> @@ -178,10 +179,10 @@ extern gcov_unsigned_t __gcov_lipo_merge_modu_edge >>>> extern gcov_unsigned_t __gcov_lipo_weak_inclusion; >>>> >>>> #if defined(inhibit_libc) >>>> -void __gcov_build_callgraph (void) {} >>>> +void __gcov_build_callgraph (int **zero_counts) {} >>>> #else >>>> >>>> -int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN; >>>> +int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN; >>>> void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN; >>>> static void gcov_dump_callgraph (gcov_type); >>>> static void gcov_dump_cgraph_node_short (struct dyn_cgraph_node *node); >>>> @@ -378,18 +379,19 @@ lineno_checksum_get_key (const void *p) >>>> } >>>> >>>> /* Create a new checksum_alias struct for function with GUID, FI_PTR, >>>> - and ZERO_COUNTS flag. Prepends to list NEXT and returns new struct. >>>> */ >>>> + and ZERO_COUNT_FIXUP flag pointer. Prepends to list NEXT and returns >>>> + new struct. */ >>>> >>>> static struct checksum_alias * >>>> new_checksum_alias (gcov_type guid, const struct gcov_fn_info *fi_ptr, >>>> - int zero_counts, >>>> + int *zero_count_fixup, >>>> struct checksum_alias *next) >>>> { >>>> struct checksum_alias *alias = XNEW (struct checksum_alias); >>>> alias->next_alias = next; >>>> alias->fi_ptr = fi_ptr; >>>> alias->guid = guid; >>>> - alias->zero_counts = zero_counts; >>>> + alias->zero_count_fixup = zero_count_fixup; >>>> return alias; >>>> } >>>> >>>> @@ -407,11 +409,12 @@ find_cfg_checksum (struct checksum_alias_info *lis >>>> } >>>> >>>> /* Insert a new checksum_alias struct into LIST for function with >>>> - CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNTS flag. */ >>>> + CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNT_FIXUP >>>> + flag pointer. */ >>>> >>>> static struct checksum_alias_info * >>>> cfg_checksum_insert (unsigned cfg_checksum, gcov_type guid, >>>> - const struct gcov_fn_info *fi_ptr, int zero_counts, >>>> + const struct gcov_fn_info *fi_ptr, int >>>> *zero_count_fixup, >>>> struct checksum_alias_info *list) >>>> { >>>> struct checksum_alias_info *alias_info; >>>> @@ -419,7 +422,8 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t >>>> if (alias_info) >>>> { >>>> gcc_assert (alias_info->alias_list); >>>> - alias_info->alias_list = new_checksum_alias (guid, fi_ptr, >>>> zero_counts, >>>> + alias_info->alias_list = new_checksum_alias (guid, fi_ptr, >>>> + zero_count_fixup, >>>> >>>> alias_info->alias_list); >>>> return list; >>>> } >>>> @@ -428,7 +432,8 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t >>>> alias_info = XNEW (struct checksum_alias_info); >>>> alias_info->next_cfg_checksum = list; >>>> alias_info->cfg_checksum = cfg_checksum; >>>> - alias_info->alias_list = new_checksum_alias (guid, fi_ptr, >>>> zero_counts, >>>> + alias_info->alias_list = new_checksum_alias (guid, fi_ptr, >>>> + zero_count_fixup, >>>> NULL); >>>> return alias_info; >>>> } >>>> @@ -436,12 +441,12 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t >>>> >>>> /* Insert a new checksum_alias struct into lineno_pointer_sets for >>>> function with >>>> LINENO_CHECKSUM and CFG_CHECKSUM with associated GUID, FI_PTR, and >>>> - ZERO_COUNTS flag. */ >>>> + ZERO_COUNT_FIXUP flag pointer. */ >>>> >>>> static void >>>> checksum_set_insert (unsigned lineno_checksum, unsigned cfg_checksum, >>>> gcov_type guid, const struct gcov_fn_info *fi_ptr, >>>> - int zero_counts) >>>> + int *zero_count_fixup) >>>> { >>>> struct dyn_pointer_set *p = the_dyn_call_graph.lineno_pointer_sets; >>>> if (!p) >>>> @@ -452,7 +457,7 @@ checksum_set_insert (unsigned lineno_checksum, uns >>>> if (*m) >>>> { >>>> (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid, >>>> - fi_ptr, zero_counts, >>>> + fi_ptr, >>>> zero_count_fixup, >>>> >>>> (*m)->cfg_checksum_list); >>>> } >>>> else >>>> @@ -460,7 +465,8 @@ checksum_set_insert (unsigned lineno_checksum, uns >>>> *m = XNEW (struct lineno_checksum_alias); >>>> (*m)->lineno_checksum = lineno_checksum; >>>> (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid, >>>> - fi_ptr, >>>> zero_counts, NULL); >>>> + fi_ptr, >>>> zero_count_fixup, >>>> + NULL); >>>> p->n_elements++; >>>> } >>>> } >>>> @@ -801,10 +807,10 @@ gcov_build_callgraph_ic_fn (struct dyn_cgraph_node >>>> } >>>> } >>>> >>>> -/* Build the dynamic call graph. */ >>>> +/* Build the dynamic call graph and update ZERO_COUNTS flags. */ >>>> >>>> static void >>>> -gcov_build_callgraph (void) >>>> +gcov_build_callgraph (int **zero_counts) >>>> { >>>> struct gcov_info *gi_ptr; >>>> unsigned m_ix; >>>> @@ -852,9 +858,19 @@ static void >>>> if (total_arc_count != 0) >>>> the_dyn_call_graph.num_nodes_executed++; >>>> if (fixup_type) >>>> - checksum_set_insert (fi_ptr->lineno_checksum, >>>> - fi_ptr->cfg_checksum, >>>> caller->guid, >>>> - fi_ptr, total_arc_count == 0); >>>> + { >>>> + int *zero_count_fixup = NULL; >>>> + /* Passing in a non-NULL zero_count_fixup pointer >>>> + indicates that the counts were all zero for this >>>> + function, and the fixup routine will set the flag >>>> + if the function's counters are updated to >>>> non-zero >>>> + values. */ >>>> + if (total_arc_count == 0) >>>> + zero_count_fixup = &zero_counts[m_ix][f_ix]; >>>> + checksum_set_insert (fi_ptr->lineno_checksum, >>>> + fi_ptr->cfg_checksum, >>>> caller->guid, >>>> + fi_ptr, zero_count_fixup); >>>> + } >>>> } >>>> ci_ptr++; >>>> } >>>> @@ -1251,7 +1267,14 @@ gcov_collect_imported_modules (const void *value, >>>> out_array = (struct gcov_import_mod_array *) data1; >>>> >>>> if (m->imp_mod != out_array->importing_module) >>>> + { >>>> out_array->imported_modules[out_array->len++] = m; >>>> + /* Sanity check that the importing (primary) module is not >>>> + actually the same as the new aux module. This could happen if >>>> + we accidentally read in the same gcda file twice. */ >>>> + gcc_assert (m->imp_mod->mod_info->ident != >>>> + out_array->importing_module->mod_info->ident); >>>> + } >>>> >>>> return 1; >>>> } >>>> @@ -2957,7 +2980,7 @@ gcov_fixup_counters_checksum (const struct checksu >>>> for (alias = info->alias_list; alias; >>>> alias = alias->next_alias) >>>> { >>>> - if (alias->zero_counts) >>>> + if (alias->zero_count_fixup) >>>> { >>>> found = 1; >>>> break; >>>> @@ -2972,7 +2995,7 @@ gcov_fixup_counters_checksum (const struct checksu >>>> for (alias = info->alias_list; alias; >>>> alias = alias->next_alias) >>>> { >>>> - if (alias->zero_counts) >>>> + if (alias->zero_count_fixup) >>>> continue; >>>> merge_ctrs (merged_ctrs, alias->fi_ptr->ctrs, alias->guid); >>>> found = 1; >>>> @@ -2990,9 +3013,10 @@ gcov_fixup_counters_checksum (const struct checksu >>>> for (alias = info->alias_list; alias; >>>> alias = alias->next_alias) >>>> { >>>> - if (!alias->zero_counts) >>>> + if (!alias->zero_count_fixup) >>>> continue; >>>> copy_ctrs (alias->fi_ptr->ctrs, alias->guid, merged_ctrs); >>>> + *alias->zero_count_fixup = 1; >>>> } >>>> >>>> return 1; >>>> @@ -3040,11 +3064,13 @@ gcov_fixup_zero_counters (void) >>>> return changed; >>>> } >>>> >>>> -/* Compute module groups needed for L-IPO compilation. Returns 1 if any >>>> - counter fixups were applied, requiring a profile rewrite, 0 otherwise. >>>> */ >>>> +/* Compute module groups needed for L-IPO compilation. The ZERO_COUNTS >>>> + flags are set for functions with zero count fixups applied. Returns 1 >>>> + if any counter fixups were applied, requiring a profile rewrite, >>>> + 0 otherwise. */ >>>> >>>> int >>>> -__gcov_compute_module_groups (void) >>>> +__gcov_compute_module_groups (int **zero_counts) >>>> { >>>> gcov_type cut_off_count; >>>> char *seed = getenv ("LIPO_RANDOM_GROUPING"); >>>> @@ -3091,7 +3117,7 @@ int >>>> fixup_type = atoi (do_fixup); >>>> >>>> /* First compute dynamic call graph. */ >>>> - gcov_build_callgraph (); >>>> + gcov_build_callgraph (zero_counts); >>>> >>>> cut_off_count = gcov_compute_cutoff_count (); >>>> >>>> Index: libgcc/libgcov-driver.c >>>> =================================================================== >>>> --- libgcc/libgcov-driver.c (revision 215230) >>>> +++ libgcc/libgcov-driver.c (working copy) >>>> @@ -55,7 +55,7 @@ static gcov_unsigned_t gcov_cur_module_id = 0; >>>> >>>> >>>> /* Dynamic call graph build and form module groups. */ >>>> -int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN; >>>> +int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN; >>>> void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN; >>>> >>>> /* The following functions can be called from outside of this file. */ >>>> @@ -129,6 +129,24 @@ set_gcov_list (struct gcov_info *head) >>>> __gcov_list = head; >>>> } >>>> >>>> +/* Flag if the current function being read was marked as having fixed-up >>>> + zero counters. */ >>>> +static int __gcov_curr_fn_fixed_up; >>>> + >>>> +/* Set function fixed up flag. */ >>>> +void >>>> +set_gcov_fn_fixed_up (int fixed_up) >>>> +{ >>>> + __gcov_curr_fn_fixed_up = fixed_up; >>>> +} >>>> + >>>> +/* Return function fixed up flag. */ >>>> +int >>>> +get_gcov_fn_fixed_up (void) >>>> +{ >>>> + return __gcov_curr_fn_fixed_up; >>>> +} >>>> + >>>> /* Size of the longest file name. */ >>>> /* We need to expose this static variable when compiling for gcov-tool. >>>> */ >>>> #ifndef IN_GCOV_TOOL >>>> @@ -564,6 +582,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr, >>>> int error = 0; >>>> struct gcov_fn_buffer **fn_tail = &fn_buffer; >>>> struct gcov_summary_buffer **sum_tail = &sum_buffer; >>>> + int *zero_fixup_flags = NULL; >>>> >>>> length = gcov_read_unsigned (); >>>> if (!gcov_version (gi_ptr, length, gi_filename)) >>>> @@ -625,6 +644,21 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr, >>>> tag = gcov_read_unsigned (); >>>> } >>>> >>>> + if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP) >>>> + { >>>> + length = gcov_read_unsigned (); >>>> + gcov_unsigned_t num_fns = 0; >>>> + zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns); >>>> + if (!zero_fixup_flags) >>>> + { >>>> + gcov_error ("profiling:%s:Error reading zero fixup flags\n", >>>> + gi_filename); >>>> + return -1; >>>> + } >>>> + >>>> + tag = gcov_read_unsigned (); >>>> + } >>>> + >>>> /* Merge execution counts for each function. */ >>>> for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; >>>> f_ix++, tag = gcov_read_unsigned ()) >>>> @@ -658,6 +692,9 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr, >>>> continue; >>>> } >>>> >>>> + if (zero_fixup_flags) >>>> + set_gcov_fn_fixed_up (zero_fixup_flags[f_ix]); >>>> + >>>> length = gcov_read_unsigned (); >>>> if (length != gfi_ptr->ident) >>>> goto read_mismatch; >>>> @@ -689,6 +726,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr, >>>> if ((error = gcov_is_error ())) >>>> goto read_error; >>>> } >>>> + free (zero_fixup_flags); >>>> >>>> if (tag && tag != GCOV_TAG_MODULE_INFO) >>>> { >>>> @@ -706,6 +744,34 @@ read_error: >>>> return -1; >>>> } >>>> >>>> + >>>> +/* Write NUM_FNS ZERO_COUNTS fixup flags to a gcda file starting from its >>>> + current location. */ >>>> + >>>> +static void >>>> +gcov_write_comdat_zero_fixup (int *zero_counts, unsigned num_fns) >>>> +{ >>>> + unsigned f_ix; >>>> + gcov_unsigned_t len = GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH (num_fns); >>>> + gcov_write_tag_length (GCOV_TAG_COMDAT_ZERO_FIXUP, len); >>>> + >>>> + gcov_write_unsigned (num_fns); >>>> + gcov_unsigned_t bitvector = 0, b_ix = 0; >>>> + for (f_ix = 0; f_ix != num_fns; f_ix++) >>>> + { >>>> + if (zero_counts[f_ix]) >>>> + bitvector |= 1 << b_ix; >>>> + if (++b_ix == 32) >>>> + { >>>> + gcov_write_unsigned (bitvector); >>>> + b_ix = 0; >>>> + bitvector = 0; >>>> + } >>>> + } >>>> + if (b_ix > 0) >>>> + gcov_write_unsigned (bitvector); >>>> +} >>>> + >>>> /* Write build_info strings from GI_PTR to a gcda file starting from >>>> its current >>>> location. */ >>>> >>>> @@ -758,7 +824,7 @@ gcov_write_func_counters (struct gcov_info *gi_ptr >>>> if (gfi_ptr && gfi_ptr->key == gi_ptr) >>>> length = GCOV_TAG_FUNCTION_LENGTH; >>>> else >>>> - length = 0; >>>> + length = 0; >>>> } >>>> >>>> gcov_write_tag_length (GCOV_TAG_FUNCTION, length); >>>> @@ -1104,9 +1170,24 @@ gcov_dump_module_info (struct gcov_filename_aux *g >>>> { >>>> struct gcov_info *gi_ptr; >>>> >>>> + unsigned max_module_id = 0; >>>> + for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next) >>>> + { >>>> + unsigned mod_id = gi_ptr->mod_info->ident; >>>> + if (max_module_id < mod_id) >>>> + max_module_id = mod_id; >>>> + } >>>> + int **zero_counts = (int **) xcalloc (max_module_id, sizeof (int *)); >>>> + for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next) >>>> + { >>>> + unsigned mod_id = gi_ptr->mod_info->ident; >>>> + zero_counts[mod_id-1] = (int *) xcalloc (gi_ptr->n_functions, >>>> + sizeof (int)); >>>> + } >>>> + >>>> /* Compute the module groups and record whether there were any >>>> counter fixups applied that require rewriting the counters. */ >>>> - int changed = __gcov_compute_module_groups (); >>>> + int changed = __gcov_compute_module_groups (zero_counts); >>>> >>>> /* Now write out module group info. */ >>>> for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next) >>>> @@ -1129,8 +1210,15 @@ gcov_dump_module_info (struct gcov_filename_aux *g >>>> gcov_position_t eof_pos = gi_ptr->eof_pos; >>>> gcov_rewrite (); >>>> gcov_seek (summary_end_pos); >>>> + >>>> + unsigned mod_id = gi_ptr->mod_info->ident; >>>> + gcov_write_comdat_zero_fixup (zero_counts[mod_id-1], >>>> + gi_ptr->n_functions); >>>> + gcov_position_t zero_fixup_eof_pos = gcov_position (); >>>> + >>>> gcov_write_func_counters (gi_ptr); >>>> - gcc_assert (eof_pos == gi_ptr->eof_pos); >>>> + gcc_assert (eof_pos + (zero_fixup_eof_pos - summary_end_pos) >>>> + == gi_ptr->eof_pos); >>>> } >>>> } >>>> else >>>> @@ -1149,7 +1237,11 @@ gcov_dump_module_info (struct gcov_filename_aux *g >>>> "profiling:%s:Error writing\n", >>>> gi_filename); >>>> gcov_write_import_file (gi_filename, gi_ptr); >>>> + free (zero_counts[gi_ptr->mod_info->ident-1]); >>>> } >>>> + >>>> + free (zero_counts); >>>> + >>>> __gcov_finalize_dyn_callgraph (); >>>> } >>>> >>>> Index: libgcc/libgcov.h >>>> =================================================================== >>>> --- libgcc/libgcov.h (revision 215230) >>>> +++ libgcc/libgcov.h (working copy) >>>> @@ -349,6 +349,9 @@ gcov_get_sorted_import_module_array (struct gcov_i >>>> ATTRIBUTE_HIDDEN; >>>> GCOV_LINKAGE inline void gcov_rewrite (void); >>>> >>>> +extern void set_gcov_fn_fixed_up (int fixed_up); >>>> +extern int get_gcov_fn_fixed_up (void); >>>> + >>>> /* "Counts" stored in gcda files can be a real counter value, or >>>> an target address. When differentiate these two types because >>>> when manipulating counts, we should only change real counter values, >>>> @@ -361,7 +364,13 @@ gcov_get_counter (void) >>>> /* This version is for reading count values in libgcov runtime: >>>> we read from gcda files. */ >>>> >>>> - return gcov_read_counter (); >>>> + if (get_gcov_fn_fixed_up ()) >>>> + { >>>> + gcov_read_counter (); >>>> + return 0; >>>> + } >>>> + else >>>> + return gcov_read_counter (); >>>> #else >>>> /* This version is for gcov-tool. We read the value from memory and >>>> multiply it by the merge weight. */ >>>> @@ -380,7 +389,13 @@ gcov_get_counter_target (void) >>>> /* This version is for reading count target values in libgcov runtime: >>>> we read from gcda files. */ >>>> >>>> - return gcov_read_counter (); >>>> + if (get_gcov_fn_fixed_up ()) >>>> + { >>>> + gcov_read_counter (); >>>> + return 0; >>>> + } >>>> + else >>>> + return gcov_read_counter (); >>>> #else >>>> /* This version is for gcov-tool. We read the value from memory >>>> and we do NOT >>>> multiply it by the merge weight. */ >>>> Index: libgcc/libgcov-util.c >>>> =================================================================== >>>> --- libgcc/libgcov-util.c (revision 215230) >>>> +++ libgcc/libgcov-util.c (working copy) >>>> @@ -66,6 +66,7 @@ static void tag_lines (unsigned, unsigned); >>>> static void tag_counters (unsigned, unsigned); >>>> static void tag_summary (unsigned, unsigned); >>>> static void tag_module_info (unsigned, unsigned); >>>> +static void tag_zero_fixup (unsigned, unsigned); >>>> >>>> /* The gcov_info for the first module. */ >>>> static struct gcov_info *curr_gcov_info; >>>> @@ -88,6 +89,8 @@ static int k_ctrs_types; >>>> /* The longest length of all the filenames. */ >>>> static int max_filename_len; >>>> >>>> +static int *zero_fixup_flags = NULL; >>>> + >>>> /* Merge functions for counters. Similar to __gcov_dyn_ipa_merge_* >>>> functions in dyn-ipa.c, which were derived from these, except >>>> the versions in dyn-ipa are used when merging from another array. */ >>>> @@ -143,6 +146,7 @@ static const tag_format_t tag_table[] = >>>> {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, >>>> {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, >>>> {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info}, >>>> + {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup}, >>>> {0, NULL, NULL} >>>> }; >>>> >>>> @@ -169,14 +173,18 @@ tag_function (unsigned tag ATTRIBUTE_UNUSED, unsig >>>> k_ctrs[i].num = 0; >>>> k_ctrs_types = 0; >>>> >>>> + if (zero_fixup_flags) >>>> + { >>>> + set_gcov_fn_fixed_up (zero_fixup_flags[num_fn_info]); >>>> + if (get_gcov_fn_fixed_up () && verbose) >>>> + fprintf (stderr, "Function id=%d fixed up\n", >>>> curr_fn_info->ident); >>>> + } >>>> + >>>> curr_fn_info->key = curr_gcov_info; >>>> curr_fn_info->ident = gcov_read_unsigned (); >>>> curr_fn_info->lineno_checksum = gcov_read_unsigned (); >>>> curr_fn_info->cfg_checksum = gcov_read_unsigned (); >>>> num_fn_info++; >>>> - >>>> - if (verbose) >>>> - fnotice (stdout, "tag one function id=%d\n", curr_fn_info->ident); >>>> } >>>> >>>> /* Handler for reading block tag. */ >>>> @@ -226,7 +234,13 @@ tag_counters (unsigned tag, unsigned length) >>>> gcc_assert (values); >>>> >>>> for (ix = 0; ix != n_counts; ix++) >>>> - values[ix] = gcov_read_counter (); >>>> + { >>>> + gcov_type val = gcov_read_counter (); >>>> + if (!get_gcov_fn_fixed_up ()) >>>> + values[ix] = val; >>>> + else >>>> + values[ix] = 0; >>>> + } >>>> } >>>> >>>> /* Handler for reading summary tag. */ >>>> @@ -323,7 +337,7 @@ lipo_process_substitute_string_1 (char *input_str, >>>> char *t; >>>> >>>> if (verbose) >>>> - printf ("Substitute: %s \n", input_str); >>>> + fprintf (stderr, "Substitute: %s \n", input_str); >>>> t = (char*) xmalloc (strlen (input_str) + 1 >>>> + strlen (new_str) - strlen (cur_str)); >>>> *p = 0; >>>> @@ -332,7 +346,7 @@ lipo_process_substitute_string_1 (char *input_str, >>>> strcat (t, new_str); >>>> strcat (t, p + strlen (cur_str)); >>>> if (verbose) >>>> - printf (" --> %s\n", t); >>>> + fprintf (stderr, " --> %s\n", t); >>>> return t; >>>> } >>>> >>>> @@ -397,6 +411,16 @@ tag_module_info (unsigned tag ATTRIBUTE_UNUSED, un >>>> free (mod_info); >>>> } >>>> >>>> +/* Handler for reading the COMDAT zero-profile fixup section. */ >>>> + >>>> +static void >>>> +tag_zero_fixup (unsigned tag ATTRIBUTE_UNUSED, unsigned length) >>>> +{ >>>> + gcov_unsigned_t num_fns = 0; >>>> + zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns); >>>> + gcc_assert (zero_fixup_flags); >>>> +} >>>> + >>>> /* Read the content of a gcda file FILENAME, and return a gcov_info >>>> data structure. >>>> Program level summary CURRENT_SUMMARY will also be updated. */ >>>> >>>> @@ -520,6 +544,7 @@ read_gcda_file (const char *filename) >>>> } >>>> >>>> read_gcda_finalize (obj_info); >>>> + free (zero_fixup_flags); >>>> gcov_close (); >>>> >>>> return obj_info; >>>> @@ -1099,7 +1124,7 @@ gcov_profile_scale (struct gcov_info *profile, flo >>>> unsigned f_ix; >>>> >>>> if (verbose) >>>> - fnotice (stdout, "scale_factor is %f or %d/%d\n", scale_factor, n, d); >>>> + fnotice (stderr, "scale_factor is %f or %d/%d\n", scale_factor, n, d); >>>> >>>> /* Scaling the counters. */ >>>> for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) >>>> @@ -1167,7 +1192,7 @@ gcov_profile_normalize (struct gcov_info *profile, >>>> scale_factor = (float)max_val / curr_max_val; >>>> #if !defined (_WIN32) >>>> if (verbose) >>>> - fnotice (stdout, "max_val is %lld\n", (long long) curr_max_val); >>>> + fnotice (stderr, "max_val is %lld\n", (long long) curr_max_val); >>>> #endif >>>> >>>> return gcov_profile_scale (profile, scale_factor, 0, 0); >>>> >>>> -- >>>> Teresa Johnson | Software Engineer | tejohn...@google.com | 408-460-2413 >> >> >> >> -- >> Teresa Johnson | Software Engineer | tejohn...@google.com | 408-460-2413 -- Teresa Johnson | Software Engineer | tejohn...@google.com | 408-460-2413