From 40c451b28f69a3d56cc40579639b1799a769b92f Mon Sep 17 00:00:00 2001 From: Justin Cady Date: Wed, 7 May 2025 09:15:01 -0400 Subject: [PATCH] [Coverage] Add testing to validate code coverage for exceptions 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. --- .../test/profile/Linux/coverage-exception.cpp | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100755 compiler-rt/test/profile/Linux/coverage-exception.cpp diff --git a/compiler-rt/test/profile/Linux/coverage-exception.cpp b/compiler-rt/test/profile/Linux/coverage-exception.cpp new file mode 100755 index 0000000000000..3f0c907764269 --- /dev/null +++ b/compiler-rt/test/profile/Linux/coverage-exception.cpp @@ -0,0 +1,144 @@ +// 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 +#include + +#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(); \ + } + +static __attribute__((noinline)) int do_throw(bool b) { + if (b) + throw b; + return 1; +} + +// clang-format off +static +int test_no_exception() { // CHECK: [[@LINE]]| 1|int test_no_exception() + try { // CHECK: [[@LINE]]| 1| try { + do_throw(false); // CHECK: [[@LINE]]| 1| do_throw( + } 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_no_exception_macro() { // CHECK: [[@LINE]]| 1|int test_no_exception_macro() + TRY_AND_CATCH_ALL(do_throw(false)); // 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() { // CHECK: [[@LINE]]| 1|int test_exception() + try { // CHECK: [[@LINE]]| 1| try { + do_throw(true); // CHECK: [[@LINE]]| 1| do_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(do_throw(true)); // 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(do_throw(true)); // 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() + TRY_MAYBE_CRASH(do_throw(false)); // 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() + TRY_AND_CATCH_CRASHES(do_throw(false)); // 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 { + do_throw(true); // CHECK: [[@LINE]]| 0| do_throw( + } // 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 { + do_throw(true); // CHECK: [[@LINE]]| 1| do_throw( + } catch (double) { // CHECK: [[@LINE]]| 1| } catch (double) + abort(); // CHECK: [[@LINE]]| 0| abort( + } catch (bool) { // CHECK: [[@LINE]]| 1| } catch (bool) + printf("bool\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