#include #include #include #include #include #include #include using namespace std::literals; // Raw trace tests // This is fickle, however, it's the only way to do it really. I've gotten it reasonably reliable test in practice. // Sanitizers do interfere. #ifndef CPPTRACE_SANITIZER_BUILD // NOTE: MSVC likes creating trampoline-like entries for non-static functions CPPTRACE_FORCE_NO_INLINE static void raw_trace_basic() { auto raw_trace = cpptrace::generate_raw_trace(); // look for within 90 bytes of the start of the function ASSERT_GE(raw_trace.frames.size(), 1); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(raw_trace_basic)); EXPECT_LE(raw_trace.frames[0], reinterpret_cast(raw_trace_basic) + 90); } #ifndef _MSC_VER CPPTRACE_FORCE_NO_INLINE void raw_trace_basic_precise() { a: auto raw_trace = cpptrace::generate_raw_trace(); b: // This is stupid, but without it gcc was optimizing both &&a and &&b to point to the start of the function's body volatile auto x = 0; if(x) { goto* &&a; } if(x) { goto* &&b; } ASSERT_GE(raw_trace.frames.size(), 1); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(&&a)); EXPECT_LE(raw_trace.frames[0], reinterpret_cast(&&b)); } #endif TEST(RawTrace, Basic) { raw_trace_basic(); #ifndef _MSC_VER raw_trace_basic_precise(); #endif [[maybe_unused]] volatile int x = 0; // prevent raw_trace_basic_precise() above being a jmp } CPPTRACE_FORCE_NO_INLINE static void raw_trace_multi_2( cpptrace::frame_ptr parent_low_bound, cpptrace::frame_ptr parent_high_bound ) { auto raw_trace = cpptrace::generate_raw_trace(); ASSERT_GE(raw_trace.frames.size(), 2); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_2)); EXPECT_LE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_2) + 90); EXPECT_GE(raw_trace.frames[1], parent_low_bound); EXPECT_LE(raw_trace.frames[1], parent_high_bound); } CPPTRACE_FORCE_NO_INLINE static void raw_trace_multi_1() { auto raw_trace = cpptrace::generate_raw_trace(); raw_trace_multi_2(reinterpret_cast(raw_trace_multi_1), reinterpret_cast(raw_trace_multi_1) + 300); ASSERT_GE(raw_trace.frames.size(), 1); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_1)); EXPECT_LE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_1) + 90); } std::vector> parents; CPPTRACE_FORCE_NO_INLINE void record_parent(uintptr_t low_bound, uintptr_t high_bound) { parents.insert(parents.begin(), {low_bound, high_bound}); } #ifndef _MSC_VER CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_3() { a: auto raw_trace = cpptrace::generate_raw_trace(); b: volatile auto x = 0; if(x) { goto* &&a; } if(x) { goto* &&b; } ASSERT_GE(raw_trace.frames.size(), parents.size() + 1); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(&&a)); // this frame EXPECT_LE(raw_trace.frames[0], reinterpret_cast(&&b)); for(size_t i = 0; i < parents.size(); i++) { // parent frames EXPECT_GE(raw_trace.frames[i + 1], parents[i].first); EXPECT_LE(raw_trace.frames[i + 1], parents[i].second); } } CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_2() { a: auto raw_trace = cpptrace::generate_raw_trace(); b: volatile auto x = 0; if(x) { goto* &&a; } if(x) { goto* &&b; } ASSERT_GE(raw_trace.frames.size(), parents.size() + 1); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(&&a)); // this frame EXPECT_LE(raw_trace.frames[0], reinterpret_cast(&&b)); for(size_t i = 0; i < parents.size(); i++) { // parent frames EXPECT_GE(raw_trace.frames[i + 1], parents[i].first); EXPECT_LE(raw_trace.frames[i + 1], parents[i].second); } record_parent(reinterpret_cast(&&c), reinterpret_cast(&&d)); c: raw_trace_multi_precise_3(); d: if(x) { goto* &&c; } if(x) { goto* &&d; } } CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_1() { a: auto raw_trace = cpptrace::generate_raw_trace(); b: volatile auto x = 0; if(x) { goto* &&a; } if(x) { goto* &&b; } ASSERT_GE(raw_trace.frames.size(), 1); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(&&a)); EXPECT_LE(raw_trace.frames[0], reinterpret_cast(&&b)); record_parent(reinterpret_cast(&&c), reinterpret_cast(&&d)); c: raw_trace_multi_precise_2(); d: if(x) { goto* &&c; } if(x) { goto* &&d; } } #endif TEST(RawTrace, MultipleCalls) { parents.clear(); raw_trace_multi_1(); #ifndef _MSC_VER raw_trace_multi_precise_1(); #endif } #endif