[gcc r15-8948] Add coverage_instrumentation_p

2025-03-26 Thread Jorgen Kvalsvik via Gcc-cvs
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

2025-03-26 Thread Jorgen Kvalsvik via Gcc-cvs
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]

2025-03-31 Thread Jorgen Kvalsvik via Gcc-cvs
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 ();