Ping.

On 2/12/25 19:00, Jørgen Kvalsvik wrote:
Ping.

On 1/31/25 10:35, Jørgen Kvalsvik wrote:
Record basic blocks that make up a conditional expression with
-fcondition-coverage and report when using the gcov -w/--verbose flag.
This makes the report more accurate when basic blocks are included as
there may be blocks in-between the actual Boolean expressions, e.g. when
there a term is the result of a function call. This helps understanding
the report as gcc uses the CFG, and not source code, to figure out
MC/DC, which is somewhat lost in gcov. While it does not make a
tremendous difference for the gcov report directly, it opens up for more
analysis and clearer reporting.

This change includes deleting the GCOV_TAG_COND_* macros as the .gcno
records are now dynamic in length.

Here is an example with, comparing two programs:

int main() {
   int a = 1;
   int b = 0;

   if (a && b)
     printf ("Success!\n");
   else
     printf ("Failure!\n");
}

int f(int);
int g(int);
int main() {
   int a = 1;
   int b = 0;

   if (f (a) && g (b))
     printf ("Success!\n");
   else
     printf ("Failure!\n");
}

And the corresponding reports:
$ gcov -tagw p1 p2
         1:    3:int main() {
         1:    4:  int a = 1;
         1:    5:  int b = 0;
         -:    6:
         1:    7:  if (a && b)
         1:    7-block 2 (BB 2)
condition outcomes covered 1/4
BBs 2 3
condition  0 not covered (true false)
condition  1 not covered (true)
         1:    7-block 3 (BB 3)
     #####:    8:    printf ("Success!\n");
     %%%%%:    8-block 4 (BB 4)
         -:    9:  else
         1:   10:    printf ("Failure!\n");
         1:   10-block 5 (BB 5)
         -:   11:}

     #####:    6:int main() {
     #####:    7:  int a = 1;
     #####:    8:  int b = 0;
         -:    9:
     #####:   10:  if (f (a) && g (b))
     %%%%%:   10-block 2 (BB 2)
condition outcomes covered 0/4
BBs 3 5
condition  0 not covered (true false)
condition  1 not covered (true false)
     %%%%%:   10-block 4 (BB 4)
     #####:   11:    printf ("Success!\n");
     %%%%%:   11-block 6 (BB 6)
         -:   12:  else
     #####:   13:    printf ("Failure!\n");
     %%%%%:   13-block 7 (BB 7)
         -:   14:}

gcc/ChangeLog:

    * doc/gcov.texi: Add example.
         * gcov-dump.cc (tag_conditions): Print basic blocks, not length.
    * gcov-io.h (GCOV_TAG_CONDS_LENGTH): Delete.
    (GCOV_TAG_CONDS_NUM): Likewise.
    * gcov.cc (output_intermediate_json_line): Output basic blocks.
    (read_graph_file): Read basic blocks.
    (output_conditions): Output basic blocks.
    * profile.cc (branch_prob): Write basic blocks for conditions.
---
  gcc/doc/gcov.texi | 32 ++++++++++++++++++++++++++++++++
  gcc/gcov-dump.cc  | 12 +++++++-----
  gcc/gcov-io.h     |  2 --
  gcc/gcov.cc       | 20 +++++++++++++++++++-
  gcc/profile.cc    | 11 +++++++++--
  5 files changed, 67 insertions(+), 10 deletions(-)

diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index dda279fbff3..268e9e553f3 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -423,6 +423,7 @@ Each @var{condition} has the following form:
    "covered": 2,
    "not_covered_false": [],
    "not_covered_true": [0, 1],
+  "basic_blocks": [2, 3]
  @}
  @end smallexample
@@ -989,6 +990,37 @@ condition  1 not covered (true)
          -:   12:@}
  @end smallexample
+With @option{-w}, each condition will also print the basic blocks that
+make up the decision.
+
+@smallexample
+$ gcov -t -m -g -a -w tmp
+        -:    0:Source:tmp.c
+        -:    0:Graph:tmp.gcno
+        -:    0:Data:tmp.gcda
+        -:    0:Runs:1
+        -:    1:#include <stdio.h>
+        -:    2:
+        1:    3:int main()
+        -:    4:@{
+        1:    5:  int a = 1;
+        1:    6:  int b = 0;
+        -:    7:
+        1:    7:  if (a && b)
+        1:    7-block 2 (BB 2)
+condition outcomes covered 1/4
+BBs 2 3
+condition  0 not covered (true false)
+condition  1 not covered (true)
+        1:    7-block 3 (BB 3)
+    #####:    8:    printf ("Success!\n");
+    %%%%%:    8-block 4 (BB 4)
+        -:    9:  else
+        1:   10:    printf ("Failure!\n");
+        1:   10-block 5 (BB 5)
+        -:   12:@}
+@end smallexample
+
  The execution counts are cumulative.  If the example program were
  executed again without removing the @file{.gcda} file, the count for the
  number of times each line in the source was executed would be added to
