Libgcc unwind backend (#11)
This commit is contained in:
parent
b2df292d80
commit
73925368cc
@ -59,6 +59,7 @@ option(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE "" OFF)
|
||||
option(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP "" OFF)
|
||||
option(CPPTRACE_GET_SYMBOLS_WITH_NOTHING "" OFF)
|
||||
|
||||
option(CPPTRACE_UNWIND_WITH_UNWIND "" OFF)
|
||||
option(CPPTRACE_UNWIND_WITH_EXECINFO "" OFF)
|
||||
option(CPPTRACE_UNWIND_WITH_WINAPI "" OFF)
|
||||
option(CPPTRACE_UNWIND_WITH_NOTHING "" OFF)
|
||||
@ -100,6 +101,7 @@ function(check_support var source includes libraries definitions)
|
||||
set(${var} ${${var}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
check_support(HAS_UNWIND has_unwind.cpp "" "" "")
|
||||
check_support(HAS_EXECINFO has_execinfo.cpp "" "" "")
|
||||
check_support(HAS_BACKTRACE has_backtrace.cpp "" "backtrace" "${CPPTRACE_BACKTRACE_PATH_DEFINITION}")
|
||||
check_support(HAS_CXXABI has_cxxabi.cpp "" "" "")
|
||||
@ -121,6 +123,7 @@ if(
|
||||
CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE OR
|
||||
CPPTRACE_GET_SYMBOLS_WITH_DBGHELP OR
|
||||
CPPTRACE_GET_SYMBOLS_WITH_NOTHING OR
|
||||
CPPTRACE_UNWIND_WITH_UNWIND OR
|
||||
CPPTRACE_UNWIND_WITH_EXECINFO OR
|
||||
CPPTRACE_UNWIND_WITH_WINAPI OR
|
||||
CPPTRACE_UNWIND_WITH_NOTHING
|
||||
@ -142,6 +145,7 @@ if(
|
||||
NOT (
|
||||
CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR
|
||||
CPPTRACE_FULL_TRACE_WITH_STACKTRACE OR
|
||||
CPPTRACE_UNWIND_WITH_UNWIND OR
|
||||
CPPTRACE_UNWIND_WITH_EXECINFO OR
|
||||
CPPTRACE_UNWIND_WITH_WINAPI OR
|
||||
CPPTRACE_UNWIND_WITH_NOTHING
|
||||
@ -149,12 +153,15 @@ if(
|
||||
)
|
||||
# Attempt to auto-config
|
||||
if(UNIX)
|
||||
if(HAS_EXECINFO)
|
||||
if(HAS_UNWIND)
|
||||
set(CPPTRACE_UNWIND_WITH_UNWIND On)
|
||||
message(STATUS "Cpptrace auto config: Using libgcc unwind for unwinding")
|
||||
elseif(HAS_EXECINFO)
|
||||
set(CPPTRACE_UNWIND_WITH_EXECINFO On)
|
||||
message(STATUS "Cpptrace auto config: Using execinfo.h for unwinding")
|
||||
else()
|
||||
set(CPPTRACE_UNWIND_WITH_NOTHING On)
|
||||
message(FATAL_ERROR "Cpptrace auto config: <execinfo.h> doesn't seem to be supported, stack tracing will not work. To compile anyway set CPPTRACE_UNWIND_WITH_NOTHING.")
|
||||
message(FATAL_ERROR "Cpptrace auto config: No unwinding back-end seems to be supported, stack tracing will not work. To compile anyway set CPPTRACE_UNWIND_WITH_NOTHING.")
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
set(CPPTRACE_UNWIND_WITH_WINAPI On)
|
||||
@ -266,6 +273,13 @@ if(CPPTRACE_GET_SYMBOLS_WITH_NOTHING)
|
||||
endif()
|
||||
|
||||
# Unwinding
|
||||
if(CPPTRACE_UNWIND_WITH_UNWIND)
|
||||
if(NOT HAS_UNWIND)
|
||||
message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_UNWIND specified but libgcc unwind doesn't seem to be available.")
|
||||
endif()
|
||||
target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_UNWIND)
|
||||
endif()
|
||||
|
||||
if(CPPTRACE_UNWIND_WITH_EXECINFO)
|
||||
if(NOT HAS_EXECINFO)
|
||||
message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_EXECINFO specified but execinfo.h doesn't seem to be available.")
|
||||
|
||||
11
README.md
11
README.md
@ -112,11 +112,12 @@ also manually set which back-end you want used.
|
||||
|
||||
**Unwinding**
|
||||
|
||||
| Library | CMake config | Platforms | Info |
|
||||
| ---------- | ------------------------------- | -------------- | ------------------------------------------------------------------ |
|
||||
| execinfo.h | `CPPTRACE_UNWIND_WITH_EXECINFO` | linux, macos | Frames are captured with `execinfo.h`'s `backtrace`, part of libc. |
|
||||
| winapi | `CPPTRACE_UNWIND_WITH_WINAPI` | windows, mingw | Frames are captured with `CaptureStackBackTrace`. |
|
||||
| N/A | `CPPTRACE_UNWIND_WITH_NOTHING` | all | Unwinding is not done, stack traces will be empty. |
|
||||
| Library | CMake config | Platforms | Info |
|
||||
| ------------- | ------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| libgcc unwind | `CPPTRACE_UNWIND_WITH_UNWIND` | linux, macos | Frames are captured with libgcc's `_Unwind_Backtrace`, which currently produces the most accurate stack traces on gcc/clang. Libgcc is often linked by default, and llvm has something equivalent. |
|
||||
| execinfo.h | `CPPTRACE_UNWIND_WITH_EXECINFO` | linux, macos | Frames are captured with `execinfo.h`'s `backtrace`, part of libc. |
|
||||
| winapi | `CPPTRACE_UNWIND_WITH_WINAPI` | windows, mingw | Frames are captured with `CaptureStackBackTrace`. |
|
||||
| N/A | `CPPTRACE_UNWIND_WITH_NOTHING` | all | Unwinding is not done, stack traces will be empty. |
|
||||
|
||||
These 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 (beyond the `skip` frames). This is configurable with `CPPTRACE_HARD_MAX_FRAMES`.
|
||||
|
||||
@ -122,6 +122,7 @@ def main():
|
||||
"target": ["Debug"],
|
||||
"std": ["11", "20"],
|
||||
"unwind": [
|
||||
"CPPTRACE_UNWIND_WITH_UNWIND",
|
||||
"CPPTRACE_UNWIND_WITH_EXECINFO",
|
||||
"CPPTRACE_UNWIND_WITH_NOTHING",
|
||||
],
|
||||
@ -152,6 +153,7 @@ def main():
|
||||
"target": ["Debug"],
|
||||
"std": ["11", "20"],
|
||||
"unwind": [
|
||||
"CPPTRACE_UNWIND_WITH_UNWIND",
|
||||
"CPPTRACE_UNWIND_WITH_EXECINFO",
|
||||
"CPPTRACE_UNWIND_WITH_NOTHING",
|
||||
],
|
||||
|
||||
@ -203,6 +203,7 @@ def main():
|
||||
"std": ["11", "20"],
|
||||
"unwind": [
|
||||
"CPPTRACE_UNWIND_WITH_EXECINFO",
|
||||
"CPPTRACE_UNWIND_WITH_UNWIND",
|
||||
#"CPPTRACE_UNWIND_WITH_NOTHING",
|
||||
],
|
||||
"symbols": [
|
||||
@ -233,6 +234,7 @@ def main():
|
||||
"std": ["11", "20"],
|
||||
"unwind": [
|
||||
"CPPTRACE_UNWIND_WITH_EXECINFO",
|
||||
"CPPTRACE_UNWIND_WITH_UNWIND",
|
||||
#"CPPTRACE_UNWIND_WITH_NOTHING",
|
||||
],
|
||||
"symbols": [
|
||||
|
||||
14
cmake/has_unwind.cpp
Normal file
14
cmake/has_unwind.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include <unwind.h>
|
||||
|
||||
_Unwind_Reason_Code unwind_callback(_Unwind_Context* context, void* arg) {
|
||||
_Unwind_GetIP(context);
|
||||
int is_before_instruction = 0;
|
||||
uintptr_t ip = _Unwind_GetIPInfo(context, &is_before_instruction);
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
|
||||
int main() {
|
||||
_Unwind_Backtrace(unwind_callback, nullptr);
|
||||
}
|
||||
2
lint.sh
2
lint.sh
@ -4,7 +4,7 @@ status=0
|
||||
while read f
|
||||
do
|
||||
echo checking $f
|
||||
flags="-DCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE -DCPPTRACE_FULL_TRACE_WITH_STACKTRACE -DCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE -DCPPTRACE_GET_SYMBOLS_WITH_LIBDL -DCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE -DCPPTRACE_GET_SYMBOLS_WITH_NOTHING -DCPPTRACE_UNWIND_WITH_EXECINFO -DCPPTRACE_UNWIND_WITH_NOTHING -DCPPTRACE_DEMANGLE_WITH_CXXABI -DCPPTRACE_DEMANGLE_WITH_NOTHING"
|
||||
flags="-DCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE -DCPPTRACE_FULL_TRACE_WITH_STACKTRACE -DCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE -DCPPTRACE_GET_SYMBOLS_WITH_LIBDL -DCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE -DCPPTRACE_GET_SYMBOLS_WITH_NOTHING -DCPPTRACE_UNWIND_WITH_EXECINFO -DCPPTRACE_UNWIND_WITH_UNWIND -DCPPTRACE_UNWIND_WITH_NOTHING -DCPPTRACE_DEMANGLE_WITH_CXXABI -DCPPTRACE_DEMANGLE_WITH_NOTHING"
|
||||
clang-tidy $f -- -std=c++11 -Iinclude $@ $flags
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ]; then
|
||||
|
||||
58
src/unwind/unwind_with_unwind.cpp
Normal file
58
src/unwind/unwind_with_unwind.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
#ifdef CPPTRACE_UNWIND_WITH_UNWIND
|
||||
|
||||
#include "cpptrace_unwind.hpp"
|
||||
#include "../platform/cpptrace_common.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include <unwind.h>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
struct unwind_state {
|
||||
std::size_t skip;
|
||||
std::size_t count;
|
||||
std::vector<void*>& vec;
|
||||
};
|
||||
|
||||
_Unwind_Reason_Code unwind_callback(_Unwind_Context* context, void* arg) {
|
||||
unwind_state& state = *static_cast<unwind_state*>(arg);
|
||||
if(state.skip) {
|
||||
state.skip--;
|
||||
if(_Unwind_GetIP(context) == uintptr_t(0)) {
|
||||
return _URC_END_OF_STACK;
|
||||
} else {
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
}
|
||||
|
||||
assert(state.count < state.vec.size());
|
||||
int is_before_instruction = 0;
|
||||
uintptr_t ip = _Unwind_GetIPInfo(context, &is_before_instruction);
|
||||
if(!is_before_instruction && ip != uintptr_t(0)) {
|
||||
ip--;
|
||||
}
|
||||
if (ip == uintptr_t(0) || state.count == state.vec.size()) {
|
||||
return _URC_END_OF_STACK;
|
||||
} else {
|
||||
state.vec[state.count++] = (void*)ip;
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
}
|
||||
|
||||
CPPTRACE_FORCE_NO_INLINE
|
||||
std::vector<void*> capture_frames(size_t skip) {
|
||||
std::vector<void*> frames(hard_max_frames, nullptr);
|
||||
unwind_state state{skip + 1, 0, frames};
|
||||
_Unwind_Backtrace(unwind_callback, &state);
|
||||
frames.resize(state.count);
|
||||
frames.shrink_to_fit();
|
||||
return frames;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user