Skip library frames when unwinding
This commit is contained in:
parent
bdc8410f82
commit
6c950e0817
@ -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(
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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) : "")
|
||||
<< " "
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
10
src/platform/libcpp_common.hpp
Normal file
10
src/platform/libcpp_common.hpp
Normal 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
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
std::vector<void*> capture_frames() {
|
||||
std::vector<void*> capture_frames(size_t) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user