public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: "Jørgen Kvalsvik" <jorgen.kvalsvik@woven.toyota>
To: Jan Hubicka <hubicka@ucw.cz>
Cc: gcc-patches@gcc.gnu.org
Subject: Re: Ping [PATCH v4] Add condition coverage profiling
Date: Fri, 30 Jun 2023 15:53:29 +0900	[thread overview]
Message-ID: <92d1bd15-662a-9765-d61c-babdf34abb57@woven.toyota> (raw)
In-Reply-To: <ZJVz36AR8Daxpj8H@kam.mff.cuni.cz>

On 23/06/2023 19:28, Jan Hubicka wrote:
>>>
>>> gcc/ChangeLog:
>>>
>>> 	* builtins.cc (expand_builtin_fork_or_exec): Check
>>> 	profile_condition_flag.
>>>         * collect2.cc (main): Add -fno-profile-conditions to OBSTACK.
>>> 	* common.opt: Add new options -fprofile-conditions and
>>> 	* doc/gcov.texi: Add --conditions documentation.
>>> 	* doc/invoke.texi: Add -fprofile-conditions documentation.
>>> 	* gcc.cc: Link gcov on -fprofile-conditions.
>>> 	* gcov-counter.def (GCOV_COUNTER_CONDS): New.
>>> 	* gcov-dump.cc (tag_conditions): New.
>>> 	* gcov-io.h (GCOV_TAG_CONDS): New.
>>> 	(GCOV_TAG_CONDS_LENGTH): Likewise.
>>> 	(GCOV_TAG_CONDS_NUM): Likewise.
>>> 	* gcov.cc (class condition_info): New.
>>> 	(condition_info::condition_info): New.
>>> 	(condition_info::popcount): New.
>>> 	(struct coverage_info): New.
>>> 	(add_condition_counts): New.
>>> 	(output_conditions): New.
>>> 	(print_usage): Add -g, --conditions.
>>> 	(process_args): Likewise.
>>> 	(output_intermediate_json_line): Output conditions.
>>> 	(read_graph_file): Read conditions counters.
>>> 	(read_count_file): Read conditions counters.
>>> 	(file_summary): Print conditions.
>>> 	(accumulate_line_info): Accumulate conditions.
>>> 	(output_line_details): Print conditions.
>>> 	* ipa-inline.cc (can_early_inline_edge_p): Check
>>> 	profile_condition_flag.
>>> 	* ipa-split.cc (pass_split_functions::gate): Likewise.
>>> 	* passes.cc (finish_optimization_passes): Likewise.
>>> 	* profile.cc (find_conditions): New declaration.
>>> 	(cov_length): Likewise.
>>> 	(cov_blocks): Likewise.
>>> 	(cov_masks): Likewise.
>>> 	(cov_free): Likewise.
>>> 	(instrument_decisions): New.
>>> 	(read_thunk_profile): Control output to file.
>>> 	(branch_prob): Call find_conditions, instrument_decisions.
>>> 	(init_branch_prob): Add total_num_conds.
>>> 	(end_branch_prob): Likewise.
>>> 	* tree-profile.cc (struct conds_ctx): New.
>>> 	(CONDITIONS_MAX_TERMS): New.
>>> 	(EDGE_CONDITION): New.
>>> 	(cmp_index_map): New.
>>> 	(index_of): New.
>>> 	(block_conditional_p): New.
>>> 	(edge_conditional_p): New.
>>> 	(single): New.
>>> 	(single_edge): New.
>>> 	(contract_edge): New.
>>> 	(contract_edge_up): New.
>>> 	(ancestors_of): New.
>>> 	(struct outcomes): New.
>>> 	(conditional_succs): New.
>>> 	(condition_index): New.
>>> 	(masking_vectors): New.
>>> 	(cond_reachable_from): New.
>>> 	(neighborhood): New.
>>> 	(isolate_expression): New.
>>> 	(emit_bitwise_op): New.
>>> 	(make_index_map_visit): New.
>>> 	(make_index_map): New.
>>> 	(collect_conditions): New.
>>> 	(yes): New.
>>> 	(struct condcov): New.
>>> 	(cov_length): New.
>>> 	(cov_blocks): New.
>>> 	(cov_masks): New.
>>> 	(cov_free): New.
>>> 	(find_conditions): New.
>>> 	(instrument_decisions): New.
>>> 	(tree_profiling): Check profile_condition_flag.
>>> 	(pass_ipa_tree_profile::gate): Likewise.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>> 	* lib/gcov.exp: Add condition coverage test function.
>>> 	* g++.dg/gcov/gcov-18.C: New test.
>>> 	* gcc.misc-tests/gcov-19.c: New test.
>>> 	* gcc.misc-tests/gcov-20.c: New test.
>>> 	* gcc.misc-tests/gcov-21.c: New test.
>>> ---
>>>
>>> v2:
>>> 	* Moved the docs to rst/sphinx
>>> 	* Output and message uses the 'conditions outcomes' vocabulary
>>> 	* Fixed errors reported by contrib/style-check. Note that a few
>>> 	  warnings persist but are either in comments (ascii art) or because
>>> 	  the surrounding code (typically lists) are formatted the same way
>>> v3:
>>> 	* Revert docs from rst/sphinx to texinfo
>>> v4:
>>> 	* Rebased on trunk, removed @gol from texi
>>>
>>>  gcc/builtins.cc                        |    2 +-
>>>  gcc/collect2.cc                        |    7 +-
>>>  gcc/common.opt                         |    8 +
>>>  gcc/doc/gcov.texi                      |   37 +
>>>  gcc/doc/invoke.texi                    |   19 +
>>>  gcc/gcc.cc                             |    4 +-
>>>  gcc/gcov-counter.def                   |    3 +
>>>  gcc/gcov-dump.cc                       |   24 +
>>>  gcc/gcov-io.h                          |    3 +
>>>  gcc/gcov.cc                            |  200 +++-
>>>  gcc/ipa-inline.cc                      |    2 +-
>>>  gcc/ipa-split.cc                       |    3 +-
>>>  gcc/passes.cc                          |    3 +-
>>>  gcc/profile.cc                         |   84 +-
>>>  gcc/testsuite/g++.dg/gcov/gcov-18.C    |  234 +++++
>>>  gcc/testsuite/gcc.misc-tests/gcov-19.c | 1250 ++++++++++++++++++++++++
>>>  gcc/testsuite/gcc.misc-tests/gcov-20.c |   22 +
>>>  gcc/testsuite/gcc.misc-tests/gcov-21.c |   16 +
>>>  gcc/testsuite/lib/gcov.exp             |  191 +++-
>>>  gcc/tree-profile.cc                    | 1048 +++++++++++++++++++-
>>>  libgcc/libgcov-merge.c                 |    5 +
>>>  21 files changed, 3137 insertions(+), 28 deletions(-)
>>>  create mode 100644 gcc/testsuite/g++.dg/gcov/gcov-18.C
>>>  create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-19.c
>>>  create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-20.c
>>>  create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-21.c
>>>
>>> diff --git a/gcc/builtins.cc b/gcc/builtins.cc
>>> index 8400adaf5b4..c71124a5615 100644
>>> --- a/gcc/builtins.cc
>>> +++ b/gcc/builtins.cc
>>> @@ -5902,7 +5902,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)
>>> +  if (!profile_arc_flag && !profile_condition_flag)
> 
> I think profile_condition_flag can simpy imply profile_arc_flag.  
> This is an information you always measure in adition to the usual bb
> profile, right?
> 
> This can be arranged in opts.c and would avoid need to test both flags
> throughout the compiler.
> Or can both profiles be used independently?