diff --git a/gcc/gcov-dump.cc b/gcc/gcov-dump.cc
index cc7f8a9ebfb..642e58c22bf 100644
--- a/gcc/gcov-dump.cc
+++ b/gcc/gcov-dump.cc
@@ -396,23 +396,25 @@ tag_arcs (const char *filename ATTRIBUTE_UNUSED,
  /* Print number of conditions (not outcomes, i.e. if (x && y) is 2, not 4).  */
  static void
-tag_conditions (const char *filename, unsigned /* tag */, int length,
+tag_conditions (const char *filename, unsigned /* tag */, int /* length */,
          unsigned depth)
  {
-  unsigned n_conditions = GCOV_TAG_CONDS_NUM (length);
+  unsigned n_conditions = gcov_read_unsigned ();
    printf (" %u conditions", n_conditions);
    if (flag_dump_contents)
      {
        for (unsigned ix = 0; ix != n_conditions; ix++)
      {
-      const unsigned blockno = gcov_read_unsigned ();
+      /* Skip the anchor -- it will be included with the blocks  */
+      (void)gcov_read_unsigned ();
        const unsigned nterms = gcov_read_unsigned ();
        printf ("\n");
        print_prefix (filename, depth, gcov_position ());
-      printf (VALUE_PADDING_PREFIX "block %u:", blockno);
-      printf (" %u", nterms);
+      printf (VALUE_PADDING_PREFIX "blocks:");
+      for (unsigned i = 0; i != nterms; ++i)
+        printf (" %u", gcov_read_unsigned ());
      }
      }
  }
diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h
index 9ad6ad98282..8e1a3c8c903 100644
--- a/gcc/gcov-io.h
+++ b/gcc/gcov-io.h
@@ -262,8 +262,6 @@ typedef uint64_t gcov_type_unsigned;
  #define GCOV_TAG_ARCS_LENGTH(NUM)  (1 + (NUM) * 2 * GCOV_WORD_SIZE)
  #define GCOV_TAG_ARCS_NUM(LENGTH)  (((LENGTH / GCOV_WORD_SIZE) - 1) / 2)
  #define GCOV_TAG_CONDS           ((gcov_unsigned_t)0x01470000)
-#define GCOV_TAG_CONDS_LENGTH(NUM) ((NUM) * 2 * GCOV_WORD_SIZE)
-#define GCOV_TAG_CONDS_NUM(LENGTH) (((LENGTH) / GCOV_WORD_SIZE) / 2)
  #define GCOV_TAG_LINES         ((gcov_unsigned_t)0x01450000)
  #define GCOV_TAG_COUNTER_BASE      ((gcov_unsigned_t)0x01a10000)
  #define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2 * GCOV_WORD_SIZE)
diff --git a/gcc/gcov.cc b/gcc/gcov.cc
index 3e6f2e4212a..ef5d828d1a0 100644
--- a/gcc/gcov.cc
+++ b/gcc/gcov.cc
@@ -159,6 +159,9 @@ public:
    /* Number of terms in the expression; if (x) -> 1, if (x && y) -> 2 etc.  */
    unsigned n_terms;
+
+  /* The block IDs that make up this expression.  */
+  vector<unsigned> bb_ids;
  };
  condition_info::condition_info (): truev (0), falsev (0), n_terms (0)
@@ -1313,6 +1316,11 @@ output_intermediate_json_line (json::array *object,
            }
        }
      conditions->append (cond);
+
+    json::array *bbs = new json::array ();
+    cond->set ("basic_blocks", bbs);
+    for (unsigned bb_id : info.bb_ids)
+      bbs->append (new json::integer_number (bb_id));
        }
    }
@@ -2174,7 +2182,7 @@ read_graph_file (void)
      }
        else if (fn && tag == GCOV_TAG_CONDS)
      {
-      unsigned num_dests = GCOV_TAG_CONDS_NUM (length);
+      unsigned num_dests = gcov_read_unsigned ();
        if (!fn->conditions.empty ())
          fnotice (stderr, "%s:already seen conditions for '%s'\n",
@@ -2191,6 +2199,8 @@ read_graph_file (void)
            condition_info *info = &fn->blocks[idx].conditions;
            info->n_terms = gcov_read_unsigned ();
+          for (unsigned k = 0; k != info->n_terms; ++k)
+        info->bb_ids.push_back (gcov_read_unsigned());
            fn->conditions[i] = info;
          }
      }
@@ -3163,6 +3173,14 @@ output_conditions (FILE *gcov_file, const block_info *binfo)
      const int got = info.popcount ();
      fnotice (gcov_file, "condition outcomes covered %d/%d\n", got, expected);
+    if (flag_verbose)
+      {
+    fnotice (gcov_file, "BBs");
+    for (unsigned bb_id : info.bb_ids)
+      fnotice (gcov_file, " %u", bb_id);
+    fnotice (gcov_file, "\n");
+      }
+
      if (expected == got)
      return;
diff --git a/gcc/profile.cc b/gcc/profile.cc
index acc0ae0cc20..76235dd2bee 100644
--- a/gcc/profile.cc
+++ b/gcc/profile.cc
@@ -1559,8 +1559,10 @@ branch_prob (bool thunk)
      {
        gcov_position_t offset {};
        if (output_to_file)
+        {
            offset = gcov_write_tag (GCOV_TAG_CONDS);
-
+          gcov_write_unsigned (nconds);
+        }
        for (size_t i = 0; i != nconds; ++i)
          {
            array_slice<basic_block> expr = cov_blocks (cov, i);
@@ -1569,12 +1571,17 @@ branch_prob (bool thunk)
            gcc_assert (expr.is_valid ());
            gcc_assert (masks.is_valid ());
            gcc_assert (maps.is_valid ());
-
            size_t terms = instrument_decisions (expr, i, maps, masks);
            if (output_to_file)
          {
            gcov_write_unsigned (expr.front ()->index);
            gcov_write_unsigned (terms);
+          /* Write the basic block ids of this condition, in order so
+             that the nth item is the nth term of the conditional
+             expression.  */
+          for (basic_block b : expr)
+            if (bitmap_bit_p (maps[0], b->index))
+              gcov_write_unsigned (b->index);
          }
          }
        if (output_to_file)


Reply via email to