https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120485

            Bug ID: 120485
           Summary: Superfluous branch coverage and condition coverage
                    before preprocessor directives
           Product: gcc
           Version: 16.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: gcov-profile
          Assignee: unassigned at gcc dot gnu.org
          Reporter: wentaoz5 at illinois dot edu
  Target Milestone: ---

Hit the issue when measuring coverage for
https://sources.debian.org/src/mawk/1.3.4.20200120-3.1/parse.c/#L1832

How to reproduce:

$ gcc --version
gcc (GCC) 16.0.0 20250511 (experimental)
Copyright (C) 2025 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ cat > test1.c << 'EOF'
int g;
int foo(int a, int b) {
#include "header.h"
    return g;
}

int main(void) {
    for (int i = 0; i < 1; i++)
        foo(0, 0);
    for (int i = 0; i < 20; i++)
        foo(1, 0);
    for (int i = 0; i < 300; i++)
        foo(0, 1);
    for (int i = 0; i < 4000; i++)
        foo(1, 1);
}
EOF

$ cat > header.h << EOF
    if (a && b)
        g++;
EOF

$ gcc --coverage -fcondition-coverage test1.c -o test1
$ ./test1
$ gcov -b -c --condition test1
$ cat header.h.gcov
...
     4321:    1:    if (a && b)
branch  0 taken 4020 (fallthrough)
branch  1 taken 301
branch  2 taken 4000 (fallthrough)
branch  3 taken 20
condition outcomes covered 4/4
     4000:    2:        g++;
...
$ cat test.c.gcov
...
     4321:    2:int foo(int a, int b) {
branch  0 taken 4020 (fallthrough)
branch  1 taken 301
condition outcomes covered 4/4
        -:    3:#include "header.h"
     4321:    4:    return g;
        -:    5:}
...

Branch and condition coverage for "a && b" is correctly shown in
header.h.gcov, but a copy of condition coverage and an incomplete copy of
branch coverage are incorrectly put after line 2 of test.c before
"#include".

Different ways of manifestation are possible, e.g.

     8040:    1:    if (a && b)
branch  0 taken 8000 (fallthrough)
branch  1 taken 40
     8000:    2:        return 0;
      642:    3:    return 1;

    17284:    1:int foo(int a, int b) {
branch  0 taken 8040 (fallthrough)
branch  1 taken 602
branch  2 taken 8040 (fallthrough)
branch  3 taken 602
condition outcomes covered 4/4
condition outcomes covered 4/4
        -:    2:#include "header.h"
        -:    3:}

This time two copies of condition coverage are both presented for the same
file.

A probably more common pattern is parser generated code, like in the mawk
example.

$ cat > this.c << EOF
int g;
int foo(int a, int b) {
#line 4 "the-same.c"
    if (a && b)
        g++;
#line 7 "this.c"
}

int main(void) {
    for (int i = 0; i < 1; i++)
        foo(0, 0);
    for (int i = 0; i < 20; i++)
        foo(1, 0);
    for (int i = 0; i < 300; i++)
        foo(0, 1);
    for (int i = 0; i < 4000; i++)
        foo(1, 1);
}
EOF

$ cp this.c the-same.c
$ gcc --coverage -fcondition-coverage this.c -o this
$ ./this
$ gcov -b -c --condition this
$ cat this.c.gcov

...
     8642:    2:int foo(int a, int b) {
branch  0 taken 8040 (fallthrough)
branch  1 taken 602
condition outcomes covered 4/4
        -:    3:#line 4 "the-same.c"
        -:    4:    if (a && b)
        -:    5:        g++;
        -:    6:#line 7 "this.c"
     8642:    7:}
...

The symptom is the same: extra branch and condition coverage is reported
after line 2.

Reply via email to