diff --git a/README.md b/README.md index a7ba858..88b45be 100644 --- a/README.md +++ b/README.md @@ -218,9 +218,11 @@ namespace cpptrace { ### Utilities `cpptrace::demangle` provides a helper function for name demangling, since it has to implement that helper internally -anyways. It also provides a function to control whether any exceptions that are throw internally are caught and ignored -before returning to user code. The library makes an attempt to fail silently during trace generation if any errors are -encountered. +anyways. + +The library makes an attempt to fail silently and continue during trace generation if any errors are encountered. +`cpptrace::absorb_trace_exceptions` can be used to configure whether these exceptions are absorbed silently internally +or wether they're rethrown to the caller. ```cpp namespace cpptrace { diff --git a/src/cpptrace.cpp b/src/cpptrace.cpp index 756b9bb..80ca69b 100644 --- a/src/cpptrace.cpp +++ b/src/cpptrace.cpp @@ -42,16 +42,30 @@ namespace cpptrace { CPPTRACE_API object_trace raw_trace::resolve_object_trace() const { - return object_trace(detail::get_frames_object_info(frames)); + try { + return object_trace(detail::get_frames_object_info(frames)); + } catch(...) { + if(!detail::should_absorb_trace_exceptions()) { + throw; + } + return object_trace({}); + } } CPPTRACE_API stacktrace raw_trace::resolve() const { - std::vector trace = detail::resolve_frames(frames); - for(auto& frame : trace) { - frame.symbol = detail::demangle(frame.symbol); + try { + std::vector trace = detail::resolve_frames(frames); + for(auto& frame : trace) { + frame.symbol = detail::demangle(frame.symbol); + } + return stacktrace(std::move(trace)); + } catch(...) { + if(!detail::should_absorb_trace_exceptions()) { + throw; + } + return stacktrace(); } - return stacktrace(std::move(trace)); } CPPTRACE_API @@ -76,7 +90,14 @@ namespace cpptrace { CPPTRACE_API stacktrace object_trace::resolve() const { - return stacktrace(detail::resolve_frames(frames)); + try { + return stacktrace(detail::resolve_frames(frames)); + } catch(...) { + if(!detail::should_absorb_trace_exceptions()) { + throw; + } + return stacktrace(); + } } CPPTRACE_API @@ -222,22 +243,50 @@ namespace cpptrace { CPPTRACE_FORCE_NO_INLINE CPPTRACE_API raw_trace generate_raw_trace(std::uint_least32_t skip) { - return raw_trace(detail::capture_frames(skip + 1, UINT_LEAST32_MAX)); + try { + return raw_trace(detail::capture_frames(skip + 1, UINT_LEAST32_MAX)); + } catch(...) { + if(!detail::should_absorb_trace_exceptions()) { + throw; + } + return raw_trace({}); + } } CPPTRACE_FORCE_NO_INLINE CPPTRACE_API raw_trace generate_raw_trace(std::uint_least32_t skip, std::uint_least32_t max_depth) { - return raw_trace(detail::capture_frames(skip + 1, max_depth)); + try { + return raw_trace(detail::capture_frames(skip + 1, max_depth)); + } catch(...) { + if(!detail::should_absorb_trace_exceptions()) { + throw; + } + return raw_trace({}); + } } CPPTRACE_FORCE_NO_INLINE CPPTRACE_API object_trace generate_object_trace(std::uint_least32_t skip) { - return object_trace(detail::get_frames_object_info(detail::capture_frames(skip + 1, UINT_LEAST32_MAX))); + try { + return object_trace(detail::get_frames_object_info(detail::capture_frames(skip + 1, UINT_LEAST32_MAX))); + } catch(...) { + if(!detail::should_absorb_trace_exceptions()) { + throw; + } + return object_trace({}); + } } CPPTRACE_FORCE_NO_INLINE CPPTRACE_API object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth) { - return object_trace(detail::get_frames_object_info(detail::capture_frames(skip + 1, max_depth))); + try { + return object_trace(detail::get_frames_object_info(detail::capture_frames(skip + 1, max_depth))); + } catch(...) { + if(!detail::should_absorb_trace_exceptions()) { + throw; + } + return object_trace({}); + } } CPPTRACE_FORCE_NO_INLINE CPPTRACE_API @@ -247,12 +296,19 @@ namespace cpptrace { CPPTRACE_FORCE_NO_INLINE CPPTRACE_API stacktrace generate_trace(std::uint32_t skip, std::uint_least32_t max_depth) { - std::vector frames = detail::capture_frames(skip + 1, max_depth); - std::vector trace = detail::resolve_frames(frames); - for(auto& frame : trace) { - frame.symbol = detail::demangle(frame.symbol); + try { + std::vector frames = detail::capture_frames(skip + 1, max_depth); + std::vector trace = detail::resolve_frames(frames); + for(auto& frame : trace) { + frame.symbol = detail::demangle(frame.symbol); + } + return stacktrace(std::move(trace)); + } catch(...) { + if(!detail::should_absorb_trace_exceptions()) { + throw; + } + return stacktrace(); } - return stacktrace(std::move(trace)); } CPPTRACE_API diff --git a/src/platform/common.hpp b/src/platform/common.hpp index 47aef79..659ae25 100644 --- a/src/platform/common.hpp +++ b/src/platform/common.hpp @@ -60,6 +60,8 @@ namespace detail { snprintf(&str[0], length + 1, args...); return str; } + + static const stacktrace_frame null_frame {0, 0, UINT_LEAST32_MAX, "", ""}; } } diff --git a/src/platform/utils.hpp b/src/platform/utils.hpp index 8fd509a..69145bb 100644 --- a/src/platform/utils.hpp +++ b/src/platform/utils.hpp @@ -126,7 +126,7 @@ namespace detail { return byte_swapper{}(value); } - inline void enable_virtual_terminal_processing_if_needed() { + inline void enable_virtual_terminal_processing_if_needed() noexcept { // enable colors / ansi processing if necessary #if IS_WINDOWS // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing @@ -142,7 +142,7 @@ namespace detail { #endif } - inline constexpr unsigned n_digits(unsigned value) { + inline constexpr unsigned n_digits(unsigned value) noexcept { return value < 10 ? 1 : 1 + n_digits(value / 10); } static_assert(n_digits(1) == 1, "n_digits utility producing the wrong result"); diff --git a/src/symbols/symbols_with_addr2line.cpp b/src/symbols/symbols_with_addr2line.cpp index a0a2460..145385a 100644 --- a/src/symbols/symbols_with_addr2line.cpp +++ b/src/symbols/symbols_with_addr2line.cpp @@ -267,7 +267,7 @@ namespace addr2line { std::vector resolve_frames(const std::vector& frames) { // TODO: Refactor better - std::vector trace(frames.size(), stacktrace_frame { 0, 0, UINT_LEAST32_MAX, "", "" }); + std::vector trace(frames.size(), null_frame); for(size_t i = 0; i < frames.size(); i++) { trace[i].address = frames[i].raw_address; // Set what is known for now, and resolutions from addr2line should overwrite @@ -277,28 +277,34 @@ namespace addr2line { if(has_addr2line()) { const auto entries = collate_frames(frames, trace); for(const auto& entry : entries) { - const auto& object_name = entry.first; - const auto& entries_vec = entry.second; - // You may ask why it'd ever happen that there could be an empty entries_vec array, if there're - // no addresses why would get_addr2line_targets do anything? The reason is because if things in - // get_addr2line_targets fail it will silently skip. This is partly an optimization but also an - // assertion below will fail if addr2line is given an empty input. - if(entries_vec.empty()) { - continue; - } - std::string address_input; - for(const auto& pair : entries_vec) { - address_input += to_hex(pair.first.get().obj_address); - #if !IS_WINDOWS - address_input += '\n'; - #else - address_input += ' '; - #endif - } - auto output = split(trim(resolve_addresses(address_input, object_name)), "\n"); - VERIFY(output.size() == entries_vec.size()); - for(size_t i = 0; i < output.size(); i++) { - update_trace(output[i], i, entries_vec); + try { + const auto& object_name = entry.first; + const auto& entries_vec = entry.second; + // You may ask why it'd ever happen that there could be an empty entries_vec array, if there're + // no addresses why would get_addr2line_targets do anything? The reason is because if things in + // get_addr2line_targets fail it will silently skip. This is partly an optimization but also an + // assertion below will fail if addr2line is given an empty input. + if(entries_vec.empty()) { + continue; + } + std::string address_input; + for(const auto& pair : entries_vec) { + address_input += to_hex(pair.first.get().obj_address); + #if !IS_WINDOWS + address_input += '\n'; + #else + address_input += ' '; + #endif + } + auto output = split(trim(resolve_addresses(address_input, object_name)), "\n"); + VERIFY(output.size() == entries_vec.size()); + for(size_t i = 0; i < output.size(); i++) { + update_trace(output[i], i, entries_vec); + } + } catch(...) { + if(!should_absorb_trace_exceptions()) { + throw; + } } } } diff --git a/src/symbols/symbols_with_dbghelp.cpp b/src/symbols/symbols_with_dbghelp.cpp index 3041e48..433339d 100644 --- a/src/symbols/symbols_with_dbghelp.cpp +++ b/src/symbols/symbols_with_dbghelp.cpp @@ -382,22 +382,10 @@ namespace dbghelp { signature }; } else { - return { - addr, - 0, - UINT_LEAST32_MAX, - "", - symbol->Name - }; + return { addr, 0, UINT_LEAST32_MAX, "", symbol->Name }; } } else { - return { - addr, - 0, - UINT_LEAST32_MAX, - "", - "" - }; + return { addr, 0, UINT_LEAST32_MAX, "", "" }; } } @@ -414,7 +402,14 @@ namespace dbghelp { throw std::logic_error("SymInitialize failed"); } for(const auto frame : frames) { - trace.push_back(resolve_frame(proc, frame)); + try { + trace.push_back(resolve_frame(proc, frame)); + } catch(...) { + if(!detail::should_absorb_trace_exceptions()) { + throw; + } + trace.push_back(null_frame); + } } if(!SymCleanup(proc)) { //throw std::logic_error("SymCleanup failed"); diff --git a/src/symbols/symbols_with_libbacktrace.cpp b/src/symbols/symbols_with_libbacktrace.cpp index 412e615..c57e4d7 100644 --- a/src/symbols/symbols_with_libbacktrace.cpp +++ b/src/symbols/symbols_with_libbacktrace.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #ifdef CPPTRACE_BACKTRACE_PATH @@ -21,9 +22,6 @@ namespace detail { namespace libbacktrace { int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) { stacktrace_frame& frame = *static_cast(data); - if(line == 0) { - ///fprintf(stderr, "Getting bad data for some reason\n"); // TODO: Eliminate - } frame.address = address; frame.line = line; frame.filename = file ? file : ""; @@ -40,7 +38,7 @@ namespace libbacktrace { } void error_callback(void*, const char* msg, int errnum) { - fprintf(stderr, "Libbacktrace error: %s, code %d\n", msg, errnum); + throw std::runtime_error(stringf("Libbacktrace error: %s, code %d\n", msg, errnum)); } backtrace_state* get_backtrace_state() { @@ -58,26 +56,33 @@ namespace libbacktrace { // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions stacktrace_frame resolve_frame(const uintptr_t addr) { - stacktrace_frame frame; - frame.column = UINT_LEAST32_MAX; - backtrace_pcinfo( - get_backtrace_state(), - addr, - full_callback, - error_callback, - &frame - ); - if(frame.symbol.empty()) { - // fallback, try to at least recover the symbol name with backtrace_syminfo - backtrace_syminfo( + try { + stacktrace_frame frame; + frame.column = UINT_LEAST32_MAX; + backtrace_pcinfo( get_backtrace_state(), addr, - syminfo_callback, + full_callback, error_callback, &frame ); + if(frame.symbol.empty()) { + // fallback, try to at least recover the symbol name with backtrace_syminfo + backtrace_syminfo( + get_backtrace_state(), + addr, + syminfo_callback, + error_callback, + &frame + ); + } + return frame; + } catch(...) { + if(!should_absorb_trace_exceptions()) { + throw; + } + return null_frame; } - return frame; } std::vector resolve_frames(const std::vector& frames) { diff --git a/src/symbols/symbols_with_libdwarf.cpp b/src/symbols/symbols_with_libdwarf.cpp index 073915c..3656b51 100644 --- a/src/symbols/symbols_with_libdwarf.cpp +++ b/src/symbols/symbols_with_libdwarf.cpp @@ -1089,7 +1089,7 @@ namespace libdwarf { CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING stacktrace_frame resolve_frame(const object_frame& frame_info) { - stacktrace_frame frame { 0, 0, UINT_LEAST32_MAX, "", "" }; + stacktrace_frame frame = null_frame; frame.filename = frame_info.obj_path; frame.symbol = frame_info.symbol; frame.address = frame_info.raw_address; @@ -1112,19 +1112,31 @@ namespace libdwarf { CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING std::vector resolve_frames(const std::vector& frames) { - std::vector trace(frames.size(), stacktrace_frame { 0, 0, UINT_LEAST32_MAX, "", "" }); + std::vector trace(frames.size(), null_frame); static std::mutex mutex; // Locking around all libdwarf interaction per https://github.com/davea42/libdwarf-code/discussions/184 const std::lock_guard lock(mutex); for(const auto& obj_entry : collate_frames(frames, trace)) { - const auto& obj_name = obj_entry.first; - dwarf_resolver resolver(obj_name); - // If there's no debug information it'll mark itself as not ok - if(resolver.ok) { - for(const auto& entry : obj_entry.second) { - const auto& dlframe = entry.first.get(); - auto& frame = entry.second.get(); - frame = resolver.resolve_frame(dlframe); + try { + const auto& obj_name = obj_entry.first; + dwarf_resolver resolver(obj_name); + // If there's no debug information it'll mark itself as not ok + if(resolver.ok) { + for(const auto& entry : obj_entry.second) { + try { + const auto& dlframe = entry.first.get(); + auto& frame = entry.second.get(); + frame = resolver.resolve_frame(dlframe); + } catch(...) { + if(!should_absorb_trace_exceptions()) { + throw; + } + } + } + } + } catch(...) { + if(!should_absorb_trace_exceptions()) { + throw; } } } diff --git a/src/symbols/symbols_with_nothing.cpp b/src/symbols/symbols_with_nothing.cpp index e89234e..a6bfc6e 100644 --- a/src/symbols/symbols_with_nothing.cpp +++ b/src/symbols/symbols_with_nothing.cpp @@ -9,11 +9,11 @@ namespace cpptrace { namespace detail { namespace nothing { std::vector resolve_frames(const std::vector& frames) { - return std::vector(frames.size(), stacktrace_frame { 0, 0, UINT_LEAST32_MAX, "", "" }); + return std::vector(frames.size(), null_frame); } std::vector resolve_frames(const std::vector& frames) { - return std::vector(frames.size(), stacktrace_frame { 0, 0, UINT_LEAST32_MAX, "", "" }); + return std::vector(frames.size(), null_frame); } } } diff --git a/test/demo.cpp b/test/demo.cpp index 8c52f9d..ef3fa05 100644 --- a/test/demo.cpp +++ b/test/demo.cpp @@ -6,6 +6,7 @@ #include void trace() { + cpptrace::absorb_trace_exceptions(false); cpptrace::generate_trace().print(); } diff --git a/test/expected/linux.txt b/test/expected/linux.txt index 918673c..f6fbac6 100644 --- a/test/expected/linux.txt +++ b/test/expected/linux.txt @@ -32,7 +32,7 @@ test/test.cpp||106||void foo(int, int, i test/test.cpp||106||void foo(int, int, int, int, int, int, int, int, int, int) test/test.cpp||112||function_two(int, float) test/test.cpp||118||function_one(int) -test/test.cpp||124||main +test/test.cpp||125||main ./csu/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main ./csu/../csu/libc-start.c||392||__libc_start_main_impl ./test||0|| \ No newline at end of file diff --git a/test/expected/macos.clang.txt b/test/expected/macos.clang.txt index ab4ed11..be1e049 100644 --- a/test/expected/macos.clang.txt +++ b/test/expected/macos.clang.txt @@ -32,7 +32,7 @@ test/test.cpp||106||void foo(int, int, i test/test.cpp||106||void foo(int, int, int, int, int, int, int, int, int, int) test/test.cpp||112||function_two(int, float) test/test.cpp||118||function_one(int) -test/test.cpp||124||main +test/test.cpp||125||main ./csu/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main ./csu/../csu/libc-start.c||392||__libc_start_main_impl ./test||0|| \ No newline at end of file diff --git a/test/expected/macos.gcc.addr2line.txt b/test/expected/macos.gcc.addr2line.txt index 542cb72..bee23ca 100644 --- a/test/expected/macos.gcc.addr2line.txt +++ b/test/expected/macos.gcc.addr2line.txt @@ -32,7 +32,7 @@ test/test.cpp||106||void foo(int, int, i test/test.cpp||106||void foo(int, int, int, int, int, int, int, int, int, int) test/test.cpp||112||function_two(int, float) test/test.cpp||118||function_one(int) -test/test.cpp||124||main +test/test.cpp||125||main ./csu/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main ./csu/../csu/libc-start.c||392||__libc_start_main_impl ./test||0|| \ No newline at end of file diff --git a/test/expected/macos.gcc.txt b/test/expected/macos.gcc.txt index 918673c..f6fbac6 100644 --- a/test/expected/macos.gcc.txt +++ b/test/expected/macos.gcc.txt @@ -32,7 +32,7 @@ test/test.cpp||106||void foo(int, int, i test/test.cpp||106||void foo(int, int, int, int, int, int, int, int, int, int) test/test.cpp||112||function_two(int, float) test/test.cpp||118||function_one(int) -test/test.cpp||124||main +test/test.cpp||125||main ./csu/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main ./csu/../csu/libc-start.c||392||__libc_start_main_impl ./test||0|| \ No newline at end of file diff --git a/test/expected/windows.gcc.txt b/test/expected/windows.gcc.txt index 84e5ec6..458cee0 100644 --- a/test/expected/windows.gcc.txt +++ b/test/expected/windows.gcc.txt @@ -32,7 +32,7 @@ test/test.cpp||106||void foo(int, int, i test/test.cpp||106||void foo(int, int, int, int, int, int, int, int, int, int) test/test.cpp||112||function_two(int, float) test/test.cpp||118||function_one(int) -test/test.cpp||124||main +test/test.cpp||125||main C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c||272||__tmainCRTStartup C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c||193||mainCRTStartup ||0||BaseThreadInitThunk diff --git a/test/expected/windows.txt b/test/expected/windows.txt index 7ca41d9..941d74d 100644 --- a/test/expected/windows.txt +++ b/test/expected/windows.txt @@ -32,7 +32,7 @@ test\test.cpp||106||foo(int, int, int, i test\test.cpp||106||foo(int, int, int, int, int, int, int, int, int, int) test\test.cpp||112||function_two(int, float) test\test.cpp||118||function_one(int) -test\test.cpp||124||main() +test\test.cpp||125||main() D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl||79||invoke_main() D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl||288||__scrt_common_main_seh() D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl||331||__scrt_common_main() diff --git a/test/test.cpp b/test/test.cpp index 5e618f9..a90f7b5 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -121,6 +121,7 @@ void function_one(int) { int main() { x = 0; + cpptrace::absorb_trace_exceptions(false); function_one(0); x = 0; }