Add can_signal_safe_unwind and update some documentation surrounding signal-safe stack tracing
This commit is contained in:
parent
ec264aa0eb
commit
da739d30c5
18
README.md
18
README.md
@ -42,6 +42,8 @@ Cpptrace also has a C API, docs [here](docs/c-api.md).
|
|||||||
- [Conan](#conan)
|
- [Conan](#conan)
|
||||||
- [Vcpkg](#vcpkg)
|
- [Vcpkg](#vcpkg)
|
||||||
- [Platform Logistics](#platform-logistics)
|
- [Platform Logistics](#platform-logistics)
|
||||||
|
- [Windows](#windows)
|
||||||
|
- [macOS](#macos)
|
||||||
- [Library Back-Ends](#library-back-ends)
|
- [Library Back-Ends](#library-back-ends)
|
||||||
- [Summary of Library Configurations](#summary-of-library-configurations)
|
- [Summary of Library Configurations](#summary-of-library-configurations)
|
||||||
- [Testing Methodology](#testing-methodology)
|
- [Testing Methodology](#testing-methodology)
|
||||||
@ -454,16 +456,20 @@ namespace cpptrace {
|
|||||||
object_frame resolve() const; // To be called outside a signal handler. Not signal safe.
|
object_frame resolve() const; // To be called outside a signal handler. Not signal safe.
|
||||||
};
|
};
|
||||||
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();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:** Not all back-ends and platforms support these interfaces. If signal-safe unwinding isn't supported,
|
> [!IMPORTANT]
|
||||||
`safe_generate_raw_trace` will just produce an empty trace, and if object information can't be resolved in a signal-safe
|
> Currently signal-safe stack unwinding is only possible with `libunwind`, which must be
|
||||||
way then `get_safe_object_frame` will not populate fields beyond the `raw_address`.
|
> [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`.
|
||||||
|
|
||||||
**Another big note:** Calls to shared objects can be lazy-loaded where the first call to the shared object invokes
|
> [!CAUTION]
|
||||||
non-signal-safe functions such as `malloc()`. To avoid this, call these routines in `main()` ahead of a signal handler
|
> Calls to shared objects can be lazy-loaded where the first call to the shared object invokes non-signal-safe functions
|
||||||
to "warm up" the library.
|
> such as `malloc()`. To avoid this, call these routines in `main()` ahead of a signal handler to "warm up" the library.
|
||||||
|
|
||||||
Because signal-safe tracing is an involved process, I have written up a comprehensive overview of
|
Because signal-safe tracing is an involved process, I have written up a comprehensive overview of
|
||||||
what is involved at [signal-safe-tracing.md](docs/signal-safe-tracing.md).
|
what is involved at [signal-safe-tracing.md](docs/signal-safe-tracing.md).
|
||||||
|
|||||||
@ -168,4 +168,5 @@ 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();
|
||||||
```
|
```
|
||||||
|
|||||||
@ -32,6 +32,13 @@ FAQ: What's the worst that could happen if you call `cpptrace::generate_trace().
|
|||||||
signal handler? In many cases you might be able to get away with it but you risk deadlocking or
|
signal handler? In many cases you might be able to get away with it but you risk deadlocking or
|
||||||
memory corruption.
|
memory corruption.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Currently signal-safe stack unwinding is only possible with `libunwind`, more details later.
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
> Calls to shared objects can be lazy-loaded where the first call to the shared object invokes non-signal-safe functions
|
||||||
|
> such as `malloc()`. Because of this, the signal safe api must be "warmed up" ahead of a signal handler.
|
||||||
|
|
||||||
# API
|
# API
|
||||||
|
|
||||||
Cpptrace provides APIs for generating raw trace information safely and then also safely resolving
|
Cpptrace provides APIs for generating raw trace information safely and then also safely resolving
|
||||||
@ -53,6 +60,8 @@ namespace cpptrace {
|
|||||||
|
|
||||||
// signal-safe
|
// signal-safe
|
||||||
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
|
||||||
|
bool can_signal_safe_unwind();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -88,12 +97,6 @@ Currently the only back-end that can unwind safely is libunwind. Currently, the
|
|||||||
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.
|
||||||
|
|
||||||
# A pitfall to be aware of
|
|
||||||
|
|
||||||
Calls to functions in shared objects can be lazy-loaded where the first call to the shared object invokes
|
|
||||||
non-signal-safe functions such as `malloc()`. To avoid this, call these safe cpptrace routines in `main()` ahead of a
|
|
||||||
signal handler to "warm up" the library.
|
|
||||||
|
|
||||||
# Signal-Safe Tracing With `fork()` + `exec()`
|
# Signal-Safe Tracing With `fork()` + `exec()`
|
||||||
|
|
||||||
Of the three strategies, `fork()` + `exec()`, is the most technically involved and the only way to resolve while the
|
Of the three strategies, `fork()` + `exec()`, is the most technically involved and the only way to resolve while the
|
||||||
|
|||||||
@ -239,6 +239,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();
|
||||||
|
|
||||||
// utilities:
|
// utilities:
|
||||||
CPPTRACE_EXPORT std::string demangle(const std::string& name);
|
CPPTRACE_EXPORT std::string demangle(const std::string& name);
|
||||||
|
|||||||
@ -131,6 +131,7 @@ 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();
|
||||||
|
|
||||||
/* 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);
|
||||||
|
|||||||
@ -399,6 +399,10 @@ namespace cpptrace {
|
|||||||
detail::get_safe_object_frame(address, out);
|
detail::get_safe_object_frame(address, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool can_signal_safe_unwind() {
|
||||||
|
return detail::has_safe_unwind();
|
||||||
|
}
|
||||||
|
|
||||||
std::string demangle(const std::string& name) {
|
std::string demangle(const std::string& name) {
|
||||||
return detail::demangle(name);
|
return detail::demangle(name);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -310,6 +310,10 @@ 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() {
|
||||||
|
return cpptrace::can_signal_safe_unwind();
|
||||||
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
|
|||||||
@ -20,6 +20,8 @@ namespace detail {
|
|||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
CPPTRACE_FORCE_NO_INLINE
|
||||||
std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth);
|
std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth);
|
||||||
|
|
||||||
|
bool has_safe_unwind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -161,6 +161,10 @@ namespace detail {
|
|||||||
#if IS_MSVC
|
#if IS_MSVC
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool has_safe_unwind() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,10 @@ namespace detail {
|
|||||||
// Can't safe trace with execinfo
|
// Can't safe trace with execinfo
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_safe_unwind() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -70,6 +70,10 @@ namespace detail {
|
|||||||
}
|
}
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_safe_unwind() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,10 @@ namespace detail {
|
|||||||
std::size_t safe_capture_frames(frame_ptr*, std::size_t, std::size_t, std::size_t) {
|
std::size_t safe_capture_frames(frame_ptr*, std::size_t, std::size_t, std::size_t) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_safe_unwind() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -65,6 +65,10 @@ namespace detail {
|
|||||||
// Can't safe trace with _Unwind
|
// Can't safe trace with _Unwind
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_safe_unwind() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -43,6 +43,10 @@ namespace detail {
|
|||||||
// Can't safe trace with winapi
|
// Can't safe trace with winapi
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_safe_unwind() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user