[gcc r15-8948] Add coverage_instrumentation_p
https://gcc.gnu.org/g:dc9ca4b2701f91b1efb82ea605f0ec7254e80974 commit r15-8948-gdc9ca4b2701f91b1efb82ea605f0ec7254e80974 Author: Jørgen Kvalsvik Date: Wed Mar 26 22:15:26 2025 +0100 Add coverage_instrumentation_p Provide a helper for checking if any coverage (arc, conditions, paths) is enabled, rather than manually checking all the flags. This should make the intent clearer, and make it easier to maintain the checks when more flags are added. The function is forward declared in two header files as different passes tend to include different headers (profile.h vs value-prof.h). This could maybe be merged at some points, but profiling related symbols are already a bit spread out and should probably be handled in a targeted effort. gcc/ChangeLog: * builtins.cc (expand_builtin_fork_or_exec): Call coverage_instrumentation_p. * ipa-inline.cc (can_early_inline_edge_p): Likewise. * passes.cc (finish_optimization_passes): Likewise. * profile.cc (coverage_instrumentation_p): New function. * profile.h (coverage_instrumentation_p): New declaration. * tree-profile.cc (tree_profiling): Call coverage_instrumentation_p. (pass_ipa_tree_profile::gate): Likewise. * value-prof.h (coverage_instrumentation_p): New declaration. Diff: --- gcc/builtins.cc | 2 +- gcc/ipa-inline.cc | 2 +- gcc/passes.cc | 4 ++-- gcc/profile.cc | 7 +++ gcc/profile.h | 4 gcc/tree-profile.cc | 11 --- gcc/value-prof.h| 4 7 files changed, 23 insertions(+), 11 deletions(-) diff --git a/gcc/builtins.cc b/gcc/builtins.cc index ff48546eafd6..a5f711a7b6a2 100644 --- a/gcc/builtins.cc +++ b/gcc/builtins.cc @@ -6362,7 +6362,7 @@ expand_builtin_fork_or_exec (tree fn, tree exp, rtx target, int ignore) tree call; /* If we are not profiling, just call the function. */ - if (!profile_arc_flag && !condition_coverage_flag && !path_coverage_flag) + if (!coverage_instrumentation_p ()) return NULL_RTX; /* Otherwise call the wrapper. This should be equivalent for the rest of diff --git a/gcc/ipa-inline.cc b/gcc/ipa-inline.cc index 38b4c9899012..d9fc111a9e76 100644 --- a/gcc/ipa-inline.cc +++ b/gcc/ipa-inline.cc @@ -701,7 +701,7 @@ can_early_inline_edge_p (struct cgraph_edge *e) } gcc_assert (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->caller->decl)) && gimple_in_ssa_p (DECL_STRUCT_FUNCTION (callee->decl))); - if ((profile_arc_flag || condition_coverage_flag || path_coverage_flag) + if (coverage_instrumentation_p () && ((lookup_attribute ("no_profile_instrument_function", DECL_ATTRIBUTES (caller->decl)) == NULL_TREE) != (lookup_attribute ("no_profile_instrument_function", diff --git a/gcc/passes.cc b/gcc/passes.cc index a8c75520c8d9..3c28db78f095 100644 --- a/gcc/passes.cc +++ b/gcc/passes.cc @@ -352,8 +352,8 @@ finish_optimization_passes (void) gcc::dump_manager *dumps = m_ctxt->get_dumps (); timevar_push (TV_DUMP); - if (profile_arc_flag || condition_coverage_flag || path_coverage_flag - || flag_test_coverage || flag_branch_probabilities) + if (coverage_instrumentation_p () || flag_test_coverage + || flag_branch_probabilities) { dumps->dump_start (pass_profile_1->static_pass_number, NULL); end_branch_prob (); diff --git a/gcc/profile.cc b/gcc/profile.cc index 8bba8b4398ff..0b222cf38640 100644 --- a/gcc/profile.cc +++ b/gcc/profile.cc @@ -1798,3 +1798,10 @@ end_branch_prob (void) total_num_conds); } } + +/* Return true if any cfg coverage/profiling is enabled; -fprofile-arcs + -fcondition-coverage -fpath-coverage. */ +bool coverage_instrumentation_p () +{ + return profile_arc_flag || condition_coverage_flag || path_coverage_flag; +} diff --git a/gcc/profile.h b/gcc/profile.h index 78d69f45ff27..a97445b8f6f3 100644 --- a/gcc/profile.h +++ b/gcc/profile.h @@ -77,4 +77,8 @@ extern void get_working_sets (void); profile.cc. */ extern struct gcov_summary *profile_info; +/* Return true if any cfg coverage/profiling is enabled; -fprofile-arcs + -fcondition-coverage -fpath-coverage. */ +extern bool coverage_instrumentation_p (); + #endif /* PROFILE_H */ diff --git a/gcc/tree-profile.cc b/gcc/tree-profile.cc index 8aaed78e60c0..fed218eb60bc 100644 --- a/gcc/tree-profile.cc +++ b/gcc/tree-profile.cc @@ -1908,7 +1908,7 @@ tree_profiling (void) thunk = true; /* When generate profile, expand thunk to gimple so it can be instrumented same way as other functions. */ - if (profile_arc_flag || condition_coverage_flag || path_coverage_flag) + if (coverage_instrumentation_p ()) expand_thunk (node, false, true); /* Read cgraph profile but keep function as thunk at profile-use time. */ @@ -1953,8 +1
[gcc r15-8946] gcov: branch, conds, calls in function summaries
https://gcc.gnu.org/g:580664d1b66a5d98e5e87d9ff728566b309e89b1 commit r15-8946-g580664d1b66a5d98e5e87d9ff728566b309e89b1 Author: Jørgen Kvalsvik Date: Wed Aug 7 17:33:31 2024 +0200 gcov: branch, conds, calls in function summaries The gcov function summaries only output the covered lines, not the branches and calls. Since the function summaries is an opt-in it probably makes sense to also include branch coverage, calls, and condition coverage. $ gcc --coverage -fpath-coverage hello.c -o hello $ ./hello Before: $ gcov -f hello Function 'main' Lines executed:100.00% of 4 Function 'fn' Lines executed:100.00% of 7 File 'hello.c' Lines executed:100.00% of 11 Creating 'hello.c.gcov' After: $ gcov -f hello Function 'main' Lines executed:100.00% of 3 No branches Calls executed:100.00% of 1 Function 'fn' Lines executed:100.00% of 7 Branches executed:100.00% of 4 Taken at least once:50.00% of 4 No calls File 'hello.c' Lines executed:100.00% of 10 Creating 'hello.c.gcov' Lines executed:100.00% of 10 With conditions: $ gcov -fg hello Function 'main' Lines executed:100.00% of 3 No branches Calls executed:100.00% of 1 No conditions Function 'fn' Lines executed:100.00% of 7 Branches executed:100.00% of 4 Taken at least once:50.00% of 4 Condition outcomes covered:100.00% of 8 No calls File 'hello.c' Lines executed:100.00% of 10 Creating 'hello.c.gcov' Lines executed:100.00% of 10 gcc/ChangeLog: * gcov.cc (generate_results): Count branches, conditions. (function_summary): Output branch, calls, condition count. Diff: --- gcc/gcov.cc | 48 +++- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/gcc/gcov.cc b/gcc/gcov.cc index 96fdc50f0e8f..f9ab93b54d99 100644 --- a/gcc/gcov.cc +++ b/gcc/gcov.cc @@ -1687,11 +1687,19 @@ generate_results (const char *file_name) memset (&coverage, 0, sizeof (coverage)); coverage.name = fn->get_name (); add_line_counts (flag_function_summary ? &coverage : NULL, fn); - if (flag_function_summary) - { - function_summary (&coverage); - fnotice (stdout, "\n"); - } + + if (!flag_function_summary) + continue; + + for (const block_info& block : fn->blocks) + for (arc_info *arc = block.succ; arc; arc = arc->succ_next) + add_branch_counts (&coverage, arc); + + for (const block_info& block : fn->blocks) + add_condition_counts (&coverage, &block); + + function_summary (&coverage); + fnotice (stdout, "\n"); } name_map needle; @@ -2764,6 +2772,36 @@ function_summary (const coverage_info *coverage) { fnotice (stdout, "%s '%s'\n", "Function", coverage->name); executed_summary (coverage->lines, coverage->lines_executed); + + if (coverage->branches) +{ + fnotice (stdout, "Branches executed:%s of %d\n", + format_gcov (coverage->branches_executed, coverage->branches, 2), + coverage->branches); + fnotice (stdout, "Taken at least once:%s of %d\n", + format_gcov (coverage->branches_taken, coverage->branches, 2), + coverage->branches); +} + else +fnotice (stdout, "No branches\n"); + + if (coverage->calls) +fnotice (stdout, "Calls executed:%s of %d\n", +format_gcov (coverage->calls_executed, coverage->calls, 2), +coverage->calls); + else +fnotice (stdout, "No calls\n"); + + if (flag_conditions) +{ + if (coverage->conditions) + fnotice (stdout, "Condition outcomes covered:%s of %d\n", +format_gcov (coverage->conditions_covered, + coverage->conditions, 2), +coverage->conditions); + else + fnotice (stdout, "No conditions\n"); +} } /* Output summary info for a file. */
[gcc r15-9072] Only write gcov when file output is on [PR119553]
https://gcc.gnu.org/g:de742091f4df997f426d99bcfed3c44914788033 commit r15-9072-gde742091f4df997f426d99bcfed3c44914788033 Author: Jørgen Kvalsvik Date: Mon Mar 31 19:03:37 2025 +0200 Only write gcov when file output is on [PR119553] gcov_write_* functions must be guarded so they only are called when output_to_file is true, like for -fcondition-coverage, otherwise it triggers an invalid read as detected by valgrind. The gcno file is mostly written to from profile.cc, so it doesn't make too much sense to hide it in path-coverage.cc. The find_paths name was a bit imprecise, and is renamed to instrument_prime_paths. PR gcov-profile/119553 gcc/ChangeLog: * path-coverage.cc (find_paths): Return path count, don't write to gcno, and rename to ... (instrument_prime_paths): ... this. * profile.cc (branch_prob): Write path counts to gcno. Diff: --- gcc/path-coverage.cc | 15 ++- gcc/profile.cc | 12 ++-- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/gcc/path-coverage.cc b/gcc/path-coverage.cc index 55b392986035..55058fd04ff2 100644 --- a/gcc/path-coverage.cc +++ b/gcc/path-coverage.cc @@ -504,8 +504,8 @@ flush_on_gsi (gimple_stmt_iterator *gsi, size_t bucket, tree local, tree mask, bit N%64 in bucket N/64. For large functions, an individual basic block will only be part of a small subset of paths, and by extension buckets and local counters. Only the necessary counters are read and written. */ -void -find_paths (struct function *fn) +unsigned +instrument_prime_paths (struct function *fn) { mark_dfs_back_edges (fn); vec> paths = find_prime_paths (fn); @@ -515,7 +515,7 @@ find_paths (struct function *fn) warning_at (fn->function_start_locus, OPT_Wcoverage_too_many_paths, "paths exceeding limit, giving up path coverage"); release_vec_vec (paths); - return; + return 0; } tree gcov_type_node = get_gcov_type (); @@ -526,14 +526,9 @@ find_paths (struct function *fn) if (!coverage_counter_alloc (GCOV_COUNTER_PATHS, nbuckets)) { release_vec_vec (paths); - return; + return 0; } - gcov_position_t offset {}; - offset = gcov_write_tag (GCOV_TAG_PATHS); - gcov_write_unsigned (paths.length ()); - gcov_write_length (offset); - hash_map ands; hash_map iors; hash_map flushes; @@ -771,6 +766,8 @@ find_paths (struct function *fn) } } + const unsigned npaths = paths.length (); blocks.release (); release_vec_vec (paths); + return npaths; } diff --git a/gcc/profile.cc b/gcc/profile.cc index 0b222cf38640..c0f5097726bd 100644 --- a/gcc/profile.cc +++ b/gcc/profile.cc @@ -1611,9 +1611,17 @@ branch_prob (bool thunk) instrument_values (values); } - void find_paths (struct function*); + unsigned instrument_prime_paths (struct function*); if (path_coverage_flag) -find_paths (cfun); +{ + const unsigned npaths = instrument_prime_paths (cfun); + if (output_to_file) + { + gcov_position_t offset = gcov_write_tag (GCOV_TAG_PATHS); + gcov_write_unsigned (npaths); + gcov_write_length (offset); + } +} free_aux_for_edges ();
[gcc r16-493] Printf properly on systems without %zu [PR120086]
https://gcc.gnu.org/g:dd87540491de7242416751666089fcdbce434e7b commit r16-493-gdd87540491de7242416751666089fcdbce434e7b Author: Jørgen Kvalsvik Date: Tue May 6 11:38:34 2025 +0200 Printf properly on systems without %zu [PR120086] Some systems don't support the %zu format modifier for size_t, such as hppa64-hp-hpux. We don't really need the full width of size_t for printing the number of prime paths as path counts of those sizes would've already blown up the machine. For printing the vector size we can use the formatting directives from hwint.h. PR gcov-profile/120086 gcc/ChangeLog: * gcov.cc (print_prime_path_lines): Use unsigned, format with %u. (print_prime_path_source): Likewise. (output_path_coverage): Format with HOST_SIZE_T_PRINT_UNSIGNED, use unsigned for pathno. Diff: --- gcc/gcov.cc | 20 ++-- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/gcc/gcov.cc b/gcc/gcov.cc index e1ad80162b38..0dfe513723c1 100644 --- a/gcc/gcov.cc +++ b/gcc/gcov.cc @@ -3591,7 +3591,7 @@ print_source_line (FILE *f, const vector &source_lines, Returns 1 if the path was printed, 0 otherwise. */ static unsigned print_prime_path_lines (FILE *gcov_file, const function_info &fn, - const vector &path, size_t pathno) + const vector &path, unsigned pathno) { const bool is_covered = fn.paths.covered_p (pathno); if (is_covered && !flag_prime_paths_lines_covered) @@ -3600,9 +3600,9 @@ print_prime_path_lines (FILE *gcov_file, const function_info &fn, return 0; if (is_covered) -fprintf (gcov_file, "path %zu covered: lines", pathno); +fprintf (gcov_file, "path %u covered: lines", pathno); else -fprintf (gcov_file, "path %zu not covered: lines", pathno); +fprintf (gcov_file, "path %u not covered: lines", pathno); for (size_t k = 0; k != path.size (); ++k) { @@ -3658,7 +3658,7 @@ print_inlined_separator (FILE *gcov_file, unsigned current_index, const Returns 1 if the path was printed, 0 otherwise. */ static unsigned print_prime_path_source (FILE *gcov_file, const function_info &fn, -const vector &path, size_t pathno) +const vector &path, unsigned pathno) { const bool is_covered = fn.paths.covered_p (pathno); if (is_covered && !flag_prime_paths_source_covered) @@ -3667,9 +3667,9 @@ print_prime_path_source (FILE *gcov_file, const function_info &fn, return 0; if (is_covered) -fprintf (gcov_file, "path %zu covered:\n", pathno); +fprintf (gcov_file, "path %u covered:\n", pathno); else -fprintf (gcov_file, "path %zu not covered:\n", pathno); +fprintf (gcov_file, "path %u not covered:\n", pathno); unsigned current = fn.src; for (size_t k = 0; k != path.size (); ++k) { @@ -3728,19 +3728,19 @@ output_path_coverage (FILE *gcov_file, const function_info *fn) if (fn->paths.paths.empty ()) fnotice (gcov_file, "path coverage omitted\n"); else -fnotice (gcov_file, "paths covered %u of %zu\n", -fn->paths.covered_paths (), fn->paths.paths.size ()); +fnotice (gcov_file, "paths covered %u of " HOST_SIZE_T_PRINT_UNSIGNED "\n", +fn->paths.covered_paths (), (fmt_size_t)fn->paths.paths.size ()); if (flag_prime_paths_lines_uncovered || flag_prime_paths_lines_covered) { - size_t pathno = 0; + unsigned pathno = 0; for (const vector &path : fn->paths.paths) print_prime_path_lines (gcov_file, *fn, path, pathno++); } if (flag_prime_paths_source_uncovered || flag_prime_paths_source_covered) { - size_t pathno = 0; + unsigned pathno = 0; for (const vector &path : fn->paths.paths) print_prime_path_source (gcov_file, *fn, path, pathno++); }
[gcc r16-1590] Free buffer on function exit [PR120634]
https://gcc.gnu.org/g:246c33ac8e8e1967c74ae20c07454a24ef02822a commit r16-1590-g246c33ac8e8e1967c74ae20c07454a24ef02822a Author: Jørgen Kvalsvik Date: Thu Jun 19 20:56:30 2025 +0200 Free buffer on function exit [PR120634] Using auto_vec ensures that the buffer is always free'd when the function returns. PR gcov-profile/120634 gcc/ChangeLog: * prime-paths.cc (trie::paths): Use auto_vec. Diff: --- gcc/prime-paths.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gcc/prime-paths.cc b/gcc/prime-paths.cc index 838343c8427d..38feeea15226 100644 --- a/gcc/prime-paths.cc +++ b/gcc/prime-paths.cc @@ -635,7 +635,7 @@ trie::insert_with_suffix (array_slice path) vec> trie::paths () const { - vec path {}; + auto_vec path {}; vec> all {}; auto iter = paths (path); while (iter.next ())
[gcc r16-1591] Use auto_vec in prime paths selftests [PR120634]
https://gcc.gnu.org/g:69725b13e9dc8bdb17ec8a7d554071b6b517ad47 commit r16-1591-g69725b13e9dc8bdb17ec8a7d554071b6b517ad47 Author: Jørgen Kvalsvik Date: Thu Jun 19 21:00:07 2025 +0200 Use auto_vec in prime paths selftests [PR120634] The selftests had a bunch of memory leaks that showed up in make selftest-valgrind as a result of not using auto_vec or other explicitly calling release. Replacing vec with auto_vec makes the problem go away. The auto_vec_vec helper is made constructable from a vec so that objects returned from functions can be automatically managed too. PR gcov-profile/120634 gcc/ChangeLog: * prime-paths.cc (struct auto_vec_vec): Add constructor from vec. (test_split_components): Use auto_vec_vec. (test_scc_internal_prime_paths): Ditto. (test_scc_entry_exit_paths): Ditto. (test_complete_prime_paths): Ditto. (test_entry_prime_paths): Ditto. (test_singleton_path): Ditto. Diff: --- gcc/prime-paths.cc | 48 +++- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/gcc/prime-paths.cc b/gcc/prime-paths.cc index 38feeea15226..5b626ce200a0 100644 --- a/gcc/prime-paths.cc +++ b/gcc/prime-paths.cc @@ -91,6 +91,8 @@ struct auto_sbitmap_vector /* A silly RAII wrpaper for automatically releasing a vec>. */ struct auto_vec_vec : vec> { + auto_vec_vec () = default; + auto_vec_vec (vec> v) : vec>(v) {} ~auto_vec_vec () { release_vec_vec (*this); } }; @@ -1658,8 +1660,8 @@ test_split_components () int nscc = graphds_scc (cfg, NULL); auto_graph ccfg (disconnect_sccs (cfg)); - vec> entries {}; - vec> exits {}; + auto_vec_vec entries {}; + auto_vec_vec exits {}; entries.safe_grow_cleared (nscc); exits.safe_grow_cleared (nscc); @@ -1707,7 +1709,7 @@ test_split_components () because other graph inconsistencies are easier to detect. */ /* Count and check singleton components. */ - vec scc_size {}; + auto_vec scc_size {}; scc_size.safe_grow_cleared (nscc); for (int i = 0; i != cfg->n_vertices; ++i) scc_size[cfg->vertices[i].component]++; @@ -1722,14 +1724,14 @@ test_split_components () /* Manually unroll the loop finding the simple paths starting at the vertices in the SCCs. In this case there is only the one SCC. */ trie ccfg_paths; - simple_paths (ccfg, ccfg_paths, 2); - simple_paths (ccfg, ccfg_paths, 4); - simple_paths (ccfg, ccfg_paths, 5); - simple_paths (ccfg, ccfg_paths, 6); - simple_paths (ccfg, ccfg_paths, 7); - simple_paths (ccfg, ccfg_paths, 9); + auto_vec_vec (simple_paths (ccfg, ccfg_paths, 2)); + auto_vec_vec (simple_paths (ccfg, ccfg_paths, 4)); + auto_vec_vec (simple_paths (ccfg, ccfg_paths, 5)); + auto_vec_vec (simple_paths (ccfg, ccfg_paths, 6)); + auto_vec_vec (simple_paths (ccfg, ccfg_paths, 7)); + auto_vec_vec (simple_paths (ccfg, ccfg_paths, 9)); /* Then in+out of trie. */ - vec> xscc_internal_pp = ccfg_paths.paths (); + auto_vec_vec xscc_internal_pp = ccfg_paths.paths (); trie scc_internal_pp; for (auto &p : xscc_internal_pp) scc_internal_pp.insert_with_suffix (p); @@ -1782,7 +1784,7 @@ test_scc_internal_prime_paths () add_edge (scc, 9, 7); add_edge (scc, 7, 2); - vec> paths = prime_paths (scc, 100); + auto_vec_vec paths = prime_paths (scc, 100); const int p01[] = { 5, 7, 2, 4, 6, 9 }; const int p02[] = { 4, 6, 9, 7, 2, 4 }; const int p03[] = { 2, 4, 6, 9, 7, 2 }; @@ -1806,7 +1808,6 @@ test_scc_internal_prime_paths () ASSERT_TRUE (any_equal_p (p09, paths)); ASSERT_TRUE (any_equal_p (p10, paths)); ASSERT_TRUE (any_equal_p (p11, paths)); - release_vec_vec (paths); } /* Test the entry/exit path helpers for the strongly connected component in @@ -1825,13 +1826,13 @@ test_scc_entry_exit_paths () add_edge (scc, 7, 2); trie scc_internal_trie; - simple_paths (scc, scc_internal_trie, 2); - simple_paths (scc, scc_internal_trie, 4); - simple_paths (scc, scc_internal_trie, 5); - simple_paths (scc, scc_internal_trie, 6); - simple_paths (scc, scc_internal_trie, 7); - simple_paths (scc, scc_internal_trie, 9); - vec> scc_prime_paths = scc_internal_trie.paths (); + auto_vec_vec (simple_paths (scc, scc_internal_trie, 2)); + auto_vec_vec (simple_paths (scc, scc_internal_trie, 4)); + auto_vec_vec (simple_paths (scc, scc_internal_trie, 5)); + auto_vec_vec (simple_paths (scc, scc_internal_trie, 6)); + auto_vec_vec (simple_paths (scc, scc_internal_trie, 7)); + auto_vec_vec (simple_paths (scc, scc_internal_trie, 9)); + auto_vec_vec scc_prime_paths = scc_internal_trie.paths (); trie entry_exits {}; scc_entry_exit_paths (scc_prime_paths, 2, 2, entry_exits); @@ -1867,8 +1868,6 @@ test_scc_entry_exit_paths () ASSERT_EQ (count (entries), 2); ASSERT_TRUE (contains (entries, p07)); ASSERT_TRUE (contains (entries, p08)); - - release_vec_