-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[Coverage] Add testing to validate code coverage for exceptions #133463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-pgo @llvm/pr-subscribers-clang-codegen Author: Justin Cady (justincady) ChangesIn cases where a terminating statement exists within the try or catch This change adjusts the mapping such that an extra region is only The testing validates coverage more broadly than the above to ensure Full diff: https://github.com/llvm/llvm-project/pull/133463.diff 3 Files Affected:
diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index 73811d15979d5..f3b819bcf3b20 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -2123,11 +2123,17 @@ struct CounterCoverageMappingBuilder
Counter ParentCount = getRegion().getCounter();
propagateCounts(ParentCount, S->getTryBlock());
+ bool TryHasTerminateStmt = HasTerminateStmt;
+
for (unsigned I = 0, E = S->getNumHandlers(); I < E; ++I)
Visit(S->getHandler(I));
- Counter ExitCount = getRegionCounter(S);
- pushRegion(ExitCount);
+ if (TryHasTerminateStmt) {
+ Counter ExitCount = getRegionCounter(S);
+ pushRegion(ExitCount);
+ }
+
+ HasTerminateStmt = TryHasTerminateStmt;
}
void VisitCXXCatchStmt(const CXXCatchStmt *S) {
diff --git a/clang/test/CoverageMapping/trycatch.cpp b/clang/test/CoverageMapping/trycatch.cpp
index 89fae8af9b720..e5d2482c6b151 100644
--- a/clang/test/CoverageMapping/trycatch.cpp
+++ b/clang/test/CoverageMapping/trycatch.cpp
@@ -33,5 +33,5 @@ int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@
catch(const Warning &w) { // CHECK-NEXT: File 0, [[@LINE]]:27 -> [[@LINE+2]]:4 = #4
j = 0;
}
- return 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE]]:11 = #1
-}
+ return 0; // CHECK-NOT: File 0
+} // CHECK-NOT: File 0
diff --git a/compiler-rt/test/profile/Linux/coverage-exception.cpp b/compiler-rt/test/profile/Linux/coverage-exception.cpp
new file mode 100644
index 0000000000000..c12303363fbb6
--- /dev/null
+++ b/compiler-rt/test/profile/Linux/coverage-exception.cpp
@@ -0,0 +1,142 @@
+// REQUIRES: lld-available
+// XFAIL: powerpc64-target-arch
+
+// RUN: %clangxx_profgen -std=c++17 -fuse-ld=lld -fcoverage-mapping -o %t %s
+// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t
+// RUN: llvm-profdata merge -o %t.profdata %t.profraw
+// RUN: llvm-cov show %t -instr-profile=%t.profdata 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define TRY_AND_CATCH_ALL(x) \
+ try { \
+ (x); \
+ } catch (...) { \
+ }
+
+#define TRY_MAYBE_CRASH(x) \
+ try { \
+ if ((x)) { \
+ printf("no crash\n"); \
+ } else { \
+ abort(); \
+ } \
+ } catch (...) { \
+ }
+
+#define TRY_AND_CATCH_CRASHES(x) \
+ try { \
+ (x); \
+ } catch (...) { \
+ abort(); \
+ }
+
+// clang-format off
+static
+int test_no_exception() { // CHECK: [[@LINE]]| 1|int test_no_exception()
+ int i = 0; // CHECK: [[@LINE]]| 1| int i
+ try { // CHECK: [[@LINE]]| 1| try {
+ i = 1; // CHECK: [[@LINE]]| 1| i =
+ } catch (...) { // CHECK: [[@LINE]]| 1| } catch (
+ abort(); // CHECK: [[@LINE]]| 0| abort();
+ } // CHECK: [[@LINE]]| 0| }
+ printf("%s: %u\n", __func__, i); // CHECK: [[@LINE]]| 1| printf(
+ return 0; // CHECK: [[@LINE]]| 1| return
+} // CHECK: [[@LINE]]| 1|}
+
+static
+int test_no_exception_macro() { // CHECK: [[@LINE]]| 1|int test_no_exception_macro()
+ int i = 0; // CHECK: [[@LINE]]| 1| int i
+ TRY_AND_CATCH_ALL(i = 1); // CHECK: [[@LINE]]| 1| TRY_AND_CATCH_ALL(
+ printf("%s: %u\n", __func__, i); // CHECK: [[@LINE]]| 1| printf(
+ return 0; // CHECK: [[@LINE]]| 1| return
+} // CHECK: [[@LINE]]| 1|}
+
+static
+int test_exception() { // CHECK: [[@LINE]]| 1|int test_exception()
+ try { // CHECK: [[@LINE]]| 1| try {
+ throw 1; // CHECK: [[@LINE]]| 1| throw
+ } catch (...) { // CHECK: [[@LINE]]| 1| } catch (
+ printf("%s\n", __func__); // CHECK: [[@LINE]]| 1| printf(
+ } // CHECK: [[@LINE]]| 1| }
+ return 0; // CHECK: [[@LINE]]| 1| return
+} // CHECK: [[@LINE]]| 1|}
+
+static
+int test_exception_macro() { // CHECK: [[@LINE]]| 1|int test_exception_macro()
+ TRY_AND_CATCH_ALL(throw 1); // CHECK: [[@LINE]]| 1| TRY_AND_CATCH_ALL(
+ printf("%s\n", __func__); // CHECK: [[@LINE]]| 1| printf(
+ return 0; // CHECK: [[@LINE]]| 1| return
+} // CHECK: [[@LINE]]| 1|}
+
+static
+int test_exception_macro_nested() { // CHECK: [[@LINE]]| 1|int test_exception_macro_nested()
+ try { // CHECK: [[@LINE]]| 1| try {
+ TRY_AND_CATCH_ALL(throw 1); // CHECK: [[@LINE]]| 1| TRY_AND_CATCH_ALL(
+ } catch (...) { // CHECK: [[@LINE]]| 1| } catch (
+ abort(); // CHECK: [[@LINE]]| 0| abort();
+ } // CHECK: [[@LINE]]| 0| }
+ printf("%s\n", __func__); // CHECK: [[@LINE]]| 1| printf(
+ return 0; // CHECK: [[@LINE]]| 1| return
+} // CHECK: [[@LINE]]| 1|}
+
+static
+int test_exception_try_crash() { // CHECK: [[@LINE]]| 1|int test_exception_try_crash()
+ int i = 1; // CHECK: [[@LINE]]| 1| int i
+ TRY_MAYBE_CRASH(i); // CHECK: [[@LINE]]| 1| TRY_MAYBE_CRASH(
+ printf("%s\n", __func__); // CHECK: [[@LINE]]| 1| printf(
+ return 0; // CHECK: [[@LINE]]| 1| return
+} // CHECK: [[@LINE]]| 1|}
+
+static
+int test_exception_crash() { // CHECK: [[@LINE]]| 1|int test_exception_crash()
+ int i = 0; // CHECK: [[@LINE]]| 1| int i
+ TRY_AND_CATCH_CRASHES(i = 1); // CHECK: [[@LINE]]| 1| TRY_AND_CATCH_CRASHES(
+ printf("%s\n", __func__); // CHECK: [[@LINE]]| 1| printf(
+ return 0; // CHECK: [[@LINE]]| 1| return
+} // CHECK: [[@LINE]]| 1|}
+
+static
+int test_conditional(int i) { // CHECK: [[@LINE]]| 1|int test_conditional(int i)
+ try { // CHECK: [[@LINE]]| 1| try {
+ if (i % 2 == 0) { // CHECK: [[@LINE]]| 1| if (
+ printf("%s\n", __func__); // CHECK: [[@LINE]]| 1| printf(
+ } else { // CHECK: [[@LINE]]| 1| } else {
+ abort(); // CHECK: [[@LINE]]| 0| abort();
+ } // CHECK: [[@LINE]]| 0| }
+ } catch (...) { // CHECK: [[@LINE]]| 1| } catch (
+ abort(); // CHECK: [[@LINE]]| 0| abort();
+ } // CHECK: [[@LINE]]| 0| }
+ return 0; // CHECK: [[@LINE]]| 1| return
+}
+
+static
+int test_multiple_catch() { // CHECK: [[@LINE]]| 1|int test_multiple_catch()
+ try { // CHECK: [[@LINE]]| 1| try {
+ throw 1; // CHECK: [[@LINE]]| 1| throw
+ } catch (double) { // CHECK: [[@LINE]]| 1| } catch (double)
+ abort(); // CHECK: [[@LINE]]| 0| abort();
+ } catch (int) { // CHECK: [[@LINE]]| 1| } catch (int)
+ printf("int\n"); // CHECK: [[@LINE]]| 1| printf(
+ } catch (float) { // CHECK: [[@LINE]]| 1| } catch (float)
+ abort(); // CHECK: [[@LINE]]| 0| abort();
+ } catch (...) { // CHECK: [[@LINE]]| 0| } catch (
+ abort(); // CHECK: [[@LINE]]| 0| abort();
+ } // CHECK: [[@LINE]]| 0| }
+ return 0; // CHECK: [[@LINE]]| 1| return
+} // CHECK: [[@LINE]]| 1|}
+
+int main() { // CHECK: [[@LINE]]| 1|int main()
+ test_no_exception(); // CHECK: [[@LINE]]| 1| test_no_exception(
+ test_no_exception_macro(); // CHECK: [[@LINE]]| 1| test_no_exception_macro(
+ test_exception(); // CHECK: [[@LINE]]| 1| test_exception(
+ test_exception_macro(); // CHECK: [[@LINE]]| 1| test_exception_macro(
+ test_exception_macro_nested(); // CHECK: [[@LINE]]| 1| test_exception_macro_nested(
+ test_exception_try_crash(); // CHECK: [[@LINE]]| 1| test_exception_try_crash(
+ test_exception_crash(); // CHECK: [[@LINE]]| 1| test_exception_crash(
+ test_conditional(2); // CHECK: [[@LINE]]| 1| test_conditional(
+ test_multiple_catch(); // CHECK: [[@LINE]]| 1| test_multiple_catch(
+ return 0; // CHECK: [[@LINE]]| 1| return
+} // CHECK: [[@LINE]]| 1|}
+// clang-format on
|
In testing this against a large codebase I found an unhandled case that leads to incorrect coverage. I'll be updating the PR in the near future. |
6f79ddb
to
f96b527
Compare
Further investigation revealed the true root cause, filed as #138871. I have updated this PR to instead only add tests that are currently working, as I found it helpful to have this validation when investigating the linked issue. |
Ping. :) Updated the description to make it clear this is a test-only change. It just validates existing coverage behavior in anticipation of other changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
While investigating an issue with code coverage reporting around exceptions it was useful to have a baseline of what works today. This change adds end-to-end testing to validate code coverage behavior that is currently working with regards to exception handling.
f96b527
to
40c451b
Compare
…#133463) While investigating an issue with code coverage reporting around exceptions it was useful to have a baseline of what works today. This change adds end-to-end testing to validate code coverage behavior that is currently working with regards to exception handling.
While investigating an issue with code coverage reporting around
exceptions it was useful to have a baseline of what works today.
This change adds end-to-end testing to validate code coverage behavior
that is currently working with regards to exception handling.