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_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(

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`. |
| 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.

View File

@ -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<stacktrace_frame> generate_trace() {
std::vector<void*> frames = detail::capture_frames();
std::vector<void*> frames = detail::capture_frames(1);
std::vector<stacktrace_frame> 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<stacktrace_frame> 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):"<<std::endl;
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
<< i++
<< " "
<< frame.filename
<< " at "
<< ":"
<< frame.line
<< (frame.col > 0 ? ":" + std::to_string(frame.col) : "")
<< " "

View File

@ -3,6 +3,7 @@
#include <cpptrace/cpptrace.hpp>
#include "libcpp_full_trace.hpp"
#include "../platform/libcpp_program_name.hpp"
#include "../platform/libcpp_common.hpp"
#include <vector>
@ -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<std::vector<stacktrace_frame>*>(data)->push_back({
address,
line,
-1,
file ? file : "",
symbol ? symbol : ""
});
struct trace_data {
std::vector<stacktrace_frame>& 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<trace_data*>(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<stacktrace_frame> generate_trace() {
LIBCPPTRACE_FORCE_NO_INLINE
std::vector<stacktrace_frame> generate_trace(size_t skip) {
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;
}
}

View File

@ -2,12 +2,15 @@
#define LIBCPP_FULL_TRACE_HPP
#include <cpptrace/cpptrace.hpp>
#include "../platform/libcpp_common.hpp"
#include <cstddef>
#include <vector>
namespace cpptrace {
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
#include <cpptrace/cpptrace.hpp>
#include "../platform/libcpp_common.hpp"
#include <cstddef>
@ -12,7 +13,8 @@ namespace cpptrace {
#else
constexpr size_t hard_max_frames = 100;
#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 "libcpp_unwind.hpp"
#include "../platform/libcpp_common.hpp"
#include <algorithm>
#include <vector>
#include <execinfo.h>
namespace cpptrace {
namespace detail {
std::vector<void*> capture_frames() {
std::vector<void*> frames(hard_max_frames, nullptr);
int n_frames = backtrace(frames.data(), hard_max_frames);
LIBCPPTRACE_FORCE_NO_INLINE
std::vector<void*> capture_frames(size_t skip) {
std::vector<void*> 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;
}

View File

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

View File

@ -2,6 +2,7 @@
#include <cpptrace/cpptrace.hpp>
#include "libcpp_unwind.hpp"
#include "../platform/libcpp_common.hpp"
#include <vector>
@ -9,9 +10,10 @@
namespace cpptrace {
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);
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.shrink_to_fit();
return addrs;