Add libunwind back-end (#62)

This commit is contained in:
Jeremy Rifkin 2023-11-12 17:15:59 -05:00 committed by GitHub
parent 259d596f76
commit 5541ec5519
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 86 additions and 9 deletions

View File

@ -11,7 +11,7 @@ jobs:
- uses: actions/checkout@v2
- name: dependencies
run: |
sudo apt install gcc-10 g++-10 libgcc-10-dev
sudo apt install gcc-10 g++-10 libgcc-10-dev libunwind8-dev
pip3 install colorama
- name: libdwarf
run: |

View File

@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v2
- name: dependencies
run: |
sudo apt install gcc-10 g++-10 libgcc-10-dev
sudo apt install gcc-10 g++-10 libgcc-10-dev libunwind8-dev
pip3 install colorama
- name: libdwarf
run: |

View File

@ -83,6 +83,7 @@ endif()
if(
NOT (
CPPTRACE_UNWIND_WITH_UNWIND OR
CPPTRACE_UNWIND_WITH_LIBUNWIND OR
CPPTRACE_UNWIND_WITH_EXECINFO OR
CPPTRACE_UNWIND_WITH_WINAPI OR
CPPTRACE_UNWIND_WITH_DBGHELP OR
@ -195,6 +196,7 @@ target_sources(
src/symbols/symbols_with_nothing.cpp
src/symbols/symbols_core.cpp
src/unwind/unwind_with_execinfo.cpp
src/unwind/unwind_with_libunwind.cpp
src/unwind/unwind_with_nothing.cpp
src/unwind/unwind_with_unwind.cpp
src/unwind/unwind_with_winapi.cpp
@ -357,6 +359,34 @@ if(CPPTRACE_UNWIND_WITH_UNWIND)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_UNWIND)
endif()
if(CPPTRACE_UNWIND_WITH_LIBUNWIND)
find_package(PkgConfig)
if(PkgConfig_FOUND)
pkg_check_modules(LIBUNWIND QUIET libunwind)
if(libunwind_FOUND)
target_compile_options(${target_name} PRIVATE ${LIBUNWIND_CFLAGS_OTHER})
target_include_directories(${target_name} PRIVATE ${LIBUNWIND_INCLUDE_DIRS})
target_link_libraries(${target_name} PRIVATE ${LIBUNWIND_LDFLAGS})
endif()
endif()
if(NOT libunwind_FOUND)
# set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS ON)
# set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS ON)
find_path(LIBUNWIND_INCLUDE_DIR NAMES "libunwind.h")
find_library(LIBUNWIND NAMES unwind libunwind libunwind8 libunwind.so.8 REQUIRED PATHS "/usr/lib/x86_64-linux-gnu/")
if(LIBUNWIND)
target_compile_options(${target_name} PRIVATE ${LIBUNWIND_CFLAGS_OTHER})
target_include_directories(${target_name} PRIVATE ${LIBUNWIND_INCLUDE_DIRS})
target_link_libraries(${target_name} PRIVATE ${LIBUNWIND_LDFLAGS})
set(libunwind_FOUND TRUE)
endif()
endif()
if(NOT libunwind_FOUND)
message(FATAL_ERROR "Unable to locate libunwind")
endif()
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_LIBUNWIND UNW_LOCAL_ONLY)
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.")

View File

@ -578,13 +578,14 @@ back-end such as addr2line, for example, you can configure the library to do so.
**Unwinding**
| Library | CMake config | Platforms | Info |
| ------------- | ------------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| libgcc unwind | `CPPTRACE_UNWIND_WITH_UNWIND` | linux, macos, mingw | Frames are captured with libgcc's `_Unwind_Backtrace`, which currently produces the most accurate stack traces on gcc/clang/mingw. 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 on linux/unix systems. |
| winapi | `CPPTRACE_UNWIND_WITH_WINAPI` | windows, mingw | Frames are captured with `CaptureStackBackTrace`. |
| dbghelp | `CPPTRACE_UNWIND_WITH_DBGHELP` | windows, mingw | Frames are captured with `StackWalk64`. |
| 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, mingw | Frames are captured with libgcc's `_Unwind_Backtrace`, which currently produces the most accurate stack traces on gcc/clang/mingw. 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 on linux/unix systems. |
| winapi | `CPPTRACE_UNWIND_WITH_WINAPI` | windows, mingw | Frames are captured with `CaptureStackBackTrace`. |
| dbghelp | `CPPTRACE_UNWIND_WITH_DBGHELP` | windows, mingw | Frames are captured with `StackWalk64`. |
| dbghelp | `CPPTRACE_UNWIND_WITH_LIBUNWIND` | linux, macos, windows, mingw | Frames are captured with [libunwind](https://github.com/libunwind/libunwind). **Note:** This is the only back-end that requires a library to be installed by the user, and a `CMAKE_PREFIX_PATH` may also be needed. |
| N/A | `CPPTRACE_UNWIND_WITH_NOTHING` | all | Unwinding is not done, stack traces will be empty. |
Some back-ends (execinfo and `CaptureStackBackTrace`) 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
@ -634,6 +635,7 @@ Back-ends:
- `CPPTRACE_GET_SYMBOLS_WITH_LIBDL=On/Off`
- `CPPTRACE_GET_SYMBOLS_WITH_NOTHING=On/Off`
- `CPPTRACE_UNWIND_WITH_UNWIND=On/Off`
- `CPPTRACE_UNWIND_WITH_LIBUNWIND=On/Off`
- `CPPTRACE_UNWIND_WITH_EXECINFO=On/Off`
- `CPPTRACE_UNWIND_WITH_WINAPI=On/Off`
- `CPPTRACE_UNWIND_WITH_DBGHELP=On/Off`

View File

@ -151,6 +151,7 @@ def main():
"unwind": [
"CPPTRACE_UNWIND_WITH_UNWIND",
"CPPTRACE_UNWIND_WITH_EXECINFO",
"CPPTRACE_UNWIND_WITH_LIBUNWIND",
"CPPTRACE_UNWIND_WITH_NOTHING",
],
"symbols": [

View File

@ -319,6 +319,7 @@ def main():
"unwind": [
"CPPTRACE_UNWIND_WITH_EXECINFO",
"CPPTRACE_UNWIND_WITH_UNWIND",
"CPPTRACE_UNWIND_WITH_LIBUNWIND",
#"CPPTRACE_UNWIND_WITH_NOTHING",
],
"symbols": [

View File

@ -128,6 +128,7 @@ option(CPPTRACE_GET_SYMBOLS_WITH_NOTHING "" OFF)
# ---- Unwinding Options ----
option(CPPTRACE_UNWIND_WITH_UNWIND "" OFF)
option(CPPTRACE_UNWIND_WITH_LIBUNWIND "" OFF)
option(CPPTRACE_UNWIND_WITH_EXECINFO "" OFF)
option(CPPTRACE_UNWIND_WITH_WINAPI "" OFF)
option(CPPTRACE_UNWIND_WITH_DBGHELP "" OFF)

View File

@ -0,0 +1,42 @@
#ifdef CPPTRACE_UNWIND_WITH_LIBUNWIND
#include "unwind.hpp"
#include "../platform/common.hpp"
#include "../platform/error.hpp"
#include "../platform/utils.hpp"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <vector>
#include <libunwind.h>
namespace cpptrace {
namespace detail {
CPPTRACE_FORCE_NO_INLINE
std::vector<frame_ptr> capture_frames(std::size_t skip, std::size_t max_depth) {
skip++;
std::vector<frame_ptr> frames;
unw_context_t context;
unw_cursor_t cursor;
unw_getcontext(&context);
unw_init_local(&cursor, &context);
do {
unw_word_t pc;
unw_word_t sp;
unw_get_reg(&cursor, UNW_REG_IP, &pc);
unw_get_reg(&cursor, UNW_REG_SP, &sp);
if(skip) {
skip--;
} else {
// pc is the instruction after the `call`, adjust back to the previous instruction
frames.push_back(to_frame_ptr(pc) - 1);
}
} while(unw_step(&cursor) > 0 && frames.size() < max_depth);
return frames;
}
}
}
#endif