@@ -10,6 +10,8 @@ use crate::crash_info::Metadata;
1010use crate :: shared:: configuration:: CrashtrackerConfiguration ;
1111use libc:: { c_void, siginfo_t, ucontext_t} ;
1212use libdd_common:: timeout:: TimeoutManager ;
13+ use std:: panic;
14+ use std:: panic:: PanicHookInfo ;
1315use std:: ptr;
1416use std:: sync:: atomic:: Ordering :: SeqCst ;
1517use std:: sync:: atomic:: { AtomicBool , AtomicPtr , AtomicU64 } ;
@@ -35,6 +37,10 @@ use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicU64};
3537// `Box::from_raw` to recreate the box, then dropping it.
3638static METADATA : AtomicPtr < ( Metadata , String ) > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
3739static CONFIG : AtomicPtr < ( CrashtrackerConfiguration , String ) > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
40+ static PANIC_MESSAGE : AtomicPtr < String > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
41+
42+ type PanicHook = Box < dyn Fn ( & PanicHookInfo < ' _ > ) + Send + Sync > ;
43+ static PREVIOUS_PANIC_HOOK : AtomicPtr < PanicHook > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
3844
3945#[ derive( Debug , thiserror:: Error ) ]
4046pub enum CrashHandlerError {
@@ -72,6 +78,73 @@ pub fn update_metadata(metadata: Metadata) -> anyhow::Result<()> {
7278 Ok ( ( ) )
7379}
7480
81+ /// Register the panic hook.
82+ ///
83+ /// This function is used to register the panic hook and store the previous hook.
84+ /// PRECONDITIONS:
85+ /// None
86+ /// SAFETY:
87+ /// Crash-tracking functions are not guaranteed to be reentrant.
88+ /// No other crash-handler functions should be called concurrently.
89+ /// ATOMICITY:
90+ /// This function uses a swap on an atomic pointer.
91+ pub fn register_panic_hook ( ) -> anyhow:: Result < ( ) > {
92+ // register only once, if it is already registered, do nothing
93+ if !PREVIOUS_PANIC_HOOK . load ( SeqCst ) . is_null ( ) {
94+ return Ok ( ( ) ) ;
95+ }
96+
97+ let old_hook = panic:: take_hook ( ) ;
98+ let old_hook_ptr = Box :: into_raw ( Box :: new ( old_hook) ) ;
99+ PREVIOUS_PANIC_HOOK . swap ( old_hook_ptr, SeqCst ) ;
100+ panic:: set_hook ( Box :: new ( panic_hook) ) ;
101+ Ok ( ( ) )
102+ }
103+
104+ /// The panic hook function.
105+ ///
106+ /// This function is used to update the metadata with the panic message
107+ /// and call the previous hook.
108+ /// PRECONDITIONS:
109+ /// None
110+ /// SAFETY:
111+ /// Crash-tracking functions are not guaranteed to be reentrant.
112+ /// No other crash-handler functions should be called concurrently.
113+ /// ATOMICITY:
114+ /// This function uses a load on an atomic pointer.
115+ fn panic_hook ( panic_info : & PanicHookInfo < ' _ > ) {
116+ if let Some ( s) = panic_info. payload ( ) . downcast_ref :: < & str > ( ) {
117+ let message_ptr = PANIC_MESSAGE . swap ( Box :: into_raw ( Box :: new ( s. to_string ( ) ) ) , SeqCst ) ;
118+ // message_ptr should be null, but just in case.
119+ if !message_ptr. is_null ( ) {
120+ unsafe {
121+ std:: mem:: drop ( Box :: from_raw ( message_ptr) ) ;
122+ }
123+ }
124+ }
125+ call_previous_panic_hook ( panic_info) ;
126+ }
127+
128+ /// Call the previous panic hook.
129+ ///
130+ /// This function is used to call the previous panic hook.
131+ /// PRECONDITIONS:
132+ /// None
133+ /// SAFETY:
134+ /// Crash-tracking functions are not guaranteed to be reentrant.
135+ /// No other crash-handler functions should be called concurrently.
136+ fn call_previous_panic_hook ( panic_info : & PanicHookInfo < ' _ > ) {
137+ let old_hook_ptr = PREVIOUS_PANIC_HOOK . load ( SeqCst ) ;
138+ if !old_hook_ptr. is_null ( ) {
139+ // Safety: This pointer can only come from Box::into_raw above in register_panic_hook.
140+ // We borrow it here without taking ownership so it remains valid for future calls.
141+ unsafe {
142+ let old_hook = & * old_hook_ptr;
143+ old_hook ( panic_info) ;
144+ }
145+ }
146+ }
147+
75148/// Updates the crashtracker config for this process
76149/// Config is stored in a global variable and sent to the crashtracking
77150/// receiver when a crash occurs.
@@ -172,6 +245,10 @@ fn handle_posix_signal_impl(
172245 }
173246 let ( _metadata, metadata_string) = unsafe { & * metadata_ptr } ;
174247
248+ // Get the panic message pointer but don't dereference or deallocate in signal handler.
249+ // The collector child process will handle converting this to a String after forking.
250+ let message_ptr = PANIC_MESSAGE . swap ( ptr:: null_mut ( ) , SeqCst ) ;
251+
175252 let timeout_manager = TimeoutManager :: new ( config. timeout ( ) ) ;
176253
177254 // Optionally, create the receiver. This all hinges on whether or not the configuration has a
@@ -190,6 +267,7 @@ fn handle_posix_signal_impl(
190267 config,
191268 config_str,
192269 metadata_string,
270+ message_ptr,
193271 sig_info,
194272 ucontext,
195273 ) ?;
0 commit comments