From 6c950e081786e2f098c001deb42d8ec6f494e117 Mon Sep 17 00:00:00 2001 From: Jeremy <51220084+jeremy-rifkin@users.noreply.github.com> Date: Sun, 2 Jul 2023 11:43:28 -0400 Subject: [PATCH] Skip library frames when unwinding --- CMakeLists.txt | 7 ++++- README.md | 6 +++- src/cpptrace.cpp | 14 +++++++--- src/full/full_trace_with_libbacktrace.cpp | 34 ++++++++++++++++------- src/full/libcpp_full_trace.hpp | 5 +++- src/platform/libcpp_common.hpp | 10 +++++++ src/unwind/libcpp_unwind.hpp | 4 ++- src/unwind/unwind_with_execinfo.cpp | 10 +++++-- src/unwind/unwind_with_nothing.cpp | 2 +- src/unwind/unwind_with_winapi.cpp | 6 ++-- 10 files changed, 74 insertions(+), 24 deletions(-) create mode 100644 src/platform/libcpp_common.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 658264c..3750424 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ project( set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) -#set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) include(GNUInstallDirs) include(CheckCXXSourceCompiles) @@ -63,6 +63,7 @@ option(LIBCPPTRACE_DEMANGLE_WITH_CXXABI "" OFF) option(LIBCPPTRACE_DEMANGLE_WITH_NOTHING "" OFF) set(LIBCPP_BACKTRACE_PATH "" CACHE STRING "Path to backtrace.h, if the compiler doesn't already know it. Check /usr/lib/gcc/x86_64-linux-gnu/*/include.") +set(LIBCPPTRACE_HARD_MAX_FRAMES "" CACHE STRING "Hard limit on unwinding depth. Default is 100.") if(NOT "${LIBCPP_BACKTRACE_PATH}" STREQUAL "") # quotes used over <> because of a macro substitution issue where @@ -254,6 +255,10 @@ if(NOT "${LIBCPP_BACKTRACE_PATH}" STREQUAL "") target_compile_definitions(cpptrace PUBLIC LIBCPP_BACKTRACE_PATH=${LIBCPP_BACKTRACE_PATH}) endif() +if(NOT "${LIBCPPTRACE_HARD_MAX_FRAMES}" STREQUAL "") + target_compile_definitions(cpptrace PUBLIC LIBCPPTRACE_HARD_MAX_FRAMES=${LIBCPPTRACE_HARD_MAX_FRAMES}) +endif() + # ====================================================================================================================== #target_link_libraries( diff --git a/README.md b/README.md index eff23d3..564b5ea 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,9 @@ also manually set which back-end you want used. | winapi | `LIBCPPTRACE_UNWIND_WITH_WINAPI` | ✔️ | ❌ | Frames are captured with `CaptureStackBackTrace`. | | N/A | `LIBCPPTRACE_UNWIND_WITH_NOTHING` | ✔️ | ✔️ | Unwinding is not done, stack traces will be empty. | +Some back-ends require a fixed buffer has to be created to read addresses into while unwinding. By default the buffer +can hold addresses for 100 frames. This is configurable with `LIBCPPTRACE_HARD_MAX_FRAMES`. + **Symbol resolution** | Library | CMake config | Windows | Linux | Info | @@ -80,7 +83,8 @@ mangled. **Full tracing** Libbacktrace can generate a full stack trace itself, both unwinding and resolving symbols. This can be chosen with -`LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE`. This is also the first configuration the auto config attempts to use. +`LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE`. This is also the first configuration the auto config attempts to use. Full +tracing with libbacktrace ignores `LIBCPPTRACE_HARD_MAX_FRAMES`. There are plenty more libraries that can be used for unwinding, parsing debug information, and demangling. In the future more back-ends can be added. Ideally this library can "just work" on systems, without additional installation work. diff --git a/src/cpptrace.cpp b/src/cpptrace.cpp index 52cab88..f58f20f 100644 --- a/src/cpptrace.cpp +++ b/src/cpptrace.cpp @@ -10,10 +10,12 @@ #include "symbols/libcpp_symbols.hpp" #include "unwind/libcpp_unwind.hpp" #include "demangle/libcpp_demangle.hpp" +#include "platform/libcpp_common.hpp" namespace cpptrace { + LIBCPPTRACE_FORCE_NO_INLINE std::vector generate_trace() { - std::vector frames = detail::capture_frames(); + std::vector frames = detail::capture_frames(1); std::vector trace; detail::symbolizer symbolizer; for(const auto frame : frames) { @@ -33,8 +35,9 @@ namespace cpptrace { #include "demangle/libcpp_demangle.hpp" namespace cpptrace { + LIBCPPTRACE_FORCE_NO_INLINE std::vector generate_trace() { - auto trace = detail::generate_trace(); + auto trace = detail::generate_trace(1); for(auto& entry : trace) { entry.symbol = detail::demangle(entry.symbol); } @@ -48,12 +51,15 @@ namespace cpptrace { void print_trace() { std::cerr<<"Stack trace (most recent call first):"< 0 ? ":" + std::to_string(frame.col) : "") << " " diff --git a/src/full/full_trace_with_libbacktrace.cpp b/src/full/full_trace_with_libbacktrace.cpp index 9b463ee..ccf73fa 100644 --- a/src/full/full_trace_with_libbacktrace.cpp +++ b/src/full/full_trace_with_libbacktrace.cpp @@ -3,6 +3,7 @@ #include #include "libcpp_full_trace.hpp" #include "../platform/libcpp_program_name.hpp" +#include "../platform/libcpp_common.hpp" #include @@ -14,14 +15,24 @@ namespace cpptrace { namespace detail { - int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) { - reinterpret_cast*>(data)->push_back({ - address, - line, - -1, - file ? file : "", - symbol ? symbol : "" - }); + struct trace_data { + std::vector& frames; + size_t& skip; + }; + + int full_callback(void* data_pointer, uintptr_t address, const char* file, int line, const char* symbol) { + trace_data& data = *reinterpret_cast(data_pointer); + if(data.skip > 0) { + data.skip--; + } else { + data.frames.push_back({ + address, + line, + -1, + file ? file : "", + symbol ? symbol : "" + }); + } return 0; } @@ -40,9 +51,12 @@ namespace cpptrace { return state; } - std::vector generate_trace() { + LIBCPPTRACE_FORCE_NO_INLINE + std::vector generate_trace(size_t skip) { std::vector frames; - backtrace_full(get_backtrace_state(), 0, full_callback, error_callback, &frames); + skip++; // add one for this call + trace_data data { frames, skip }; + backtrace_full(get_backtrace_state(), 0, full_callback, error_callback, &data); return frames; } } diff --git a/src/full/libcpp_full_trace.hpp b/src/full/libcpp_full_trace.hpp index 3b17f38..2c4ac5b 100644 --- a/src/full/libcpp_full_trace.hpp +++ b/src/full/libcpp_full_trace.hpp @@ -2,12 +2,15 @@ #define LIBCPP_FULL_TRACE_HPP #include +#include "../platform/libcpp_common.hpp" +#include #include namespace cpptrace { namespace detail { - std::vector generate_trace(); + LIBCPPTRACE_FORCE_NO_INLINE + std::vector generate_trace(size_t skip); } } diff --git a/src/platform/libcpp_common.hpp b/src/platform/libcpp_common.hpp new file mode 100644 index 0000000..2e367fe --- /dev/null +++ b/src/platform/libcpp_common.hpp @@ -0,0 +1,10 @@ +#ifndef LIBCPP_COMMON_HPP +#define LIBCPP_COMMON_HPP + +#ifdef _MSC_VER +#define LIBCPPTRACE_FORCE_NO_INLINE __declspec(noinline) +#else +#define LIBCPPTRACE_FORCE_NO_INLINE __attribute__((noinline)) +#endif + +#endif diff --git a/src/unwind/libcpp_unwind.hpp b/src/unwind/libcpp_unwind.hpp index d1b41a9..299cf61 100644 --- a/src/unwind/libcpp_unwind.hpp +++ b/src/unwind/libcpp_unwind.hpp @@ -2,6 +2,7 @@ #define LIBCPP_UNWIND_HPP #include +#include "../platform/libcpp_common.hpp" #include @@ -12,7 +13,8 @@ namespace cpptrace { #else constexpr size_t hard_max_frames = 100; #endif - std::vector capture_frames(); + LIBCPPTRACE_FORCE_NO_INLINE + std::vector capture_frames(size_t skip); } } diff --git a/src/unwind/unwind_with_execinfo.cpp b/src/unwind/unwind_with_execinfo.cpp index 7aa0f32..88f2b15 100644 --- a/src/unwind/unwind_with_execinfo.cpp +++ b/src/unwind/unwind_with_execinfo.cpp @@ -2,17 +2,21 @@ #include #include "libcpp_unwind.hpp" +#include "../platform/libcpp_common.hpp" +#include #include #include namespace cpptrace { namespace detail { - std::vector capture_frames() { - std::vector frames(hard_max_frames, nullptr); - int n_frames = backtrace(frames.data(), hard_max_frames); + LIBCPPTRACE_FORCE_NO_INLINE + std::vector capture_frames(size_t skip) { + std::vector frames(hard_max_frames + skip, nullptr); + int n_frames = backtrace(frames.data(), hard_max_frames + skip); frames.resize(n_frames); + frames.erase(frames.begin(), frames.begin() + std::min(skip + 1, frames.size())); frames.shrink_to_fit(); return frames; } diff --git a/src/unwind/unwind_with_nothing.cpp b/src/unwind/unwind_with_nothing.cpp index b9f905b..f2181cd 100644 --- a/src/unwind/unwind_with_nothing.cpp +++ b/src/unwind/unwind_with_nothing.cpp @@ -7,7 +7,7 @@ namespace cpptrace { namespace detail { - std::vector capture_frames() { + std::vector capture_frames(size_t) { return {}; } } diff --git a/src/unwind/unwind_with_winapi.cpp b/src/unwind/unwind_with_winapi.cpp index aa2fefa..ae397fc 100644 --- a/src/unwind/unwind_with_winapi.cpp +++ b/src/unwind/unwind_with_winapi.cpp @@ -2,6 +2,7 @@ #include #include "libcpp_unwind.hpp" +#include "../platform/libcpp_common.hpp" #include @@ -9,9 +10,10 @@ namespace cpptrace { namespace detail { - std::vector capture_frames() { + LIBCPPTRACE_FORCE_NO_INLINE + std::vector capture_frames(size_t skip) { std::vector addrs(hard_max_frames, nullptr); - int frames = CaptureStackBackTrace(0, hard_max_frames, addrs.data(), NULL); + int frames = CaptureStackBackTrace(static_cast(skip + 1), hard_max_frames, addrs.data(), NULL); addrs.resize(frames); addrs.shrink_to_fit(); return addrs;