diff --git a/README.md b/README.md index 1d04b7c..5909752 100644 --- a/README.md +++ b/README.md @@ -423,6 +423,10 @@ namespace cpptrace { `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_minimal_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 +non-signal-safe functions 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 what is involved at [signal-safe-tracing.md](signal-safe-tracing.md). diff --git a/signal-safe-tracing.md b/signal-safe-tracing.md index d396da1..c1f0166 100644 --- a/signal-safe-tracing.md +++ b/signal-safe-tracing.md @@ -80,6 +80,12 @@ 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 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()` Of the three strategies, `fork()` + `exec()`, is the most technically involved and the only way to resolve while the @@ -96,6 +102,8 @@ The main program handles most of the complexity for tracing from signal handlers - Resolving raw frame pointers to minimal object frames - Sending that info to the other process +Also note: A warmup for the library is done in main. + A basic implementation is as follows: ```cpp @@ -155,7 +163,16 @@ void handler(int signo, siginfo_t* info, void* context) { _exit(1); } +void warmup_cpptrace() { + // This is done for any dynamic-loading shenanigans + cpptrace::frame_ptr buffer[10]; + std::size_t count = cpptrace::safe_generate_raw_trace(buffer, 10); + cpptrace::minimal_object_frame frame; + cpptrace::get_minimal_object_frame(buffer[0], &frame); +} + int main() { + warmup_cpptrace(); // Setup signal handler struct sigaction action = { 0 }; action.sa_flags = 0; diff --git a/test/signal_demo.cpp b/test/signal_demo.cpp index 8c6c082..3d757f7 100644 --- a/test/signal_demo.cpp +++ b/test/signal_demo.cpp @@ -65,9 +65,17 @@ void handler(int signo, siginfo_t* info, void* context) { _exit(1); } +void warmup_cpptrace() { + cpptrace::frame_ptr buffer[10]; + std::size_t count = cpptrace::safe_generate_raw_trace(buffer, 10); + cpptrace::minimal_object_frame frame; + cpptrace::get_minimal_object_frame(buffer[0], &frame); +} + int main() { cpptrace::absorb_trace_exceptions(false); cpptrace::register_terminate_handler(); + warmup_cpptrace(); struct sigaction action = { 0 }; action.sa_flags = 0;