Issue 138871
Summary [coverage] incorrect coverage when non-throwing code exists within a try block
Labels new issue
Assignees
Reporter justincady
    Code coverage reporting is incorrect for `try`/`catch` blocks where the contents of the `try` _cannot_ throw. Example:

```
// coverage.cc
#include <stdio.h>
#include <stdlib.h>

static __attribute__((noinline)) int do_throw(bool b) {
  if (b) {
    throw b;
  }
  return 1;
}

int main() {
  int i = 0;
  try {
    i = 1;
  } catch (...) {
    abort();
  }
  printf("%s\n", __func__);
  return i;
}
```

```
# build.sh
/usr/bin/clang++ --version | /bin/grep "clang version"
/usr/bin/clang++ -fprofile-instr-generate -fcoverage-mapping coverage.cc -o coverage
./coverage
/usr/bin/llvm-profdata merge -sparse default.profraw -o default.profdata
/usr/bin/llvm-cov show ./coverage -instr-profile=""
```

The coverage report incorrectly marks lines following `catch` as uncovered:

```
$ ./build.sh
clang version 21.0.0
main
    1|       |#include <stdio.h>
    2|       |#include <stdlib.h>
    3|       |
    4|      0|static __attribute__((noinline)) int do_throw(bool b) {
    5|      0|  if (b) {
    6|      0|    throw b;
    7|      0|  }
    8|      0|  return 1;
    9|      0|}
   10| |
   11|      1|int main() {
   12|      1|  int i = 0;
   13|      1| try {
   14|      1|    i = 1;
   15|      1|  } catch (...) {
   16| 0|    abort();
   17|      0|  }
   18|      0|  printf("%s\n", __func__); // INCORRECT
   19|      0|  return i;                 // INCORRECT
   20|      1|}
```

If the example is updated with a call that _may_ throw, everything works as expected:

```
$ ./build.sh
clang version 21.0.0
main
    1|       |#include <stdio.h>
    2| |#include <stdlib.h>
    3|       |
    4|      1|static __attribute__((noinline)) int do_throw(bool b) {
    5|      1|  if (b) {
 6|      0|    throw b;
    7|      0|  }
    8|      1|  return 1;
 9|      1|}
   10|       |
   11|      1|int main() {
   12|      1|  int i = 0;
   13|      1|  try {
   14|      1|    do_throw(false); // MAY THROW NOW
   15|      1|  } catch (...) {
   16|      0|    abort();
 17|      0|  }
   18|      1|  printf("%s\n", __func__); // CORRECT
 19|      1|  return i;                 // CORRECT
   20| 1|}
```

Note that the mapping for `main` appears to be identical for both cases. First, for the non-throwing case:

```
$ clang++ -fprofile-instr-generate -fcoverage-mapping coverage.cc -o coverage -Xclang -dump-coverage-mapping
main:
  File 0, 11:12 -> 20:2 = #0
  File 0, 13:7 -> 15:4 = #0
  File 0, 15:17 -> 17:4 = #2
  Gap,File 0, 17:4 -> 18:3 = 0
 File 0, 18:3 -> 19:11 = #1
[...]
```

And for the maybe throwing case:

```
$ clang++ -fprofile-instr-generate -fcoverage-mapping coverage.cc -o coverage -Xclang -dump-coverage-mapping
main:
  File 0, 11:12 -> 20:2 = #0
  File 0, 13:7 -> 15:4 = #0
  File 0, 15:17 -> 17:4 = #2
  Gap,File 0, 17:4 -> 18:3 = 0
  File 0, 18:3 -> 19:11 = #1
[...]
```

Thus I _suspect_ this to be a problem with exception handling removal in codegen.
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to