Compare commits

...

17 Commits
v0.8.0 ... main

Author SHA1 Message Date
Tsche
fac4d08fd0
fix #221 (#223) 2025-02-28 10:55:44 -06:00
Patrick Quist
c0354799c7
add arm test with gh arm runners (#222) 2025-02-25 13:23:09 -06:00
Jeremy Rifkin
c37b5ed736
Bump to v0.8.2 2025-02-23 14:03:25 -06:00
Jeremy Rifkin
a32e22aa44
Don't print internal errors when image base resolution fails, this can happen on mac since platform dylibs don't actually exist on disk. Closes #217. 2025-02-22 13:20:03 -06:00
Jeremy Rifkin
6cec10601e
Bump zstd, #219 2025-02-22 12:18:01 -06:00
Jeremy Rifkin
c9dc51aa61
Move FAQ and add entry on standard library symbol linker errors on macos, related to #216 2025-02-22 00:06:27 -06:00
Jeremy Rifkin
e6d55b5e7d
Add missing iostream include in a README example, closes #218 2025-02-22 00:01:02 -06:00
Jeremy Rifkin
1940dc607a
Bump to v0.8.1 2025-02-20 22:46:25 -06:00
Jeremy Rifkin
477aecec01
Add guard on forwarding constructor 2025-02-20 22:22:46 -06:00
Jeremy Rifkin
03b292c20b
mach-o fixes 2025-02-20 22:18:42 -06:00
Jeremy Rifkin
5bfcf280a5
Fix scope_guard forwarding 2025-02-20 22:04:07 -06:00
Jeremy Rifkin
9b02fc6f74
Replaced some un-ergonomic unique_ptr use with make_unique, switched some unique_ptr<T[]> use to just std::vector<T>, and added some RAII protection to dbghelp symbol resolution TI_FINDCHILDREN_PARAMS management 2025-02-20 22:02:01 -06:00
Jeremy Rifkin
3a4da8ccf0
Fix for an auto return type 2025-02-20 21:47:36 -06:00
Jeremy Rifkin
9a2ae3c96f
Add some NODISCARD attributes and uncomment scope_exit utility code 2025-02-20 21:43:25 -06:00
Jeremy Rifkin
6877782d96
Add cpptrace::can_get_safe_object_frame and add ctrace prefix for can_signal_safe_unwind 2025-02-20 21:36:19 -06:00
Jeremy Rifkin
98ea78445c
Add CI workflow for older msvc 2025-02-20 21:13:54 -06:00
Jeremy Rifkin
3aa080d536
Remove a SFINAE check that keeps surfacing msvc bugs, fixes #215 2025-02-20 21:07:04 -06:00
24 changed files with 284 additions and 106 deletions

View File

@ -25,6 +25,26 @@ jobs:
- name: build and test
run: |
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:
runs-on: macos-14
strategy:
@ -68,6 +88,31 @@ jobs:
- name: build and test
run: |
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:
runs-on: ubuntu-22.04
strategy:
@ -537,6 +582,32 @@ jobs:
- name: test opt
run: |
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:
runs-on: macos-14
steps:

View File

@ -1,6 +1,8 @@
# Changelog
- [Changelog](#changelog)
- [v0.8.2](#v082)
- [v0.8.1](#v081)
- [v0.8.0](#v080)
- [v0.7.5](#v075)
- [v0.7.4](#v074)
@ -26,6 +28,31 @@
- [v0.1.1](#v011)
- [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
Added:

View File

@ -9,7 +9,7 @@ set(package_name "cpptrace")
project(
cpptrace
VERSION 0.8.0
VERSION 0.8.2
DESCRIPTION "Simple, portable, and self-contained stacktrace library for C++11 and newer "
HOMEPAGE_URL "https://github.com/jeremy-rifkin/cpptrace"
LANGUAGES C CXX

102
README.md
View File

@ -16,9 +16,6 @@ Cpptrace also has a C API, docs [here](docs/c-api.md).
- [30-Second Overview](#30-second-overview)
- [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)
- [Basic Usage](#basic-usage)
- [`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)
- [Testing Methodology](#testing-methodology)
- [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)
- [License](#license)
@ -138,7 +139,7 @@ include(FetchContent)
FetchContent_Declare(
cpptrace
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)
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
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
> [!IMPORTANT]
@ -465,6 +439,8 @@ thrown exception object, with minimal or no overhead in the non-throwing path:
```cpp
#include <cpptrace/from_current.hpp>
#include <iostream>
void foo() {
throw std::runtime_error("foo failed");
}
@ -774,6 +750,7 @@ namespace cpptrace {
};
void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
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]
> 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
> produce an empty trace. `can_signal_safe_unwind` can be used to check for signal-safe unwinding support. 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`.
> produce an empty trace. `can_signal_safe_unwind` can be used to check for signal-safe unwinding support and
> `can_get_safe_object_frame` can be used to check `get_safe_object_frame` support. 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`.
> [!IMPORTANT]
> `_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(
cpptrace
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)
target_link_libraries(your_target cpptrace::cpptrace)
@ -944,7 +921,7 @@ information.
```sh
git clone https://github.com/jeremy-rifkin/cpptrace.git
git checkout v0.8.0
git checkout v0.8.2
mkdir cpptrace/build
cd cpptrace/build
cmake .. -DCMAKE_BUILD_TYPE=Release
@ -987,7 +964,7 @@ you when installing new libraries.
```ps1
git clone https://github.com/jeremy-rifkin/cpptrace.git
git checkout v0.8.0
git checkout v0.8.2
mkdir cpptrace/build
cd cpptrace/build
cmake .. -DCMAKE_BUILD_TYPE=Release
@ -1005,7 +982,7 @@ To install just for the local user (or any custom prefix):
```sh
git clone https://github.com/jeremy-rifkin/cpptrace.git
git checkout v0.8.0
git checkout v0.8.2
mkdir cpptrace/build
cd cpptrace/build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$HOME/wherever
@ -1088,7 +1065,7 @@ make install
cd ~/scratch/cpptrace-test
git clone https://github.com/jeremy-rifkin/cpptrace.git
cd cpptrace
git checkout v0.8.0
git checkout v0.8.2
mkdir 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
@ -1108,7 +1085,7 @@ cpptrace and its dependencies.
Cpptrace is available through conan at https://conan.io/center/recipes/cpptrace.
```
[requires]
cpptrace/0.8.0
cpptrace/0.8.2
[generators]
CMakeDeps
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
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
I'm grateful for the help I've received with this library and I welcome contributions! For information on contributing

View File

@ -180,7 +180,7 @@ option(CPPTRACE_SKIP_UNIT "" OFF)
option(CPPTRACE_STD_FORMAT "" ON)
option(CPPTRACE_UNPREFIXED_TRY_CATCH "" 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_TAG "fe09ca800b988e2ff21225ac5e7468ceade2a30e" CACHE STRING "") # v0.11.1
set(CPPTRACE_LIBDWARF_SHALLOW "1" CACHE STRING "")

View File

@ -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);
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();
```

View File

@ -73,6 +73,7 @@ namespace cpptrace {
void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
// signal-safe
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
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
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.

View File

@ -235,6 +235,7 @@ namespace cpptrace {
// signal-safe
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_get_safe_object_frame();
}
#ifdef _MSC_VER

View File

@ -131,7 +131,8 @@ CTRACE_BEGIN_DEFINITIONS
/* 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 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: */
CPPTRACE_EXPORT ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color);

View File

@ -103,7 +103,7 @@ namespace detail {
Result<const char*, internal_error> mach_o::symtab_info_data::get_string(std::size_t index) const {
if(stringtab && index < symtab.strsize) {
return stringtab.get() + index;
return stringtab.unwrap().data() + index;
} else {
return internal_error("can't retrieve symbol from symtab");
}
@ -287,7 +287,7 @@ namespace detail {
}
print_symbol_table_entry(
entry.unwrap_value(),
stringtab ? stringtab.unwrap_value().get() : nullptr,
stringtab ? stringtab.unwrap_value().data() : nullptr,
symtab.strsize,
j
);
@ -642,12 +642,12 @@ namespace detail {
return common;
}
Result<std::unique_ptr<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]);
Result<std::vector<char>, internal_error> mach_o::load_string_table(std::uint32_t offset, std::uint32_t byte_count) const {
std::vector<char> buffer(byte_count + 1);
if(std::fseek(file, load_base + offset, SEEK_SET) != 0) {
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");
}
buffer[byte_count] = 0; // just out of an abundance of caution

View File

@ -63,7 +63,7 @@ namespace detail {
struct symtab_info_data {
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;
};
@ -131,7 +131,7 @@ namespace detail {
template<std::size_t Bits>
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;
};

View File

@ -78,7 +78,9 @@ namespace detail {
- reinterpret_cast<std::uintptr_t>(info.dli_fbase)
+ base.unwrap_value();
} else {
base.drop_error();
if(!should_absorb_trace_exceptions()) {
base.drop_error();
}
}
}
return frame;
@ -101,7 +103,9 @@ namespace detail {
- reinterpret_cast<std::uintptr_t>(info.dli_fbase)
+ base.unwrap_value();
} else {
base.drop_error();
if(!should_absorb_trace_exceptions()) {
base.drop_error();
}
}
}
return frame;
@ -146,7 +150,9 @@ namespace detail {
- reinterpret_cast<std::uintptr_t>(handle)
+ base.unwrap_value();
} else {
base.drop_error();
if(!should_absorb_trace_exceptions()) {
base.drop_error();
}
}
} else {
std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());

View File

@ -53,6 +53,10 @@ namespace detail {
// 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.
}
bool has_get_safe_object_frame() {
return true;
}
}
}
#else
@ -63,6 +67,10 @@ namespace detail {
out->address_relative_to_object_start = 0;
out->object_path[0] = 0;
}
bool has_get_safe_object_frame() {
return false;
}
}
}
#endif

View File

@ -6,6 +6,8 @@
namespace cpptrace {
namespace detail {
void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
bool has_get_safe_object_frame();
}
}

View File

@ -333,4 +333,8 @@ namespace cpptrace {
bool can_signal_safe_unwind() {
return detail::has_safe_unwind();
}
bool can_get_safe_object_frame() {
return detail::has_get_safe_object_frame();
}
}

View File

@ -310,10 +310,14 @@ extern "C" {
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();
}
ctrace_bool ctrace_can_get_safe_object_frame(void) {
return cpptrace::can_get_safe_object_frame();
}
// ctrace::io:
ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color) {
if(!trace || !trace->frames) {

View File

@ -208,18 +208,19 @@ namespace cpptrace {
const auto yellow = color ? YELLOW : "";
const auto blue = color ? BLUE : "";
if(frame.is_inline) {
microfmt::print(stream, "{<{}}", 2 * sizeof(frame_ptr) + 2, "(inlined)");
} else {
microfmt::print(stream, "{<{}} ", 2 * sizeof(frame_ptr) + 2, "(inlined)");
} else if(options.addresses != address_mode::none) {
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()) {
microfmt::print(stream, " in {}{}{}", yellow, frame.symbol, reset);
microfmt::print(stream, "in {}{}{}", yellow, frame.symbol, reset);
}
if(!frame.filename.empty()) {
microfmt::print(
stream,
" at {}{}{}",
"{}at {}{}{}",
frame.symbol.empty() ? "" : " ",
green,
options.paths == path_mode::full ? frame.filename : detail::basename(frame.filename, true),
reset

View File

@ -5,6 +5,8 @@
|| defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) \
|| defined(CPPTRACE_DEMANGLE_WITH_WINAPI)
#include "utils/common.hpp"
#include <unordered_map>
#include <mutex>
@ -20,8 +22,8 @@ namespace detail {
~dbghelp_syminit_info();
void release();
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_not_owned(void* 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(dbghelp_syminit_info&&);
@ -39,7 +41,7 @@ namespace detail {
// - Calls SymInitialize and returns an owning dbghelp_syminit_info which will handle cleanup
dbghelp_syminit_info ensure_syminit();
std::unique_lock<std::recursive_mutex> get_dbghelp_lock();
NODISCARD std::unique_lock<std::recursive_mutex> get_dbghelp_lock();
}
}

View File

@ -34,7 +34,7 @@ namespace libdwarf {
if(!resolver) {
// 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
resolver = std::unique_ptr<null_resolver>(new null_resolver);
resolver = detail::make_unique<null_resolver>();
resolver = make_dwarf_resolver(object_path);
}
return resolver;
@ -198,7 +198,7 @@ namespace libdwarf {
};
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
}

View File

@ -932,12 +932,7 @@ namespace libdwarf {
if(it == split_full_cu_resolvers.end()) {
it = split_full_cu_resolvers.emplace(
off,
std::unique_ptr<dwarf_resolver>(
new dwarf_resolver(
path,
skeleton_info{cu_die.clone(), dwversion, *this}
)
)
detail::make_unique<dwarf_resolver>(path, skeleton_info{cu_die.clone(), dwversion, *this})
).first;
}
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) {
return std::unique_ptr<dwarf_resolver>(new dwarf_resolver(object_path));
return detail::make_unique<dwarf_resolver>(object_path);
}
}
}

View File

@ -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) {
#if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) && defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
std::vector<stacktrace_frame> trace = libdwarf::resolve_frames(frames);

View File

@ -6,6 +6,7 @@
#include "binary/object.hpp"
#include "utils/common.hpp"
#include "utils/error.hpp"
#include "utils/utils.hpp"
#include "options.hpp"
#include <regex>
@ -239,6 +240,9 @@ namespace dbghelp {
std::size_t sz = sizeof(TI_FINDCHILDREN_PARAMS) +
(n_children) * sizeof(TI_FINDCHILDREN_PARAMS::ChildId[0]);
TI_FINDCHILDREN_PARAMS* children = (TI_FINDCHILDREN_PARAMS*) new char[sz];
auto guard = scope_exit([&] {
delete[] (char*) children;
});
children->Start = 0;
children->Count = n_children;
if(
@ -264,7 +268,6 @@ namespace dbghelp {
extent += (i == 0 ? "" : ", ") + resolve_type(children->ChildId[i], proc, modbase);
}
extent += ")";
delete[] (char*) children;
return {return_type.base, extent + return_type.extent};
}
}

View File

@ -201,25 +201,19 @@ namespace detail {
// Also allow file_wrapper file = std::fopen(object_path.c_str(), "rb");
template<
typename T,
typename D
// workaround for:
typename D,
// 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.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)
,
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,
typename std::enable_if<
std::is_nothrow_move_constructible<T>::value,
int
>::type = 0
#endif
// <= 19.39 msvc also has trouble with it for different reasons https://godbolt.org/z/aPPPT7z3z
typename std::enable_if<
std::is_standard_layout<T>::value && std::is_trivial<T>::value,
int
>::type = 0,
typename std::enable_if<
std::is_nothrow_move_constructible<T>::value,
int
>::type = 0
>
class raii_wrapper {
T obj;
@ -251,22 +245,7 @@ namespace detail {
}
};
template<
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
>
template<typename T, typename D>
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);
}
@ -298,6 +277,36 @@ namespace detail {
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));
}
}
}

View File

@ -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) {
cpptrace::stacktrace trace;
trace.frames.push_back({0x1, 0x1001, {20}, {30}, "/home/foo/foo.cpp", "foo()", false});