| // Test program to verify libunwind ret injection feature for execution flow | |
| // rebalancing. | |
| // | |
| // This test creates a multi-frame call stack and throws a C++ exception to | |
| // trigger libunwind's two-phase exception handling. The test verifies that | |
| // libunwind correctly injects the right amount of 'ret' instructions to | |
| // rebalance the execution flow when returning to the landing pad, which is | |
| // important for Apple Processor Trace analysis. | |
| #include <cstdio> | |
| #include <exception> | |
| #include <stdexcept> | |
| // Marker functions with noinline to ensure they appear in the stack. | |
| static void __attribute__((noinline)) func_d() { | |
| printf("In func_d, about to throw exception\n"); | |
| throw std::runtime_error("test exception"); | |
| } | |
| static void __attribute__((noinline)) func_c() { | |
| printf("In func_c\n"); | |
| func_d(); | |
| } | |
| static void __attribute__((noinline)) func_b() { | |
| printf("In func_b\n"); | |
| func_c(); | |
| } | |
| static void __attribute__((noinline)) func_a() { | |
| printf("In func_a\n"); | |
| func_b(); | |
| } | |
| int main(int argc, char *argv[]) { | |
| try { | |
| printf("In main, about to call func_a\n"); | |
| func_a(); | |
| printf("ERROR: Should not reach here\n"); | |
| return 1; | |
| } catch (const std::exception &e) { | |
| printf("Caught exception in main: %s\n", e.what()); | |
| return 0; | |
| } | |
| } |