Skip library frames when unwinding

This commit is contained in:
Jeremy 2023-07-02 11:43:28 -04:00
parent bdc8410f82
commit 6c950e0817
No known key found for this signature in database
GPG Key ID: BE03111EB7ED6E2E
10 changed files with 74 additions and 24 deletions

View File

@ -12,7 +12,7 @@ project(
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
#set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON)
include(GNUInstallDirs) include(GNUInstallDirs)
include(CheckCXXSourceCompiles) include(CheckCXXSourceCompiles)
@ -63,6 +63,7 @@ option(LIBCPPTRACE_DEMANGLE_WITH_CXXABI "" OFF)
option(LIBCPPTRACE_DEMANGLE_WITH_NOTHING "" 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(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 "") if(NOT "${LIBCPP_BACKTRACE_PATH}" STREQUAL "")
# quotes used over <> because of a macro substitution issue where # 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}) target_compile_definitions(cpptrace PUBLIC LIBCPP_BACKTRACE_PATH=${LIBCPP_BACKTRACE_PATH})
endif() 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( #target_link_libraries(

View File

@ -57,6 +57,9 @@ also manually set which back-end you want used.
| winapi | `LIBCPPTRACE_UNWIND_WITH_WINAPI` | ✔️ | ❌ | Frames are captured with `CaptureStackBackTrace`. | | 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. | | 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** **Symbol resolution**
| Library | CMake config | Windows | Linux | Info | | Library | CMake config | Windows | Linux | Info |
@ -80,7 +83,8 @@ mangled.
**Full tracing** **Full tracing**
Libbacktrace can generate a full stack trace itself, both unwinding and resolving symbols. This can be chosen with 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 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. more back-ends can be added. Ideally this library can "just work" on systems, without additional installation work.

View File

@ -10,10 +10,12 @@
#include "symbols/libcpp_symbols.hpp" #include "symbols/libcpp_symbols.hpp"
#include "unwind/libcpp_unwind.hpp" #include "unwind/libcpp_unwind.hpp"
#include "demangle/libcpp_demangle.hpp" #include "demangle/libcpp_demangle.hpp"
#include "platform/libcpp_common.hpp"
namespace cpptrace { namespace cpptrace {
LIBCPPTRACE_FORCE_NO_INLINE
std::vector<stacktrace_frame> generate_trace() { std::vector<stacktrace_frame> generate_trace() {
std::vector<void*> frames = detail::capture_frames(); std::vector<void*> frames = detail::capture_frames(1);
std::vector<stacktrace_frame> trace; std::vector<stacktrace_frame> trace;
detail::symbolizer symbolizer; detail::symbolizer symbolizer;
for(const auto frame : frames) { for(const auto frame : frames) {
@ -33,8 +35,9 @@ namespace cpptrace {
#include "demangle/libcpp_demangle.hpp" #include "demangle/libcpp_demangle.hpp"
namespace cpptrace { namespace cpptrace {
LIBCPPTRACE_FORCE_NO_INLINE
std::vector<stacktrace_frame> generate_trace() { std::vector<stacktrace_frame> generate_trace() {
auto trace = detail::generate_trace(); auto trace = detail::generate_trace(1);
for(auto& entry : trace) { for(auto& entry : trace) {
entry.symbol = detail::demangle(entry.symbol); entry.symbol = detail::demangle(entry.symbol);
} }
@ -48,12 +51,15 @@ namespace cpptrace {
void print_trace() { void print_trace() {
std::cerr<<"Stack trace (most recent call first):"<<std::endl; std::cerr<<"Stack trace (most recent call first):"<<std::endl;
std::size_t i = 0; std::size_t i = 0;
for(const auto& frame : generate_trace()) { const auto trace = generate_trace();
// +1 to skip one frame
for(auto it = trace.begin() + 1; it != trace.end(); it++) {
const auto& frame = *it;
std::cerr std::cerr
<< i++ << i++
<< " " << " "
<< frame.filename << frame.filename
<< " at " << ":"
<< frame.line << frame.line
<< (frame.col > 0 ? ":" + std::to_string(frame.col) : "") << (frame.col > 0 ? ":" + std::to_string(frame.col) : "")
<< " " << " "

View File

@ -3,6 +3,7 @@
#include <cpptrace/cpptrace.hpp> #include <cpptrace/cpptrace.hpp>
#include "libcpp_full_trace.hpp" #include "libcpp_full_trace.hpp"
#include "../platform/libcpp_program_name.hpp" #include "../platform/libcpp_program_name.hpp"
#include "../platform/libcpp_common.hpp"
#include <vector> #include <vector>
@ -14,14 +15,24 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) { struct trace_data {
reinterpret_cast<std::vector<stacktrace_frame>*>(data)->push_back({ std::vector<stacktrace_frame>& frames;
address, size_t& skip;
line, };
-1,
file ? file : "", int full_callback(void* data_pointer, uintptr_t address, const char* file, int line, const char* symbol) {
symbol ? symbol : "" trace_data& data = *reinterpret_cast<trace_data*>(data_pointer);
}); if(data.skip > 0) {
data.skip--;
} else {
data.frames.push_back({
address,
line,
-1,
file ? file : "",
symbol ? symbol : ""
});
}
return 0; return 0;
} }
@ -40,9 +51,12 @@ namespace cpptrace {
return state; return state;
} }
std::vector<stacktrace_frame> generate_trace() { LIBCPPTRACE_FORCE_NO_INLINE
std::vector<stacktrace_frame> generate_trace(size_t skip) {
std::vector<stacktrace_frame> frames; std::vector<stacktrace_frame> 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; return frames;
} }
} }

View File

@ -2,12 +2,15 @@
#define LIBCPP_FULL_TRACE_HPP #define LIBCPP_FULL_TRACE_HPP
#include <cpptrace/cpptrace.hpp> #include <cpptrace/cpptrace.hpp>
#include "../platform/libcpp_common.hpp"
#include <cstddef>
#include <vector> #include <vector>
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
std::vector<stacktrace_frame> generate_trace(); LIBCPPTRACE_FORCE_NO_INLINE
std::vector<stacktrace_frame> generate_trace(size_t skip);
} }
} }

View File

@ -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

View File

@ -2,6 +2,7 @@
#define LIBCPP_UNWIND_HPP #define LIBCPP_UNWIND_HPP
#include <cpptrace/cpptrace.hpp> #include <cpptrace/cpptrace.hpp>
#include "../platform/libcpp_common.hpp"
#include <cstddef> #include <cstddef>
@ -12,7 +13,8 @@ namespace cpptrace {
#else #else
constexpr size_t hard_max_frames = 100; constexpr size_t hard_max_frames = 100;
#endif #endif
std::vector<void*> capture_frames(); LIBCPPTRACE_FORCE_NO_INLINE
std::vector<void*> capture_frames(size_t skip);
} }
} }

View File

@ -2,17 +2,21 @@
#include <cpptrace/cpptrace.hpp> #include <cpptrace/cpptrace.hpp>
#include "libcpp_unwind.hpp" #include "libcpp_unwind.hpp"
#include "../platform/libcpp_common.hpp"
#include <algorithm>
#include <vector> #include <vector>
#include <execinfo.h> #include <execinfo.h>
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
std::vector<void*> capture_frames() { LIBCPPTRACE_FORCE_NO_INLINE
std::vector<void*> frames(hard_max_frames, nullptr); std::vector<void*> capture_frames(size_t skip) {
int n_frames = backtrace(frames.data(), hard_max_frames); std::vector<void*> frames(hard_max_frames + skip, nullptr);
int n_frames = backtrace(frames.data(), hard_max_frames + skip);
frames.resize(n_frames); frames.resize(n_frames);
frames.erase(frames.begin(), frames.begin() + std::min(skip + 1, frames.size()));
frames.shrink_to_fit(); frames.shrink_to_fit();
return frames; return frames;
} }

View File

@ -7,7 +7,7 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
std::vector<void*> capture_frames() { std::vector<void*> capture_frames(size_t) {
return {}; return {};
} }
} }

View File

@ -2,6 +2,7 @@
#include <cpptrace/cpptrace.hpp> #include <cpptrace/cpptrace.hpp>
#include "libcpp_unwind.hpp" #include "libcpp_unwind.hpp"
#include "../platform/libcpp_common.hpp"
#include <vector> #include <vector>
@ -9,9 +10,10 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
std::vector<void*> capture_frames() { LIBCPPTRACE_FORCE_NO_INLINE
std::vector<void*> capture_frames(size_t skip) {
std::vector<PVOID> addrs(hard_max_frames, nullptr); std::vector<PVOID> addrs(hard_max_frames, nullptr);
int frames = CaptureStackBackTrace(0, hard_max_frames, addrs.data(), NULL); int frames = CaptureStackBackTrace(static_cast<DWORD>(skip + 1), hard_max_frames, addrs.data(), NULL);
addrs.resize(frames); addrs.resize(frames);
addrs.shrink_to_fit(); addrs.shrink_to_fit();
return addrs; return addrs;