From 6877782d966661cb3f12543846666c4a75276d8b Mon Sep 17 00:00:00 2001 From: Jeremy Rifkin <51220084+jeremy-rifkin@users.noreply.github.com> Date: Thu, 20 Feb 2025 21:36:19 -0600 Subject: [PATCH] Add cpptrace::can_get_safe_object_frame and add ctrace prefix for can_signal_safe_unwind --- README.md | 7 ++++--- docs/c-api.md | 3 ++- docs/signal-safe-tracing.md | 4 ++++ include/cpptrace/basic.hpp | 1 + include/ctrace/ctrace.h | 3 ++- src/binary/safe_dl.cpp | 8 ++++++++ src/binary/safe_dl.hpp | 2 ++ src/cpptrace.cpp | 4 ++++ src/ctrace.cpp | 6 +++++- 9 files changed, 32 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cec5053..0b5da96 100644 --- a/README.md +++ b/README.md @@ -774,6 +774,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 +791,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 diff --git a/docs/c-api.md b/docs/c-api.md index 43f91d8..928d205 100644 --- a/docs/c-api.md +++ b/docs/c-api.md @@ -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(); ``` diff --git a/docs/signal-safe-tracing.md b/docs/signal-safe-tracing.md index b9bd6cd..52bf246 100644 --- a/docs/signal-safe-tracing.md +++ b/docs/signal-safe-tracing.md @@ -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. diff --git a/include/cpptrace/basic.hpp b/include/cpptrace/basic.hpp index e953e31..735e39e 100644 --- a/include/cpptrace/basic.hpp +++ b/include/cpptrace/basic.hpp @@ -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 diff --git a/include/ctrace/ctrace.h b/include/ctrace/ctrace.h index e4924be..433a28e 100644 --- a/include/ctrace/ctrace.h +++ b/include/ctrace/ctrace.h @@ -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); diff --git a/src/binary/safe_dl.cpp b/src/binary/safe_dl.cpp index 4ed6db7..299546d 100644 --- a/src/binary/safe_dl.cpp +++ b/src/binary/safe_dl.cpp @@ -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 diff --git a/src/binary/safe_dl.hpp b/src/binary/safe_dl.hpp index 714bfff..415cf70 100644 --- a/src/binary/safe_dl.hpp +++ b/src/binary/safe_dl.hpp @@ -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(); } } diff --git a/src/cpptrace.cpp b/src/cpptrace.cpp index 180c3e6..9f04430 100644 --- a/src/cpptrace.cpp +++ b/src/cpptrace.cpp @@ -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(); + } } diff --git a/src/ctrace.cpp b/src/ctrace.cpp index e67c9e3..91439a9 100644 --- a/src/ctrace.cpp +++ b/src/ctrace.cpp @@ -310,10 +310,14 @@ extern "C" { cpptrace::get_safe_object_frame(address, reinterpret_cast(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) {