Bug 117415 - Bogus execution count for assignment to *func()
Summary: Bogus execution count for assignment to *func()
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: gcov-profile (show other bugs)
Version: 15.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-11-02 03:04 UTC by Wentao Zhang
Modified: 2024-11-02 12:03 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Wentao Zhang 2024-11-02 03:04:48 UTC
I hit the issue when dealing with https://github.com/apache/httpd/blob/2.4.62/server/mpm_unix.c#L901. After reduction, I ended up with two variants with slightly different triggering conditions. See comments in code for details.

How to reproduce:

$ gcc --version

gcc (GCC) 15.0.0 20240923 (experimental)
Copyright (C) 2024 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 > test.c << 'EOF'
int arr[10];

int *foo() {
    return arr;
}

int bar() {
    return 0;
}

int main(void) {
    int *a = arr;
    int zero = 0;

    if (zero) {} else {
        // Variant 1 triggering conditions:
        //
        // 1. RHS of assignment is a function call
        // 2. The first function name and assignment are put in one line;
        //    the second function name is put in the other line
        // 3. Inside if(){...}, else{...}, for(){...} etc
        *foo()=
            bar();
    }

    // Variant 2 triggering conditions:
    //
    // 1. RHS of assignment is a dereference
    // 2. The function name and assignment are put in one line; the second
    //    dereference is put in the other line
    // 3. The operand of dereference is referenced elsewhere
    *foo()=
        *a;
    &a;

    return 0;
}
EOF
$ gcc --coverage test.c -o test
$ ./test
$ gcov test
$ cat test.c.gcov
...
        2:   22:        *foo()=
        1:   23:            bar();
...
        2:   32:    *foo()=
        1:   33:        *a;
...

Line 22 and 32 are reported to executed twice, which is hard to interpret.
Comment 1 Andrew Pinski 2024-11-02 03:08:21 UTC
I think this is the same underlying issue as PR 117412.
Comment 2 Wentao Zhang 2024-11-02 03:42:16 UTC
(In reply to Andrew Pinski from comment #1)
> I think this is the same underlying issue as PR 117412.

Hi Andrew, thanks for your reply!

In practice we've seen many such cases caused by multi-line expressions. Internally we have been triaging based on the minimal language constructs needed to reproduce the behavior (triggering conditions). That's why I filed two different bug reports. Do you have suggestions for triaging or debugging in general?

Thanks!
Comment 3 Jørgen Kvalsvik 2024-11-02 12:03:16 UTC
From a quick look it seems like the problem is fundamentally the difference in how gcc counts executions (on the basic block) and how that is mapped to lines. I don't know if there a complete fix if the expectation is lines [1], other than maybe applying some heuristics to not count multiple basic blocks in a straight line that map to the same line. 

[1] For example, you can remove all newlines from the source and it would most likely still be a valid C program. How would you count "lines executed"?