llvm / llvm/llvm-project

[Coverage] Nested macros in `while` loop-condition are not counted correctly

Open
#176,953 1 comment 0 reactions 0 assignees View on GitHub
coverage
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

In the following example, llvm-cov can correctly show branch coverage for `skip_spaces_direct`, but fails to do branch coverage for `skip_spaces_indirect`.

```c++
#define IS_SPACE(c) ((c) == 0x20)

#define INDIRECT_IS_SPACE(c) IS_SPACE(c)

char* skip_spaces_direct(char* input){
while(IS_SPACE(input[0])) // <-- This produces branches in the coverage report
input++;

return input;
}

char* skip_spaces_indirect(char* input){
while(INDIRECT_IS_SPACE(input[0])) // <-- This does not produce branches in the coverage report
input++;

return input;
}

int main(int argc, char** argv){
char* to_test = argv[0];
char* to_test2 = argv[0];
if(argc > 1){
to_test = argv[1];
to_test2 = argv[1];
}
if(skip_spaces_direct(to_test)[0] == skip_spaces_indirect(to_test2)[0])
return 0;
return 1;
}
```

llvm-cov produces the following coverage info, with line 13 missing the expansion and branch info. The branch itself is actually counted correctly (in line 1), the counter is just not associated with the location where it is expanded to, which means it also does not end up in llvm-cov's totals:
```
1| 2|#define IS_SPACE(c) ((c) == 0x20)
------------------
| Branch (1:22): [True: 0, False: 1]
| Branch (1:22): [True: 0, False: 1]
------------------
2| |
3| |#define INDIRECT_IS_SPACE(c) IS_SPACE(c)
4| |
5| 1|char* skip_spaces_direct(char* input){
6| 1| while(IS_SPACE(input[0])) // <-- This produces branches in the coverage report
------------------
| | 1| 1|#define IS_SPACE(c) ((c) == 0x20)
| | ------------------
| | | Branch (1:22): [True: 0, False: 1]
| | ------------------
------------------
7| 0| input++;
8| |
9| 1| return input;
10| 1|}
11| |
12| 1|char* skip_spaces_indirect(char* input){
13| 1| while(INDIRECT_IS_SPACE(input[0])) // <-- This does not produce branches in the coverage report
14| 0| input++;
15| |
16| 1| return input;
17| 1|}
18| |
19| 1|int main(int argc, char** argv){
20| 1| char* to_test = argv[0];
21| 1| char* to_test2 = argv[0];
22| 1| if(argc > 1){
------------------
| Branch (22:8): [True: 0, False: 1]
------------------
23| 0| to_test = argv[1];
24| 0| to_test2 = argv[1];
25| 0| }
26| 1| if(skip_spaces_direct(to_test)[0] == skip_spaces_indirect(to_test2)[0])
------------------
| Branch (26:8): [True: 1, False: 0]
------------------
27| 1| return 0;
28| 0| return 1;
29| 1|}
```

With `clang -fprofile-instr-generate -fcoverage-mapping -Xclang -dump-coverage-mapping` we can see that the issue seems to be that the `Expansion` is not being generated for the indirect case. Unfortunately, I have not been able to find the root cause for this.

```
_Z18skip_spaces_directPc:
File 0, 5:38 -> 10:2 = #0
Expansion,File 0, 6:9 -> 6:17 = (#0 + #1) (Expanded file = 1)
Gap,File 0, 6:28 -> 7:5 = #1
File 0, 7:5 -> 7:12 = #1
Skipped,File 0, 8:1 -> 8:1 = 0
File 1, 1:22 -> 1:35 = (#0 + #1)
Branch,File 1, 1:22 -> 1:35 = #1, #0
_Z20skip_spaces_indirectPc:
File 0, 12:40 -> 17:2 = #0
Gap,File 0, 13:37 -> 14:5 = #1
File 0, 14:5 -> 14:12 = #1
Skipped,File 0, 15:1 -> 15:1 = 0
File 1, 1:22 -> 1:35 = (#0 + #1)
Branch,File 1, 1:22 -> 1:35 = #1, #0
```

https://godbolt.org/z/ndYfvMc5r

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.