Both can be used independently, and you might actually want to - for branch
coverage (was every branch taken at least once?) the profile-arc instrumentation
was sufficient, but redundant if you also want condition coverage. However,
profile-arcs also give you branch *counts* which is useful for data-driven
decisions.

Now, we might want to have --coverage imply profile-conditions.

>>>      return NULL_RTX;
>>>  
>>>    /* Otherwise call the wrapper.  This should be equivalent for the rest of
>>> diff --git a/gcc/collect2.cc b/gcc/collect2.cc
>>> index 63b9a0c233a..12ff5d81424 100644
>>> --- a/gcc/collect2.cc
>>> +++ b/gcc/collect2.cc
>>> @@ -1032,9 +1032,9 @@ main (int argc, char **argv)
>>>        lto_mode = LTO_MODE_LTO;
>>>    }
>>>  
>>> -  /* -fno-profile-arcs -fno-test-coverage -fno-branch-probabilities
>>> -     -fno-exceptions -w -fno-whole-program */
>>> -  num_c_args += 6;
>>> +  /* -fno-profile-arcs -fno-profile-conditions -fno-test-coverage
>>> +     -fno-branch-probabilities -fno-exceptions -w -fno-whole-program */
>>> +  num_c_args += 7;
>>>  
>>>    c_argv = XCNEWVEC (char *, num_c_args);
>>>    c_ptr = CONST_CAST2 (const char **, char **, c_argv);
>>> @@ -1230,6 +1230,7 @@ main (int argc, char **argv)
>>>      }
>>>    obstack_free (&temporary_obstack, temporary_firstobj);
>>>    *c_ptr++ = "-fno-profile-arcs";
>>> +  *c_ptr++ = "-fno-profile-conditions";
> If this was initialized from an array of strings, we can avoid need for
> num_c_args to be computed by hand....
>>>    *c_ptr++ = "-fno-test-coverage";
>>>    *c_ptr++ = "-fno-branch-probabilities";
>>>    *c_ptr++ = "-fno-exceptions";
>>> diff --git a/gcc/common.opt b/gcc/common.opt
>>> index a28ca13385a..e32a5f7b0db 100644
>>> --- a/gcc/common.opt
>>> +++ b/gcc/common.opt
>>> @@ -862,6 +862,10 @@ Wcoverage-invalid-line-number
>>>  Common Var(warn_coverage_invalid_linenum) Init(1) Warning
>>>  Warn in case a function ends earlier than it begins due to an invalid linenum macros.
>>>  
>>> +Wcoverage-too-many-conditions
>>> +Common Var(warn_too_many_conditions) Init(1) Warning
>>> +Warn when a conditional has too many terms and coverage gives up.
> 
> Maybe when user reads it in --help, he/she will not know what coverage
> you means.  It is called condition coverage profiling later, so we can
> stick to this term here.
>>> +
>>>  Wmissing-profile
>>>  Common Var(warn_missing_profile) Init(1) Warning
>>>  Warn in case profiles in -fprofile-use do not exist.
>>> @@ -2351,6 +2355,10 @@ fprofile-arcs
>>>  Common Var(profile_arc_flag)
>>>  Insert arc-based program profiling code.
>>>  
>>> +fprofile-conditions
>>> +Common Var(profile_condition_flag)
>>> +Insert condition coverage profiling code.
>>> +
>>>  fprofile-dir=
>>>  Common Joined RejectNegative Var(profile_data_prefix)
>>>  Set the top-level directory for storing the profile data.
>>> diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
>>> index 3019efc4674..10cfdcf24aa 100644
>>> --- a/gcc/doc/gcov.texi
>>> +++ b/gcc/doc/gcov.texi
>>> @@ -124,6 +124,7 @@ gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
>>>       [@option{-a}|@option{--all-blocks}]
>>>       [@option{-b}|@option{--branch-probabilities}]
>>>       [@option{-c}|@option{--branch-counts}]
>>> +     [@option{-g}|@option{--conditions}]
>>>       [@option{-d}|@option{--display-progress}]
>>>       [@option{-f}|@option{--function-summaries}]
>>>       [@option{-j}|@option{--json-format}]
>>> @@ -169,6 +170,13 @@ be shown, unless the @option{-u} option is given.
>>>  Write branch frequencies as the number of branches taken, rather than
>>>  the percentage of branches taken.
>>>  
>>> +@item -g
>>> +@itemx --conditions
>>> +Write condition coverage to the output file, and write condition summary info
>>> +to the standard output.  This option allows you to see if the conditions in
>>> +your program at least once had an independent effect on the outcome of the
>>> +boolean expression (modified condition/decision coverage).
> 
> Maybe you want to like -fprofile-conditions flag here, since that is
> needed at the instrumentaiton time.
>>> +@opindex Wno-coverage-too-many-conditions
>>> +@opindex Wcoverage-too-many-conditions
>>> +@item -Wno-coverage-too-many-conditions
>>> +Warn in case a condition have too many terms and GCC gives up coverage.
>>> +Coverage is given up when there are more terms in the conditional than there
>>> +are bits in a @code{gcov_type_unsigned}.  This warning is enabled by default.
> 
> Here you can explicitly mention -fprofile-conditions so user knows what
> kind of coverage you refer to.
>>> +
>>>  @opindex Wno-coverage-invalid-line-number
>>>  @opindex Wcoverage-invalid-line-number
>>>  @item -Wno-coverage-invalid-line-number
>>> @@ -16430,6 +16438,13 @@ Note that if a command line directly links source files, the corresponding
>>>  E.g. @code{gcc a.c b.c -o binary} would generate @file{binary-a.gcda} and
>>>  @file{binary-b.gcda} files.
>>>  
>>> +@item -fprofile-conditions
>>> +@opindex fprofile-conditions
>>> +Add code so that program conditions are instrumented.  During execution the
>>> +program records what terms in a conditional contributes to a decision.  The
>>> +data may be used to verify that all terms in a booleans are tested and have an
>>> +effect on the outcome of a condition.
> 
> I would say that the result can be read with gcov --conditions
>>> @@ -392,6 +394,28 @@ tag_arcs (const char *filename ATTRIBUTE_UNUSED,
>>>      }
>>>  }
>>>  
>>> +static void
>>> +tag_conditions (const char *filename ATTRIBUTE_UNUSED,
>>> +		unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
>>> +		unsigned depth)
> Coding style wants us to have a comment before functions
> summarizing what they do.
> Also with C++, one can ust skip "filename ATTRIBUTE_UNUSED" for unused
> args.
>>> diff --git a/gcc/gcov.cc b/gcc/gcov.cc
>>> index 2fad6aa7ede..ffedbb37483 100644
>>> --- a/gcc/gcov.cc
>>> +++ b/gcc/gcov.cc
>>> @@ -81,6 +81,7 @@ using namespace std;
>>>  class function_info;
>>>  class block_info;
>>>  class source_info;
>>> +class condition_info;
>>>  
>>>  /* Describes an arc between two basic blocks.  */
>>>  
>>> @@ -134,6 +135,28 @@ public:
>>>    vector<unsigned> lines;
>>>  };
>>>  
>>> +class condition_info
> Also block comment here.
>>> +{
>>> +public:
>>> +  condition_info ();
>>> +
>>> +  int popcount () const;
>>> +
>>> +  gcov_type_unsigned truev;
>>> +  gcov_type_unsigned falsev;
>>> +
>>> +  unsigned n_terms;
>>> +};
>>> +
>>> +condition_info::condition_info (): truev (0), falsev (0), n_terms (0)
>>> +{
>>> +}
>>> +
>>> +int condition_info::popcount () const
>>> +{
>>> +    return __builtin_popcountll (truev) + __builtin_popcountll (falsev);
> If you build with --disable-bootstrap and use non-GCC host compiler, I
> don't think we have a promise of __builtin_popcountll being supported.
> THere is popcount_hwi in hwint.h
>>> @@ -2456,6 +2573,13 @@ add_branch_counts (coverage_info *coverage, const arc_info *arc)
>>>      }
>>>  }
>>>  
>>> +static void
>>> +add_condition_counts (coverage_info *coverage, const block_info *block)
> Similarly here block comment will make coding conventions happy
>>> +{
>>> +  coverage->conditions += 2 * block->conditions.n_terms;
>>> +  coverage->conditions_covered += block->conditions.popcount ();
>>> +}
>>> +
>>>  /* Format COUNT, if flag_human_readable_numbers is set, return it human
>>>     readable format.  */
>>>  
>>> @@ -2559,6 +2683,18 @@ file_summary (const coverage_info *coverage)
>>>  		 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");
>>>      }
>>>  }
>>>  
>>> @@ -2793,6 +2929,12 @@ static void accumulate_line_info (line_info *line, source_info *src,
>>>  	 it != line->branches.end (); it++)
>>>        add_branch_counts (&src->coverage, *it);
>>>  
>>> +  if (add_coverage)
>>> +    for (vector<block_info *>::iterator it = line->blocks.begin ();
>>> +	 it != line->blocks.end (); it++)
>>> +      add_condition_counts (&src->coverage, *it);
>>> +
>>> +
>>>    if (!line->blocks.empty ())
>>>      {
>>>        /* The user expects the line count to be the number of times
>>> @@ -2894,6 +3036,33 @@ accumulate_line_counts (source_info *src)
>>>        }
>>>  }
>>>  
>>> +static void
>>> +output_conditions (FILE *gcov_file, const block_info *binfo)
> Again, I think we want a comment (even though it is easy to figure out
> what it does).
>>> +{
>>> +    const condition_info& info = binfo->conditions;
>>> +    if (info.n_terms == 0)
>>> +	return;
>>> +
>>> +    const int expected = 2 * info.n_terms;
>>> +    const int got = info.popcount ();
>>> +
>>> +    fnotice (gcov_file, "condition outcomes covered %d/%d\n", got, expected);
>>> +    if (expected == got)
>>> +	return;
>>> +
>>> +    for (unsigned i = 0; i < info.n_terms; i++)
>>> +    {
>>> +	gcov_type_unsigned index = 1;
>>> +	index <<= i;
>>> +	if ((index & info.truev & info.falsev))
>>> +	    continue;
>>> +
>>> +	const char *t = (index & info.truev) ? "" : "true";
>>> +	const char *f = (index & info.falsev) ? "" : " false";
>>> +	fnotice (gcov_file, "condition %2u not covered (%s%s)\n", i, t, f + !t[0]);
>>> +    }
>>> +}
>>> +
>>>  /* Output information about ARC number IX.  Returns nonzero if
>>>     anything is output.  */
>>>  
>>> @@ -3104,16 +3273,29 @@ output_line_details (FILE *f, const line_info *line, unsigned line_num)
>>>  	  if (flag_branches)
>>>  	    for (arc = (*it)->succ; arc; arc = arc->succ_next)
>>>  	      jx += output_branch_count (f, jx, arc);
>>> +
>>> +	  if (flag_conditions)
>>> +	      output_conditions (f, *it);
>>>  	}
>>>      }
>>> -  else if (flag_branches)
>>> +  else
>>>      {
>>> -      int ix;
>>> +      if (flag_branches)
>>> +	{
>>> +	  int ix;
>>> +
>>> +	  ix = 0;
>>> +	  for (vector<arc_info *>::const_iterator it = line->branches.begin ();
>>> +		  it != line->branches.end (); it++)
>>> +	      ix += output_branch_count (f, ix, (*it));
>>> +	}
>>>  
>>> -      ix = 0;
>>> -      for (vector<arc_info *>::const_iterator it = line->branches.begin ();
>>> -	   it != line->branches.end (); it++)
>>> -	ix += output_branch_count (f, ix, (*it));
>>> +      if (flag_conditions)
>>> +	{
>>> +	  for (vector<block_info *>::const_iterator it = line->blocks.begin ();
>>> +	       it != line->blocks.end (); it++)
>>> +	      output_conditions (f, *it);
>>> +	}
> 
> The gcov tool bits seems good to me except the nits above.  I will
> continue reading the instrumentation part and write incrementally.
> 
> Honza

I kept the profile-arcs/profille-coverage inline, but for the other feedback you
can consider it applied. I'll create some new patches to address them, then
squash them post-review. Thanks!

Jørgen

      reply	other threads:[~2023-06-30  6:53 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-13  0:59 Jørgen Kvalsvik
2023-06-23 10:11 ` Ping " Jørgen Kvalsvik
2023-06-23 10:28   ` Jan Hubicka
2023-06-30  6:53     ` Jørgen Kvalsvik [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=92d1bd15-662a-9765-d61c-babdf34abb57@woven.toyota \
    --to=jorgen.kvalsvik@woven.toyota \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=hubicka@ucw.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).