From 73ca2b5b49b5c64ea79abd4c64f70db603d53a3c Mon Sep 17 00:00:00 2001 From: Jeremy Rifkin <51220084+jeremy-rifkin@users.noreply.github.com> Date: Mon, 19 Aug 2024 23:53:57 -0500 Subject: [PATCH] Implement a system for preventing redundant tracing during search --- include/cpptrace/from_current.hpp | 42 ++++++++++++++++++++++++++++--- src/from_current.cpp | 19 ++++++++------ 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/include/cpptrace/from_current.hpp b/include/cpptrace/from_current.hpp index 3c31051..59497b5 100644 --- a/include/cpptrace/from_current.hpp +++ b/include/cpptrace/from_current.hpp @@ -8,15 +8,46 @@ namespace cpptrace { CPPTRACE_EXPORT const stacktrace& from_current_exception(); namespace detail { + // Trace switch is to prevent multiple tracing of stacks on call stacks with multiple catches that don't + // immediately match + inline bool& get_trace_switch() { + static thread_local bool trace_switch = true; + return trace_switch; + } + + class CPPTRACE_EXPORT try_canary { + public: + ~try_canary() { + // Fires when we exit a try block, either via normal means or during unwinding. + // Either way: Flip the switch. + get_trace_switch() = true; + } + }; + + CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip); + + // this function can be void, however, a char return is used to prevent TCO of the collect_current_trace + CPPTRACE_FORCE_NO_INLINE inline char exception_unwind_interceptor(std::size_t skip) { + if(get_trace_switch()) { + // Done during a search phase. Flip the switch off, no more traces until an unwind happens + get_trace_switch() = false; + collect_current_trace(skip + 1); + } + return 42; + } + #ifdef _MSC_VER - CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE int exception_filter(); + CPPTRACE_FORCE_NO_INLINE inline int exception_filter() { + exception_unwind_interceptor(1); + return 0; // EXCEPTION_CONTINUE_SEARCH + } #else class CPPTRACE_EXPORT unwind_interceptor { public: virtual ~unwind_interceptor(); }; - CPPTRACE_EXPORT void do_prepare_unwind_interceptor(); + CPPTRACE_EXPORT void do_prepare_unwind_interceptor(char(*)(std::size_t)); #ifndef CPPTRACE_DONT_PREPARE_UNWIND_INTERCEPTOR_ON __attribute__((constructor)) inline void prepare_unwind_interceptor() { @@ -27,7 +58,7 @@ namespace cpptrace { // against it here too as a fast path, not that this should matter for performance static bool did_prepare = false; if(!did_prepare) { - do_prepare_unwind_interceptor(); + do_prepare_unwind_interceptor(exception_unwind_interceptor); did_prepare = true; } } @@ -41,6 +72,7 @@ namespace cpptrace { // exception handling (try/catch) in the same function." #define CPPTRACE_TRY \ try { \ + ::cpptrace::detail::try_canary cpptrace_try_canary; \ [&]() { \ __try { \ [&]() { @@ -52,6 +84,10 @@ namespace cpptrace { #else #define CPPTRACE_TRY \ try { \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wshadow\"") \ + ::cpptrace::detail::try_canary cpptrace_try_canary; \ + _Pragma("GCC diagnostic pop") \ try { #define CPPTRACE_CATCH(param) \ } catch(::cpptrace::detail::unwind_interceptor&) {} \ diff --git a/src/from_current.cpp b/src/from_current.cpp index 06656d2..f86017c 100644 --- a/src/from_current.cpp +++ b/src/from_current.cpp @@ -32,10 +32,19 @@ namespace cpptrace { namespace detail { thread_local lazy_trace_holder current_exception_trace; + CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip) { + current_exception_trace = lazy_trace_holder(cpptrace::generate_raw_trace(skip + 1)); + } + #ifndef _MSC_VER + // set only once by do_prepare_unwind_interceptor + char (*intercept_unwind_handler)(std::size_t) = nullptr; + CPPTRACE_FORCE_NO_INLINE bool intercept_unwind(const std::type_info*, const std::type_info*, void**, unsigned) { - current_exception_trace = lazy_trace_holder(cpptrace::generate_raw_trace(1)); + if(intercept_unwind_handler) { + intercept_unwind_handler(1); + } return false; } @@ -259,9 +268,10 @@ namespace cpptrace { mprotect_page(reinterpret_cast(page_addr), page_size, old_protections); } - void do_prepare_unwind_interceptor() { + void do_prepare_unwind_interceptor(char(*intercept_unwind_handler)(std::size_t)) { static bool did_prepare = false; if(!did_prepare) { + cpptrace::detail::intercept_unwind_handler = intercept_unwind_handler; try { perform_typeinfo_surgery(typeid(cpptrace::detail::unwind_interceptor)); } catch(std::exception& e) { @@ -276,11 +286,6 @@ namespace cpptrace { did_prepare = true; } } - #else - CPPTRACE_FORCE_NO_INLINE int exception_filter() { - current_exception_trace = lazy_trace_holder(cpptrace::generate_raw_trace(1)); - return EXCEPTION_CONTINUE_SEARCH; - } #endif }