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.