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)