Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fac4d08fd0 | ||
|
|
c0354799c7 | ||
|
|
c37b5ed736 | ||
|
|
a32e22aa44 | ||
|
|
6cec10601e | ||
|
|
c9dc51aa61 | ||
|
|
e6d55b5e7d | ||
|
|
1940dc607a | ||
|
|
477aecec01 | ||
|
|
03b292c20b | ||
|
|
5bfcf280a5 | ||
|
|
9b02fc6f74 | ||
|
|
3a4da8ccf0 | ||
|
|
9a2ae3c96f | ||
|
|
6877782d96 | ||
|
|
98ea78445c | ||
|
|
3aa080d536 |
71
.github/workflows/ci.yml
vendored
71
.github/workflows/ci.yml
vendored
@ -25,6 +25,26 @@ jobs:
|
|||||||
- name: build and test
|
- name: build and test
|
||||||
run: |
|
run: |
|
||||||
python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config
|
python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config
|
||||||
|
test-linux-arm:
|
||||||
|
runs-on: ubuntu-22.04-arm
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
compiler: [gcc, clang]
|
||||||
|
shared: [--shared, ""]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt install gcc-10 g++-10 libgcc-10-dev libunwind8-dev
|
||||||
|
pip3 install colorama
|
||||||
|
- name: libdwarf
|
||||||
|
run: |
|
||||||
|
cd ..
|
||||||
|
cpptrace/ci/setup-prerequisites.sh
|
||||||
|
- name: build and test
|
||||||
|
run: |
|
||||||
|
python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config
|
||||||
test-macos:
|
test-macos:
|
||||||
runs-on: macos-14
|
runs-on: macos-14
|
||||||
strategy:
|
strategy:
|
||||||
@ -68,6 +88,31 @@ jobs:
|
|||||||
- name: build and test
|
- name: build and test
|
||||||
run: |
|
run: |
|
||||||
python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config
|
python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config
|
||||||
|
test-windows-old:
|
||||||
|
runs-on: windows-2022
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
compiler: [msvc]
|
||||||
|
shared: [--shared, ""]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Enable Developer Command Prompt
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||||
|
with:
|
||||||
|
toolset: 14.29 # vc 2019
|
||||||
|
- name: dependencies
|
||||||
|
run: |
|
||||||
|
pip3 install colorama
|
||||||
|
- name: libdwarf
|
||||||
|
run: |
|
||||||
|
if("${{matrix.compiler}}" -eq "gcc") {
|
||||||
|
cd ..
|
||||||
|
cpptrace/ci/setup-prerequisites-mingw.ps1
|
||||||
|
}
|
||||||
|
- name: build and test
|
||||||
|
run: |
|
||||||
|
python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config
|
||||||
test-linux-all-configurations:
|
test-linux-all-configurations:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
strategy:
|
strategy:
|
||||||
@ -537,6 +582,32 @@ jobs:
|
|||||||
- name: test opt
|
- name: test opt
|
||||||
run: |
|
run: |
|
||||||
bazel test //... -c opt
|
bazel test //... -c opt
|
||||||
|
unittest-linux-arm:
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
compiler: [g++-10, clang++-18]
|
||||||
|
stdlib: [libstdc++, libc++]
|
||||||
|
dwarf_version: [4, 5]
|
||||||
|
split_dwarf: [OFF, ON]
|
||||||
|
exclude:
|
||||||
|
- compiler: g++-10
|
||||||
|
stdlib: libc++
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt install gcc-10 g++-10 libgcc-10-dev ninja-build libc++-dev
|
||||||
|
cd ..
|
||||||
|
cpptrace/ci/setup-prerequisites-unittest.sh
|
||||||
|
- name: build and test
|
||||||
|
run: |
|
||||||
|
python3 ci/unittest.py \
|
||||||
|
--slice=compiler:${{matrix.compiler}} \
|
||||||
|
--slice=stdlib:${{matrix.stdlib}} \
|
||||||
|
--slice=dwarf_version:${{matrix.dwarf_version}} \
|
||||||
|
--slice=split_dwarf:${{matrix.split_dwarf}}
|
||||||
unittest-macos:
|
unittest-macos:
|
||||||
runs-on: macos-14
|
runs-on: macos-14
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
27
CHANGELOG.md
27
CHANGELOG.md
@ -1,6 +1,8 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
- [Changelog](#changelog)
|
- [Changelog](#changelog)
|
||||||
|
- [v0.8.2](#v082)
|
||||||
|
- [v0.8.1](#v081)
|
||||||
- [v0.8.0](#v080)
|
- [v0.8.0](#v080)
|
||||||
- [v0.7.5](#v075)
|
- [v0.7.5](#v075)
|
||||||
- [v0.7.4](#v074)
|
- [v0.7.4](#v074)
|
||||||
@ -26,6 +28,31 @@
|
|||||||
- [v0.1.1](#v011)
|
- [v0.1.1](#v011)
|
||||||
- [v0.1](#v01)
|
- [v0.1](#v01)
|
||||||
|
|
||||||
|
# v0.8.2
|
||||||
|
|
||||||
|
Fixed:
|
||||||
|
- Fixed printing of internal error messages when an object file can't be loaded, mainly affecting MacOS https://github.com/jeremy-rifkin/cpptrace/issues/217
|
||||||
|
|
||||||
|
Other:
|
||||||
|
- Bumped zstd via FetchContent to 1.5.7
|
||||||
|
|
||||||
|
# v0.8.1
|
||||||
|
|
||||||
|
Fixed:
|
||||||
|
- Fixed compile error on msvc https://github.com/jeremy-rifkin/cpptrace/issues/215
|
||||||
|
|
||||||
|
Added:
|
||||||
|
- Added `cpptrace::can_get_safe_object_frame()`
|
||||||
|
|
||||||
|
Breaking changes:
|
||||||
|
- Renamed ctrace's `can_signal_safe_unwind` to `ctrace_can_signal_safe_unwind`. This was an oversight. Apologies for
|
||||||
|
including a breaking change in a patch release. Github code search suggests this API isn't used in public code, at
|
||||||
|
least.
|
||||||
|
|
||||||
|
Other:
|
||||||
|
- Added CI workflow to test on old msvc
|
||||||
|
- Made some internal improvements on robustness and cleanliness
|
||||||
|
|
||||||
# v0.8.0
|
# v0.8.0
|
||||||
|
|
||||||
Added:
|
Added:
|
||||||
|
|||||||
@ -9,7 +9,7 @@ set(package_name "cpptrace")
|
|||||||
|
|
||||||
project(
|
project(
|
||||||
cpptrace
|
cpptrace
|
||||||
VERSION 0.8.0
|
VERSION 0.8.2
|
||||||
DESCRIPTION "Simple, portable, and self-contained stacktrace library for C++11 and newer "
|
DESCRIPTION "Simple, portable, and self-contained stacktrace library for C++11 and newer "
|
||||||
HOMEPAGE_URL "https://github.com/jeremy-rifkin/cpptrace"
|
HOMEPAGE_URL "https://github.com/jeremy-rifkin/cpptrace"
|
||||||
LANGUAGES C CXX
|
LANGUAGES C CXX
|
||||||
|
|||||||
102
README.md
102
README.md
@ -16,9 +16,6 @@ Cpptrace also has a C API, docs [here](docs/c-api.md).
|
|||||||
|
|
||||||
- [30-Second Overview](#30-second-overview)
|
- [30-Second Overview](#30-second-overview)
|
||||||
- [CMake FetchContent Usage](#cmake-fetchcontent-usage)
|
- [CMake FetchContent Usage](#cmake-fetchcontent-usage)
|
||||||
- [FAQ](#faq)
|
|
||||||
- [What about C++23 `<stacktrace>`?](#what-about-c23-stacktrace)
|
|
||||||
- [What does cpptrace have over other C++ stacktrace libraries?](#what-does-cpptrace-have-over-other-c-stacktrace-libraries)
|
|
||||||
- [Prerequisites](#prerequisites)
|
- [Prerequisites](#prerequisites)
|
||||||
- [Basic Usage](#basic-usage)
|
- [Basic Usage](#basic-usage)
|
||||||
- [`namespace cpptrace`](#namespace-cpptrace)
|
- [`namespace cpptrace`](#namespace-cpptrace)
|
||||||
@ -57,6 +54,10 @@ Cpptrace also has a C API, docs [here](docs/c-api.md).
|
|||||||
- [Summary of Library Configurations](#summary-of-library-configurations)
|
- [Summary of Library Configurations](#summary-of-library-configurations)
|
||||||
- [Testing Methodology](#testing-methodology)
|
- [Testing Methodology](#testing-methodology)
|
||||||
- [Notes About the Library](#notes-about-the-library)
|
- [Notes About the Library](#notes-about-the-library)
|
||||||
|
- [FAQ](#faq)
|
||||||
|
- [What about C++23 `<stacktrace>`?](#what-about-c23-stacktrace)
|
||||||
|
- [What does cpptrace have over other C++ stacktrace libraries?](#what-does-cpptrace-have-over-other-c-stacktrace-libraries)
|
||||||
|
- [I'm getting undefined standard library symbols like `std::__1::basic_string` on MacOS](#im-getting-undefined-standard-library-symbols-like-std__1basic_string-on-macos)
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
|
|
||||||
@ -138,7 +139,7 @@ include(FetchContent)
|
|||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
cpptrace
|
cpptrace
|
||||||
GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git
|
GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git
|
||||||
GIT_TAG v0.8.0 # <HASH or TAG>
|
GIT_TAG v0.8.2 # <HASH or TAG>
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(cpptrace)
|
FetchContent_MakeAvailable(cpptrace)
|
||||||
target_link_libraries(your_target cpptrace::cpptrace)
|
target_link_libraries(your_target cpptrace::cpptrace)
|
||||||
@ -163,33 +164,6 @@ On macOS it is recommended to generate a `.dSYM` file, see [Platform Logistics](
|
|||||||
For other ways to use the library, such as through package managers, a system-wide installation, or on a platform
|
For other ways to use the library, such as through package managers, a system-wide installation, or on a platform
|
||||||
without internet access see [How to Include The Library](#how-to-include-the-library) below.
|
without internet access see [How to Include The Library](#how-to-include-the-library) below.
|
||||||
|
|
||||||
# FAQ
|
|
||||||
|
|
||||||
## What about C++23 `<stacktrace>`?
|
|
||||||
|
|
||||||
Some day C++23's `<stacktrace>` will be ubiquitous. And maybe one day the msvc implementation will be acceptable.
|
|
||||||
The original motivation for cpptrace was to support projects using older C++ standards and as the library has grown its
|
|
||||||
functionality has extended beyond the standard library's implementation.
|
|
||||||
|
|
||||||
Cpptrace provides functionality beyond what the standard library provides and what implementations provide, such as:
|
|
||||||
- Walking inlined function calls
|
|
||||||
- Providing a lightweight interface for "raw traces"
|
|
||||||
- Resolving function parameter types
|
|
||||||
- Providing traced exception objects
|
|
||||||
- Providing an API for signal-safe stacktrace generation
|
|
||||||
- Providing a way to retrieve stack traces from arbitrary exceptions, not just special cpptrace traced exception
|
|
||||||
objects. This is a feature coming to C++26, but cpptrace provides a solution for C++11.
|
|
||||||
|
|
||||||
## What does cpptrace have over other C++ stacktrace libraries?
|
|
||||||
|
|
||||||
Other C++ stacktrace libraries, such as boost stacktrace and backward-cpp, fall short when it comes to portability and
|
|
||||||
ease of use. In testing, I found neither to provide adaquate coverage of various environments. Even when they can be
|
|
||||||
made to work in an environment they require manual configuration from the end-user, possibly requiring manual
|
|
||||||
installation of third-party dependencies. This is a highly undesirable burden to impose on users, especially when it is
|
|
||||||
for a software package which just provides diagnostics as opposed to core functionality. Additionally, cpptrace provides
|
|
||||||
support for resolving inlined calls by default for DWARF symbols (boost does not do this, backward-cpp can do this but
|
|
||||||
only for some back-ends), better support for resolving full function signatures, and nicer API, among other features.
|
|
||||||
|
|
||||||
# Prerequisites
|
# Prerequisites
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
@ -465,6 +439,8 @@ thrown exception object, with minimal or no overhead in the non-throwing path:
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <cpptrace/from_current.hpp>
|
#include <cpptrace/from_current.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
void foo() {
|
void foo() {
|
||||||
throw std::runtime_error("foo failed");
|
throw std::runtime_error("foo failed");
|
||||||
}
|
}
|
||||||
@ -774,6 +750,7 @@ namespace cpptrace {
|
|||||||
};
|
};
|
||||||
void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
|
void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
|
||||||
bool can_signal_safe_unwind();
|
bool can_signal_safe_unwind();
|
||||||
|
bool can_get_safe_object_frame();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -790,9 +767,9 @@ see the comprehensive overview and demo at [signal-safe-tracing.md](docs/signal-
|
|||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> Currently signal-safe stack unwinding is only possible with `libunwind`, which must be
|
> Currently signal-safe stack unwinding is only possible with `libunwind`, which must be
|
||||||
> [manually enabled](#library-back-ends). If signal-safe unwinding isn't supported, `safe_generate_raw_trace` will just
|
> [manually enabled](#library-back-ends). If signal-safe unwinding isn't supported, `safe_generate_raw_trace` will just
|
||||||
> produce an empty trace. `can_signal_safe_unwind` can be used to check for signal-safe unwinding support. If object
|
> produce an empty trace. `can_signal_safe_unwind` can be used to check for signal-safe unwinding support and
|
||||||
> information can't be resolved in a signal-safe way then `get_safe_object_frame` will not populate fields beyond the
|
> `can_get_safe_object_frame` can be used to check `get_safe_object_frame` support. If object information can't be
|
||||||
> `raw_address`.
|
> resolved in a signal-safe way then `get_safe_object_frame` will not populate fields beyond the `raw_address`.
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> `_dl_find_object` is required for signal-safe stack tracing. This is a relatively recent addition to glibc, added in
|
> `_dl_find_object` is required for signal-safe stack tracing. This is a relatively recent addition to glibc, added in
|
||||||
@ -928,7 +905,7 @@ include(FetchContent)
|
|||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
cpptrace
|
cpptrace
|
||||||
GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git
|
GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git
|
||||||
GIT_TAG v0.8.0 # <HASH or TAG>
|
GIT_TAG v0.8.2 # <HASH or TAG>
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(cpptrace)
|
FetchContent_MakeAvailable(cpptrace)
|
||||||
target_link_libraries(your_target cpptrace::cpptrace)
|
target_link_libraries(your_target cpptrace::cpptrace)
|
||||||
@ -944,7 +921,7 @@ information.
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/jeremy-rifkin/cpptrace.git
|
git clone https://github.com/jeremy-rifkin/cpptrace.git
|
||||||
git checkout v0.8.0
|
git checkout v0.8.2
|
||||||
mkdir cpptrace/build
|
mkdir cpptrace/build
|
||||||
cd cpptrace/build
|
cd cpptrace/build
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Release
|
cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||||
@ -987,7 +964,7 @@ you when installing new libraries.
|
|||||||
|
|
||||||
```ps1
|
```ps1
|
||||||
git clone https://github.com/jeremy-rifkin/cpptrace.git
|
git clone https://github.com/jeremy-rifkin/cpptrace.git
|
||||||
git checkout v0.8.0
|
git checkout v0.8.2
|
||||||
mkdir cpptrace/build
|
mkdir cpptrace/build
|
||||||
cd cpptrace/build
|
cd cpptrace/build
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Release
|
cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||||
@ -1005,7 +982,7 @@ To install just for the local user (or any custom prefix):
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/jeremy-rifkin/cpptrace.git
|
git clone https://github.com/jeremy-rifkin/cpptrace.git
|
||||||
git checkout v0.8.0
|
git checkout v0.8.2
|
||||||
mkdir cpptrace/build
|
mkdir cpptrace/build
|
||||||
cd cpptrace/build
|
cd cpptrace/build
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$HOME/wherever
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$HOME/wherever
|
||||||
@ -1088,7 +1065,7 @@ make install
|
|||||||
cd ~/scratch/cpptrace-test
|
cd ~/scratch/cpptrace-test
|
||||||
git clone https://github.com/jeremy-rifkin/cpptrace.git
|
git clone https://github.com/jeremy-rifkin/cpptrace.git
|
||||||
cd cpptrace
|
cd cpptrace
|
||||||
git checkout v0.8.0
|
git checkout v0.8.2
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=On -DCPPTRACE_USE_EXTERNAL_LIBDWARF=On -DCMAKE_PREFIX_PATH=~/scratch/cpptrace-test/resources -DCMAKE_INSTALL_PREFIX=~/scratch/cpptrace-test/resources
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=On -DCPPTRACE_USE_EXTERNAL_LIBDWARF=On -DCMAKE_PREFIX_PATH=~/scratch/cpptrace-test/resources -DCMAKE_INSTALL_PREFIX=~/scratch/cpptrace-test/resources
|
||||||
@ -1108,7 +1085,7 @@ cpptrace and its dependencies.
|
|||||||
Cpptrace is available through conan at https://conan.io/center/recipes/cpptrace.
|
Cpptrace is available through conan at https://conan.io/center/recipes/cpptrace.
|
||||||
```
|
```
|
||||||
[requires]
|
[requires]
|
||||||
cpptrace/0.8.0
|
cpptrace/0.8.2
|
||||||
[generators]
|
[generators]
|
||||||
CMakeDeps
|
CMakeDeps
|
||||||
CMakeToolchain
|
CMakeToolchain
|
||||||
@ -1317,6 +1294,51 @@ A couple things I'd like to improve in the future:
|
|||||||
in dbghelp the library cannot accurately show const and volatile qualifiers or rvalue references (these appear as
|
in dbghelp the library cannot accurately show const and volatile qualifiers or rvalue references (these appear as
|
||||||
pointers).
|
pointers).
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
## What about C++23 `<stacktrace>`?
|
||||||
|
|
||||||
|
Some day C++23's `<stacktrace>` will be ubiquitous. And maybe one day the msvc implementation will be acceptable.
|
||||||
|
The original motivation for cpptrace was to support projects using older C++ standards and as the library has grown its
|
||||||
|
functionality has extended beyond the standard library's implementation.
|
||||||
|
|
||||||
|
Cpptrace provides functionality beyond what the standard library provides and what implementations provide, such as:
|
||||||
|
- Walking inlined function calls
|
||||||
|
- Providing a lightweight interface for "raw traces"
|
||||||
|
- Resolving function parameter types
|
||||||
|
- Providing traced exception objects
|
||||||
|
- Providing an API for signal-safe stacktrace generation
|
||||||
|
- Providing a way to retrieve stack traces from arbitrary exceptions, not just special cpptrace traced exception
|
||||||
|
objects. This is a feature coming to C++26, but cpptrace provides a solution for C++11.
|
||||||
|
|
||||||
|
## What does cpptrace have over other C++ stacktrace libraries?
|
||||||
|
|
||||||
|
Other C++ stacktrace libraries, such as boost stacktrace and backward-cpp, fall short when it comes to portability and
|
||||||
|
ease of use. In testing, I found neither to provide adaquate coverage of various environments. Even when they can be
|
||||||
|
made to work in an environment they require manual configuration from the end-user, possibly requiring manual
|
||||||
|
installation of third-party dependencies. This is a highly undesirable burden to impose on users, especially when it is
|
||||||
|
for a software package which just provides diagnostics as opposed to core functionality. Additionally, cpptrace provides
|
||||||
|
support for resolving inlined calls by default for DWARF symbols (boost does not do this, backward-cpp can do this but
|
||||||
|
only for some back-ends), better support for resolving full function signatures, and nicer API, among other features.
|
||||||
|
|
||||||
|
## I'm getting undefined standard library symbols like `std::__1::basic_string` on MacOS
|
||||||
|
|
||||||
|
If you see a linker error along the lines of the following on MacOS then it's highly likely you are mixing standard
|
||||||
|
library ABIs.
|
||||||
|
|
||||||
|
```
|
||||||
|
Undefined symbols for architecture arm64:
|
||||||
|
"std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::find(char, unsigned long) const", referenced from:
|
||||||
|
cpptrace::detail::demangle(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool) in libcpptrace.a(demangle_with_cxxabi.cpp.o)
|
||||||
|
cpptrace::detail::snippet_manager::build_line_table() in libcpptrace.a(snippet.cpp.o)
|
||||||
|
```
|
||||||
|
|
||||||
|
This can happen when using apple clang to compile cpptrace and gcc to compile your code, or vice versa. The reason is
|
||||||
|
that apple clang defaults to libc++ and gcc defaults to libstdc++ and these two standard library implementations are not
|
||||||
|
ABI-compatible. To resolve this, ensure you are compiling both cpptrace and your code with the same standard library by
|
||||||
|
either using the same compiler for both or using `-stdlib=libc++`/`-stdlib=libstdc++` to control which standard library
|
||||||
|
is used.
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
I'm grateful for the help I've received with this library and I welcome contributions! For information on contributing
|
I'm grateful for the help I've received with this library and I welcome contributions! For information on contributing
|
||||||
|
|||||||
@ -180,7 +180,7 @@ option(CPPTRACE_SKIP_UNIT "" OFF)
|
|||||||
option(CPPTRACE_STD_FORMAT "" ON)
|
option(CPPTRACE_STD_FORMAT "" ON)
|
||||||
option(CPPTRACE_UNPREFIXED_TRY_CATCH "" OFF)
|
option(CPPTRACE_UNPREFIXED_TRY_CATCH "" OFF)
|
||||||
option(CPPTRACE_USE_EXTERNAL_GTEST "" OFF)
|
option(CPPTRACE_USE_EXTERNAL_GTEST "" OFF)
|
||||||
set(CPPTRACE_ZSTD_URL "https://github.com/facebook/zstd/releases/download/v1.5.6/zstd-1.5.6.tar.gz" CACHE STRING "")
|
set(CPPTRACE_ZSTD_URL "https://github.com/facebook/zstd/releases/download/v1.5.7/zstd-1.5.7.tar.gz" CACHE STRING "")
|
||||||
set(CPPTRACE_LIBDWARF_REPO "https://github.com/jeremy-rifkin/libdwarf-lite.git" CACHE STRING "")
|
set(CPPTRACE_LIBDWARF_REPO "https://github.com/jeremy-rifkin/libdwarf-lite.git" CACHE STRING "")
|
||||||
set(CPPTRACE_LIBDWARF_TAG "fe09ca800b988e2ff21225ac5e7468ceade2a30e" CACHE STRING "") # v0.11.1
|
set(CPPTRACE_LIBDWARF_TAG "fe09ca800b988e2ff21225ac5e7468ceade2a30e" CACHE STRING "") # v0.11.1
|
||||||
set(CPPTRACE_LIBDWARF_SHALLOW "1" CACHE STRING "")
|
set(CPPTRACE_LIBDWARF_SHALLOW "1" CACHE STRING "")
|
||||||
|
|||||||
@ -168,5 +168,6 @@ struct ctrace_safe_object_frame {
|
|||||||
};
|
};
|
||||||
size_t ctrace_safe_generate_raw_trace(ctrace_frame_ptr* buffer, size_t size, size_t skip, size_t max_depth);
|
size_t ctrace_safe_generate_raw_trace(ctrace_frame_ptr* buffer, size_t size, size_t skip, size_t max_depth);
|
||||||
void ctrace_get_safe_object_frame(ctrace_frame_ptr address, ctrace_safe_object_frame* out);
|
void ctrace_get_safe_object_frame(ctrace_frame_ptr address, ctrace_safe_object_frame* out);
|
||||||
ctrace_bool can_signal_safe_unwind();
|
ctrace_bool ctrace_can_signal_safe_unwind();
|
||||||
|
ctrace_bool ctrace_can_get_safe_object_frame();
|
||||||
```
|
```
|
||||||
|
|||||||
@ -73,6 +73,7 @@ namespace cpptrace {
|
|||||||
void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
|
void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
|
||||||
// signal-safe
|
// signal-safe
|
||||||
bool can_signal_safe_unwind();
|
bool can_signal_safe_unwind();
|
||||||
|
bool can_get_safe_object_frame();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -104,6 +105,9 @@ only ways to do this safely as far as I can tell.
|
|||||||
`safe_generate_raw_trace` will just produce an empty trace and if object information can't be resolved in a signal-safe
|
`safe_generate_raw_trace` will just produce an empty trace and if object information can't be resolved in a signal-safe
|
||||||
way then `get_safe_object_frame` will not populate fields beyond the `raw_address`.
|
way then `get_safe_object_frame` will not populate fields beyond the `raw_address`.
|
||||||
|
|
||||||
|
`cpptrace::can_signal_safe_unwind` and `cpptrace::can_get_safe_object_frame` can be used to check for safe tracing
|
||||||
|
support.
|
||||||
|
|
||||||
Currently the only back-end that can unwind safely is libunwind. Currently, the only way I know to get `dladdr`'s
|
Currently the only back-end that can unwind safely is libunwind. Currently, the only way I know to get `dladdr`'s
|
||||||
information in a signal-safe manner is `_dl_find_object`, which doesn't exist on macos (or windows of course). If anyone
|
information in a signal-safe manner is `_dl_find_object`, which doesn't exist on macos (or windows of course). If anyone
|
||||||
knows ways to do these safely on other platforms, I'd be much appreciative.
|
knows ways to do these safely on other platforms, I'd be much appreciative.
|
||||||
|
|||||||
@ -235,6 +235,7 @@ namespace cpptrace {
|
|||||||
// signal-safe
|
// signal-safe
|
||||||
CPPTRACE_EXPORT void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
|
CPPTRACE_EXPORT void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
|
||||||
CPPTRACE_EXPORT bool can_signal_safe_unwind();
|
CPPTRACE_EXPORT bool can_signal_safe_unwind();
|
||||||
|
CPPTRACE_EXPORT bool can_get_safe_object_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
|||||||
@ -131,7 +131,8 @@ CTRACE_BEGIN_DEFINITIONS
|
|||||||
/* ctrace::safe: */
|
/* ctrace::safe: */
|
||||||
CPPTRACE_EXPORT size_t ctrace_safe_generate_raw_trace(ctrace_frame_ptr* buffer, size_t size, size_t skip, size_t max_depth);
|
CPPTRACE_EXPORT size_t ctrace_safe_generate_raw_trace(ctrace_frame_ptr* buffer, size_t size, size_t skip, size_t max_depth);
|
||||||
CPPTRACE_EXPORT void ctrace_get_safe_object_frame(ctrace_frame_ptr address, ctrace_safe_object_frame* out);
|
CPPTRACE_EXPORT void ctrace_get_safe_object_frame(ctrace_frame_ptr address, ctrace_safe_object_frame* out);
|
||||||
CPPTRACE_EXPORT ctrace_bool can_signal_safe_unwind(void);
|
CPPTRACE_EXPORT ctrace_bool ctrace_can_signal_safe_unwind(void);
|
||||||
|
CPPTRACE_EXPORT ctrace_bool ctrace_can_get_safe_object_frame(void);
|
||||||
|
|
||||||
/* ctrace::io: */
|
/* ctrace::io: */
|
||||||
CPPTRACE_EXPORT ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color);
|
CPPTRACE_EXPORT ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color);
|
||||||
|
|||||||
@ -103,7 +103,7 @@ namespace detail {
|
|||||||
|
|
||||||
Result<const char*, internal_error> mach_o::symtab_info_data::get_string(std::size_t index) const {
|
Result<const char*, internal_error> mach_o::symtab_info_data::get_string(std::size_t index) const {
|
||||||
if(stringtab && index < symtab.strsize) {
|
if(stringtab && index < symtab.strsize) {
|
||||||
return stringtab.get() + index;
|
return stringtab.unwrap().data() + index;
|
||||||
} else {
|
} else {
|
||||||
return internal_error("can't retrieve symbol from symtab");
|
return internal_error("can't retrieve symbol from symtab");
|
||||||
}
|
}
|
||||||
@ -287,7 +287,7 @@ namespace detail {
|
|||||||
}
|
}
|
||||||
print_symbol_table_entry(
|
print_symbol_table_entry(
|
||||||
entry.unwrap_value(),
|
entry.unwrap_value(),
|
||||||
stringtab ? stringtab.unwrap_value().get() : nullptr,
|
stringtab ? stringtab.unwrap_value().data() : nullptr,
|
||||||
symtab.strsize,
|
symtab.strsize,
|
||||||
j
|
j
|
||||||
);
|
);
|
||||||
@ -642,12 +642,12 @@ namespace detail {
|
|||||||
return common;
|
return common;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<std::unique_ptr<char[]>, internal_error> mach_o::load_string_table(std::uint32_t offset, std::uint32_t byte_count) const {
|
Result<std::vector<char>, internal_error> mach_o::load_string_table(std::uint32_t offset, std::uint32_t byte_count) const {
|
||||||
std::unique_ptr<char[]> buffer(new char[byte_count + 1]);
|
std::vector<char> buffer(byte_count + 1);
|
||||||
if(std::fseek(file, load_base + offset, SEEK_SET) != 0) {
|
if(std::fseek(file, load_base + offset, SEEK_SET) != 0) {
|
||||||
return internal_error("fseek error while loading mach-o symbol table");
|
return internal_error("fseek error while loading mach-o symbol table");
|
||||||
}
|
}
|
||||||
if(std::fread(buffer.get(), sizeof(char), byte_count, file) != byte_count) {
|
if(std::fread(buffer.data(), sizeof(char), byte_count, file) != byte_count) {
|
||||||
return internal_error("fread error while loading mach-o symbol table");
|
return internal_error("fread error while loading mach-o symbol table");
|
||||||
}
|
}
|
||||||
buffer[byte_count] = 0; // just out of an abundance of caution
|
buffer[byte_count] = 0; // just out of an abundance of caution
|
||||||
|
|||||||
@ -63,7 +63,7 @@ namespace detail {
|
|||||||
|
|
||||||
struct symtab_info_data {
|
struct symtab_info_data {
|
||||||
symtab_command symtab;
|
symtab_command symtab;
|
||||||
std::unique_ptr<char[]> stringtab;
|
optional<std::vector<char>> stringtab;
|
||||||
Result<const char*, internal_error> get_string(std::size_t index) const;
|
Result<const char*, internal_error> get_string(std::size_t index) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ namespace detail {
|
|||||||
template<std::size_t Bits>
|
template<std::size_t Bits>
|
||||||
Result<nlist_64, internal_error> load_symtab_entry(std::uint32_t symbol_base, std::size_t index) const;
|
Result<nlist_64, internal_error> load_symtab_entry(std::uint32_t symbol_base, std::size_t index) const;
|
||||||
|
|
||||||
Result<std::unique_ptr<char[]>, internal_error> load_string_table(std::uint32_t offset, std::uint32_t byte_count) const;
|
Result<std::vector<char>, internal_error> load_string_table(std::uint32_t offset, std::uint32_t byte_count) const;
|
||||||
|
|
||||||
bool should_swap() const;
|
bool should_swap() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -78,9 +78,11 @@ namespace detail {
|
|||||||
- reinterpret_cast<std::uintptr_t>(info.dli_fbase)
|
- reinterpret_cast<std::uintptr_t>(info.dli_fbase)
|
||||||
+ base.unwrap_value();
|
+ base.unwrap_value();
|
||||||
} else {
|
} else {
|
||||||
|
if(!should_absorb_trace_exceptions()) {
|
||||||
base.drop_error();
|
base.drop_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -101,9 +103,11 @@ namespace detail {
|
|||||||
- reinterpret_cast<std::uintptr_t>(info.dli_fbase)
|
- reinterpret_cast<std::uintptr_t>(info.dli_fbase)
|
||||||
+ base.unwrap_value();
|
+ base.unwrap_value();
|
||||||
} else {
|
} else {
|
||||||
|
if(!should_absorb_trace_exceptions()) {
|
||||||
base.drop_error();
|
base.drop_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -146,8 +150,10 @@ namespace detail {
|
|||||||
- reinterpret_cast<std::uintptr_t>(handle)
|
- reinterpret_cast<std::uintptr_t>(handle)
|
||||||
+ base.unwrap_value();
|
+ base.unwrap_value();
|
||||||
} else {
|
} else {
|
||||||
|
if(!should_absorb_trace_exceptions()) {
|
||||||
base.drop_error();
|
base.drop_error();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
|
std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,6 +53,10 @@ namespace detail {
|
|||||||
// may return the object that defines the function descriptor (and not the object that contains the code
|
// may return the object that defines the function descriptor (and not the object that contains the code
|
||||||
// implementing the function), or fail to find any object at all.
|
// implementing the function), or fail to find any object at all.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_get_safe_object_frame() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -63,6 +67,10 @@ namespace detail {
|
|||||||
out->address_relative_to_object_start = 0;
|
out->address_relative_to_object_start = 0;
|
||||||
out->object_path[0] = 0;
|
out->object_path[0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_get_safe_object_frame() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -6,6 +6,8 @@
|
|||||||
namespace cpptrace {
|
namespace cpptrace {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
|
void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
|
||||||
|
|
||||||
|
bool has_get_safe_object_frame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -333,4 +333,8 @@ namespace cpptrace {
|
|||||||
bool can_signal_safe_unwind() {
|
bool can_signal_safe_unwind() {
|
||||||
return detail::has_safe_unwind();
|
return detail::has_safe_unwind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool can_get_safe_object_frame() {
|
||||||
|
return detail::has_get_safe_object_frame();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -310,10 +310,14 @@ extern "C" {
|
|||||||
cpptrace::get_safe_object_frame(address, reinterpret_cast<cpptrace::safe_object_frame*>(out));
|
cpptrace::get_safe_object_frame(address, reinterpret_cast<cpptrace::safe_object_frame*>(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrace_bool can_signal_safe_unwind() {
|
ctrace_bool ctrace_can_signal_safe_unwind() {
|
||||||
return cpptrace::can_signal_safe_unwind();
|
return cpptrace::can_signal_safe_unwind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctrace_bool ctrace_can_get_safe_object_frame(void) {
|
||||||
|
return cpptrace::can_get_safe_object_frame();
|
||||||
|
}
|
||||||
|
|
||||||
// ctrace::io:
|
// ctrace::io:
|
||||||
ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color) {
|
ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color) {
|
||||||
if(!trace || !trace->frames) {
|
if(!trace || !trace->frames) {
|
||||||
|
|||||||
@ -208,18 +208,19 @@ namespace cpptrace {
|
|||||||
const auto yellow = color ? YELLOW : "";
|
const auto yellow = color ? YELLOW : "";
|
||||||
const auto blue = color ? BLUE : "";
|
const auto blue = color ? BLUE : "";
|
||||||
if(frame.is_inline) {
|
if(frame.is_inline) {
|
||||||
microfmt::print(stream, "{<{}}", 2 * sizeof(frame_ptr) + 2, "(inlined)");
|
microfmt::print(stream, "{<{}} ", 2 * sizeof(frame_ptr) + 2, "(inlined)");
|
||||||
} else {
|
} else if(options.addresses != address_mode::none) {
|
||||||
auto address = options.addresses == address_mode::raw ? frame.raw_address : frame.object_address;
|
auto address = options.addresses == address_mode::raw ? frame.raw_address : frame.object_address;
|
||||||
microfmt::print(stream, "{}0x{>{}:0h}{}", blue, 2 * sizeof(frame_ptr), address, reset);
|
microfmt::print(stream, "{}0x{>{}:0h}{} ", blue, 2 * sizeof(frame_ptr), address, reset);
|
||||||
}
|
}
|
||||||
if(!frame.symbol.empty()) {
|
if(!frame.symbol.empty()) {
|
||||||
microfmt::print(stream, " in {}{}{}", yellow, frame.symbol, reset);
|
microfmt::print(stream, "in {}{}{}", yellow, frame.symbol, reset);
|
||||||
}
|
}
|
||||||
if(!frame.filename.empty()) {
|
if(!frame.filename.empty()) {
|
||||||
microfmt::print(
|
microfmt::print(
|
||||||
stream,
|
stream,
|
||||||
" at {}{}{}",
|
"{}at {}{}{}",
|
||||||
|
frame.symbol.empty() ? "" : " ",
|
||||||
green,
|
green,
|
||||||
options.paths == path_mode::full ? frame.filename : detail::basename(frame.filename, true),
|
options.paths == path_mode::full ? frame.filename : detail::basename(frame.filename, true),
|
||||||
reset
|
reset
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
|| defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) \
|
|| defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) \
|
||||||
|| defined(CPPTRACE_DEMANGLE_WITH_WINAPI)
|
|| defined(CPPTRACE_DEMANGLE_WITH_WINAPI)
|
||||||
|
|
||||||
|
#include "utils/common.hpp"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
@ -20,8 +22,8 @@ namespace detail {
|
|||||||
~dbghelp_syminit_info();
|
~dbghelp_syminit_info();
|
||||||
void release();
|
void release();
|
||||||
|
|
||||||
static dbghelp_syminit_info make_not_owned(void* handle);
|
NODISCARD static dbghelp_syminit_info make_not_owned(void* handle);
|
||||||
static dbghelp_syminit_info make_owned(void* handle, bool should_close_handle);
|
NODISCARD static dbghelp_syminit_info make_owned(void* handle, bool should_close_handle);
|
||||||
|
|
||||||
dbghelp_syminit_info(const dbghelp_syminit_info&) = delete;
|
dbghelp_syminit_info(const dbghelp_syminit_info&) = delete;
|
||||||
dbghelp_syminit_info(dbghelp_syminit_info&&);
|
dbghelp_syminit_info(dbghelp_syminit_info&&);
|
||||||
@ -39,7 +41,7 @@ namespace detail {
|
|||||||
// - Calls SymInitialize and returns an owning dbghelp_syminit_info which will handle cleanup
|
// - Calls SymInitialize and returns an owning dbghelp_syminit_info which will handle cleanup
|
||||||
dbghelp_syminit_info ensure_syminit();
|
dbghelp_syminit_info ensure_syminit();
|
||||||
|
|
||||||
std::unique_lock<std::recursive_mutex> get_dbghelp_lock();
|
NODISCARD std::unique_lock<std::recursive_mutex> get_dbghelp_lock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,7 @@ namespace libdwarf {
|
|||||||
if(!resolver) {
|
if(!resolver) {
|
||||||
// this seems silly but it's an attempt to not repeatedly try to initialize new dwarf_resolvers if
|
// this seems silly but it's an attempt to not repeatedly try to initialize new dwarf_resolvers if
|
||||||
// exceptions are thrown, e.g. if the path doesn't exist
|
// exceptions are thrown, e.g. if the path doesn't exist
|
||||||
resolver = std::unique_ptr<null_resolver>(new null_resolver);
|
resolver = detail::make_unique<null_resolver>();
|
||||||
resolver = make_dwarf_resolver(object_path);
|
resolver = make_dwarf_resolver(object_path);
|
||||||
}
|
}
|
||||||
return resolver;
|
return resolver;
|
||||||
@ -198,7 +198,7 @@ namespace libdwarf {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<symbol_resolver> make_debug_map_resolver(const std::string& object_path) {
|
std::unique_ptr<symbol_resolver> make_debug_map_resolver(const std::string& object_path) {
|
||||||
return std::unique_ptr<debug_map_resolver>(new debug_map_resolver(object_path));
|
return detail::make_unique<debug_map_resolver>(object_path);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@ -932,12 +932,7 @@ namespace libdwarf {
|
|||||||
if(it == split_full_cu_resolvers.end()) {
|
if(it == split_full_cu_resolvers.end()) {
|
||||||
it = split_full_cu_resolvers.emplace(
|
it = split_full_cu_resolvers.emplace(
|
||||||
off,
|
off,
|
||||||
std::unique_ptr<dwarf_resolver>(
|
detail::make_unique<dwarf_resolver>(path, skeleton_info{cu_die.clone(), dwversion, *this})
|
||||||
new dwarf_resolver(
|
|
||||||
path,
|
|
||||||
skeleton_info{cu_die.clone(), dwversion, *this}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).first;
|
).first;
|
||||||
}
|
}
|
||||||
res = it->second->resolve_frame(object_frame_info);
|
res = it->second->resolve_frame(object_frame_info);
|
||||||
@ -1015,7 +1010,7 @@ namespace libdwarf {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<symbol_resolver> make_dwarf_resolver(const std::string& object_path) {
|
std::unique_ptr<symbol_resolver> make_dwarf_resolver(const std::string& object_path) {
|
||||||
return std::unique_ptr<dwarf_resolver>(new dwarf_resolver(object_path));
|
return detail::make_unique<dwarf_resolver>(object_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,6 +83,8 @@ namespace detail {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Symbol resolution code should probably handle when object addresses are 0
|
||||||
|
|
||||||
std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) {
|
std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) {
|
||||||
#if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) && defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
|
#if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) && defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
|
||||||
std::vector<stacktrace_frame> trace = libdwarf::resolve_frames(frames);
|
std::vector<stacktrace_frame> trace = libdwarf::resolve_frames(frames);
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include "binary/object.hpp"
|
#include "binary/object.hpp"
|
||||||
#include "utils/common.hpp"
|
#include "utils/common.hpp"
|
||||||
#include "utils/error.hpp"
|
#include "utils/error.hpp"
|
||||||
|
#include "utils/utils.hpp"
|
||||||
#include "options.hpp"
|
#include "options.hpp"
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
@ -239,6 +240,9 @@ namespace dbghelp {
|
|||||||
std::size_t sz = sizeof(TI_FINDCHILDREN_PARAMS) +
|
std::size_t sz = sizeof(TI_FINDCHILDREN_PARAMS) +
|
||||||
(n_children) * sizeof(TI_FINDCHILDREN_PARAMS::ChildId[0]);
|
(n_children) * sizeof(TI_FINDCHILDREN_PARAMS::ChildId[0]);
|
||||||
TI_FINDCHILDREN_PARAMS* children = (TI_FINDCHILDREN_PARAMS*) new char[sz];
|
TI_FINDCHILDREN_PARAMS* children = (TI_FINDCHILDREN_PARAMS*) new char[sz];
|
||||||
|
auto guard = scope_exit([&] {
|
||||||
|
delete[] (char*) children;
|
||||||
|
});
|
||||||
children->Start = 0;
|
children->Start = 0;
|
||||||
children->Count = n_children;
|
children->Count = n_children;
|
||||||
if(
|
if(
|
||||||
@ -264,7 +268,6 @@ namespace dbghelp {
|
|||||||
extent += (i == 0 ? "" : ", ") + resolve_type(children->ChildId[i], proc, modbase);
|
extent += (i == 0 ? "" : ", ") + resolve_type(children->ChildId[i], proc, modbase);
|
||||||
}
|
}
|
||||||
extent += ")";
|
extent += ")";
|
||||||
delete[] (char*) children;
|
|
||||||
return {return_type.base, extent + return_type.extent};
|
return {return_type.base, extent + return_type.extent};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -201,16 +201,11 @@ namespace detail {
|
|||||||
// Also allow file_wrapper file = std::fopen(object_path.c_str(), "rb");
|
// Also allow file_wrapper file = std::fopen(object_path.c_str(), "rb");
|
||||||
template<
|
template<
|
||||||
typename T,
|
typename T,
|
||||||
typename D
|
typename D,
|
||||||
// workaround for:
|
// Note: Previously checked if D was invocable and returned void but this kept causing problems for MSVC
|
||||||
// == 19.38-specific msvc bug https://developercommunity.visualstudio.com/t/MSVC-1938331290-preview-fails-to-comp/10505565
|
// == 19.38-specific msvc bug https://developercommunity.visualstudio.com/t/MSVC-1938331290-preview-fails-to-comp/10505565
|
||||||
// <= 19.23 msvc also appears to fail (but for a different reason https://godbolt.org/z/6Y5EvdWPK)
|
// <= 19.23 msvc also appears to fail (but for a different reason https://godbolt.org/z/6Y5EvdWPK)
|
||||||
#if !defined(_MSC_VER) || !(_MSC_VER <= 1923 || _MSC_VER == 1938)
|
// <= 19.39 msvc also has trouble with it for different reasons https://godbolt.org/z/aPPPT7z3z
|
||||||
,
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same<decltype(std::declval<D>()(std::declval<T>())), void>::value,
|
|
||||||
int
|
|
||||||
>::type = 0,
|
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
std::is_standard_layout<T>::value && std::is_trivial<T>::value,
|
std::is_standard_layout<T>::value && std::is_trivial<T>::value,
|
||||||
int
|
int
|
||||||
@ -219,7 +214,6 @@ namespace detail {
|
|||||||
std::is_nothrow_move_constructible<T>::value,
|
std::is_nothrow_move_constructible<T>::value,
|
||||||
int
|
int
|
||||||
>::type = 0
|
>::type = 0
|
||||||
#endif
|
|
||||||
>
|
>
|
||||||
class raii_wrapper {
|
class raii_wrapper {
|
||||||
T obj;
|
T obj;
|
||||||
@ -251,22 +245,7 @@ namespace detail {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<
|
template<typename T, typename D>
|
||||||
typename T,
|
|
||||||
typename D
|
|
||||||
// workaround a msvc bug https://developercommunity.visualstudio.com/t/MSVC-1938331290-preview-fails-to-comp/10505565
|
|
||||||
#if !defined(_MSC_VER) || _MSC_VER != 1938
|
|
||||||
,
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same<decltype(std::declval<D>()(std::declval<T>())), void>::value,
|
|
||||||
int
|
|
||||||
>::type = 0,
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_standard_layout<T>::value && std::is_trivial<T>::value,
|
|
||||||
int
|
|
||||||
>::type = 0
|
|
||||||
#endif
|
|
||||||
>
|
|
||||||
raii_wrapper<typename std::remove_reference<T>::type, D> raii_wrap(T obj, D deleter) {
|
raii_wrapper<typename std::remove_reference<T>::type, D> raii_wrap(T obj, D deleter) {
|
||||||
return raii_wrapper<typename std::remove_reference<T>::type, D>(obj, deleter);
|
return raii_wrapper<typename std::remove_reference<T>::type, D>(obj, deleter);
|
||||||
}
|
}
|
||||||
@ -298,6 +277,36 @@ namespace detail {
|
|||||||
return *ptr;
|
return *ptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
class scope_guard {
|
||||||
|
F f;
|
||||||
|
bool active;
|
||||||
|
public:
|
||||||
|
template<
|
||||||
|
typename G,
|
||||||
|
typename std::enable_if<!std::is_same<typename std::decay<G>::type, scope_guard<F>>::value, int>::type = 0
|
||||||
|
>
|
||||||
|
scope_guard(G&& f) : f(std::forward<F>(f)), active(true) {}
|
||||||
|
~scope_guard() {
|
||||||
|
if(active) {
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope_guard(const scope_guard&) = delete;
|
||||||
|
scope_guard(scope_guard&& other) : f(std::move(other.f)), active(exchange(other.active, false)) {}
|
||||||
|
scope_guard& operator=(const scope_guard&) = delete;
|
||||||
|
scope_guard& operator=(scope_guard&& other) {
|
||||||
|
f = std::move(other.f);
|
||||||
|
active = exchange(other.active, false);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
NODISCARD auto scope_exit(F&& f) -> scope_guard<F> {
|
||||||
|
return scope_guard<F>(std::forward<F>(f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -96,6 +96,21 @@ TEST(FormatterTest, ObjectAddresses) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(FormatterTest, NoAddresses) {
|
||||||
|
auto formatter = cpptrace::formatter{}
|
||||||
|
.addresses(cpptrace::formatter::address_mode::none);
|
||||||
|
auto res = split(formatter.format(make_test_stacktrace()), "\n");
|
||||||
|
EXPECT_THAT(
|
||||||
|
res,
|
||||||
|
ElementsAre(
|
||||||
|
"Stack trace (most recent call first):",
|
||||||
|
"#0 in foo() at foo.cpp:20:30",
|
||||||
|
"#1 in bar() at bar.cpp:30:40",
|
||||||
|
"#2 in main at foo.cpp:40:25"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, PathShortening) {
|
TEST(FormatterTest, PathShortening) {
|
||||||
cpptrace::stacktrace trace;
|
cpptrace::stacktrace trace;
|
||||||
trace.frames.push_back({0x1, 0x1001, {20}, {30}, "/home/foo/foo.cpp", "foo()", false});
|
trace.frames.push_back({0x1, 0x1001, {20}, {30}, "/home/foo/foo.cpp", "foo()", false});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user