Libgcc unwind backend (#11)

This commit is contained in:
Jeremy Rifkin 2023-07-20 01:14:38 -04:00 committed by GitHub
parent b2df292d80
commit 73925368cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 99 additions and 8 deletions

View File

@ -59,6 +59,7 @@ option(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE "" OFF)
option(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP "" OFF) option(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP "" OFF)
option(CPPTRACE_GET_SYMBOLS_WITH_NOTHING "" OFF) option(CPPTRACE_GET_SYMBOLS_WITH_NOTHING "" OFF)
option(CPPTRACE_UNWIND_WITH_UNWIND "" OFF)
option(CPPTRACE_UNWIND_WITH_EXECINFO "" OFF) option(CPPTRACE_UNWIND_WITH_EXECINFO "" OFF)
option(CPPTRACE_UNWIND_WITH_WINAPI "" OFF) option(CPPTRACE_UNWIND_WITH_WINAPI "" OFF)
option(CPPTRACE_UNWIND_WITH_NOTHING "" OFF) option(CPPTRACE_UNWIND_WITH_NOTHING "" OFF)
@ -100,6 +101,7 @@ function(check_support var source includes libraries definitions)
set(${var} ${${var}} PARENT_SCOPE) set(${var} ${${var}} PARENT_SCOPE)
endfunction() endfunction()
check_support(HAS_UNWIND has_unwind.cpp "" "" "")
check_support(HAS_EXECINFO has_execinfo.cpp "" "" "") check_support(HAS_EXECINFO has_execinfo.cpp "" "" "")
check_support(HAS_BACKTRACE has_backtrace.cpp "" "backtrace" "${CPPTRACE_BACKTRACE_PATH_DEFINITION}") check_support(HAS_BACKTRACE has_backtrace.cpp "" "backtrace" "${CPPTRACE_BACKTRACE_PATH_DEFINITION}")
check_support(HAS_CXXABI has_cxxabi.cpp "" "" "") check_support(HAS_CXXABI has_cxxabi.cpp "" "" "")
@ -121,6 +123,7 @@ if(
CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE OR CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE OR
CPPTRACE_GET_SYMBOLS_WITH_DBGHELP OR CPPTRACE_GET_SYMBOLS_WITH_DBGHELP OR
CPPTRACE_GET_SYMBOLS_WITH_NOTHING OR CPPTRACE_GET_SYMBOLS_WITH_NOTHING OR
CPPTRACE_UNWIND_WITH_UNWIND OR
CPPTRACE_UNWIND_WITH_EXECINFO OR CPPTRACE_UNWIND_WITH_EXECINFO OR
CPPTRACE_UNWIND_WITH_WINAPI OR CPPTRACE_UNWIND_WITH_WINAPI OR
CPPTRACE_UNWIND_WITH_NOTHING CPPTRACE_UNWIND_WITH_NOTHING
@ -142,6 +145,7 @@ if(
NOT ( NOT (
CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR
CPPTRACE_FULL_TRACE_WITH_STACKTRACE OR CPPTRACE_FULL_TRACE_WITH_STACKTRACE OR
CPPTRACE_UNWIND_WITH_UNWIND OR
CPPTRACE_UNWIND_WITH_EXECINFO OR CPPTRACE_UNWIND_WITH_EXECINFO OR
CPPTRACE_UNWIND_WITH_WINAPI OR CPPTRACE_UNWIND_WITH_WINAPI OR
CPPTRACE_UNWIND_WITH_NOTHING CPPTRACE_UNWIND_WITH_NOTHING
@ -149,12 +153,15 @@ if(
) )
# Attempt to auto-config # Attempt to auto-config
if(UNIX) 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) set(CPPTRACE_UNWIND_WITH_EXECINFO On)
message(STATUS "Cpptrace auto config: Using execinfo.h for unwinding") message(STATUS "Cpptrace auto config: Using execinfo.h for unwinding")
else() else()
set(CPPTRACE_UNWIND_WITH_NOTHING On) 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() endif()
elseif(WIN32) elseif(WIN32)
set(CPPTRACE_UNWIND_WITH_WINAPI On) set(CPPTRACE_UNWIND_WITH_WINAPI On)
@ -266,6 +273,13 @@ if(CPPTRACE_GET_SYMBOLS_WITH_NOTHING)
endif() endif()
# Unwinding # 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(CPPTRACE_UNWIND_WITH_EXECINFO)
if(NOT HAS_EXECINFO) if(NOT HAS_EXECINFO)
message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_EXECINFO specified but execinfo.h doesn't seem to be available.") message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_EXECINFO specified but execinfo.h doesn't seem to be available.")

View File

@ -112,11 +112,12 @@ also manually set which back-end you want used.
**Unwinding** **Unwinding**
| Library | CMake config | Platforms | Info | | Library | CMake config | Platforms | Info |
| ---------- | ------------------------------- | -------------- | ------------------------------------------------------------------ | | ------------- | ------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| execinfo.h | `CPPTRACE_UNWIND_WITH_EXECINFO` | linux, macos | Frames are captured with `execinfo.h`'s `backtrace`, part of libc. | | 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. |
| winapi | `CPPTRACE_UNWIND_WITH_WINAPI` | windows, mingw | Frames are captured with `CaptureStackBackTrace`. | | execinfo.h | `CPPTRACE_UNWIND_WITH_EXECINFO` | linux, macos | Frames are captured with `execinfo.h`'s `backtrace`, part of libc. |
| N/A | `CPPTRACE_UNWIND_WITH_NOTHING` | all | Unwinding is not done, stack traces will be empty. | | 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 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`. can hold addresses for 100 frames (beyond the `skip` frames). This is configurable with `CPPTRACE_HARD_MAX_FRAMES`.

View File

@ -122,6 +122,7 @@ def main():
"target": ["Debug"], "target": ["Debug"],
"std": ["11", "20"], "std": ["11", "20"],
"unwind": [ "unwind": [
"CPPTRACE_UNWIND_WITH_UNWIND",
"CPPTRACE_UNWIND_WITH_EXECINFO", "CPPTRACE_UNWIND_WITH_EXECINFO",
"CPPTRACE_UNWIND_WITH_NOTHING", "CPPTRACE_UNWIND_WITH_NOTHING",
], ],
@ -152,6 +153,7 @@ def main():
"target": ["Debug"], "target": ["Debug"],
"std": ["11", "20"], "std": ["11", "20"],
"unwind": [ "unwind": [
"CPPTRACE_UNWIND_WITH_UNWIND",
"CPPTRACE_UNWIND_WITH_EXECINFO", "CPPTRACE_UNWIND_WITH_EXECINFO",
"CPPTRACE_UNWIND_WITH_NOTHING", "CPPTRACE_UNWIND_WITH_NOTHING",
], ],

View File

@ -203,6 +203,7 @@ def main():
"std": ["11", "20"], "std": ["11", "20"],
"unwind": [ "unwind": [
"CPPTRACE_UNWIND_WITH_EXECINFO", "CPPTRACE_UNWIND_WITH_EXECINFO",
"CPPTRACE_UNWIND_WITH_UNWIND",
#"CPPTRACE_UNWIND_WITH_NOTHING", #"CPPTRACE_UNWIND_WITH_NOTHING",
], ],
"symbols": [ "symbols": [
@ -233,6 +234,7 @@ def main():
"std": ["11", "20"], "std": ["11", "20"],
"unwind": [ "unwind": [
"CPPTRACE_UNWIND_WITH_EXECINFO", "CPPTRACE_UNWIND_WITH_EXECINFO",
"CPPTRACE_UNWIND_WITH_UNWIND",
#"CPPTRACE_UNWIND_WITH_NOTHING", #"CPPTRACE_UNWIND_WITH_NOTHING",
], ],
"symbols": [ "symbols": [

14
cmake/has_unwind.cpp Normal file
View 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);
}

View File

@ -4,7 +4,7 @@ status=0
while read f while read f
do do
echo checking $f 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 clang-tidy $f -- -std=c++11 -Iinclude $@ $flags
ret=$? ret=$?
if [ $ret -ne 0 ]; then if [ $ret -ne 0 ]; then

View 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