1mod error;
2pub use error::Error;
3
4use std::sync::atomic;
5
6pub trait CrashEvent: Sync + Send {
9 fn on_crash(&self, minidump_path: std::path::PathBuf);
10}
11
12impl<F> CrashEvent for F
13where
14 F: Fn(std::path::PathBuf) + Send + Sync,
15{
16 fn on_crash(&self, minidump_path: std::path::PathBuf) {
17 self(minidump_path);
18 }
19}
20
21static HANDLER_ATTACHED: atomic::AtomicBool = atomic::AtomicBool::new(false);
22
23pub enum InstallOptions {
27 NoHandlers,
30 ExceptionHandler,
35 SignalHandler,
43 BothHandlers,
46}
47
48pub struct BreakpadHandler {
49 handler: *mut breakpad_sys::ExceptionHandler,
50 on_crash: *mut std::ffi::c_void,
51}
52
53#[allow(unsafe_code)]
54unsafe impl Send for BreakpadHandler {}
55#[allow(unsafe_code)]
56unsafe impl Sync for BreakpadHandler {}
57
58impl BreakpadHandler {
59 pub fn attach<P: AsRef<std::path::Path>>(
63 crash_dir: P,
64 install_opts: InstallOptions,
65 on_crash: Box<dyn CrashEvent>,
66 ) -> Result<Self, Error> {
67 match HANDLER_ATTACHED.compare_exchange(
68 false,
69 true,
70 atomic::Ordering::Relaxed,
71 atomic::Ordering::Relaxed,
72 ) {
73 Ok(true) | Err(true) => return Err(Error::HandlerAlreadyRegistered),
74 _ => {}
75 }
76
77 let on_crash = Box::into_raw(Box::new(on_crash)).cast();
78
79 #[allow(unsafe_code)]
80 unsafe {
82 let os_str = crash_dir.as_ref().as_os_str();
83
84 let path: Vec<breakpad_sys::PathChar> = {
85 #[cfg(windows)]
86 {
87 use std::os::windows::ffi::OsStrExt;
88 os_str.encode_wide().collect()
89 }
90 #[cfg(unix)]
91 {
92 use std::os::unix::ffi::OsStrExt;
93 Vec::from(os_str.as_bytes())
94 }
95 };
96
97 extern "C" fn crash_callback(
98 path: *const breakpad_sys::PathChar,
99 path_len: usize,
100 ctx: *mut std::ffi::c_void,
101 ) {
102 let path_slice = unsafe { std::slice::from_raw_parts(path, path_len) };
103
104 let path = {
105 #[cfg(windows)]
106 {
107 use std::os::windows::ffi::OsStringExt;
108 std::path::PathBuf::from(std::ffi::OsString::from_wide(path_slice))
109 }
110 #[cfg(unix)]
111 {
112 use std::os::unix::ffi::OsStrExt;
113 std::path::PathBuf::from(std::ffi::OsStr::from_bytes(path_slice).to_owned())
114 }
115 };
116
117 let context: Box<Box<dyn CrashEvent>> = unsafe { Box::from_raw(ctx.cast()) };
118 context.on_crash(path);
119 Box::leak(context);
120 }
121
122 let install_opts = match install_opts {
123 InstallOptions::NoHandlers => breakpad_sys::INSTALL_NO_HANDLER,
124 InstallOptions::ExceptionHandler => breakpad_sys::INSTALL_EXCEPTION_HANDLER,
125 InstallOptions::SignalHandler => breakpad_sys::INSTALL_SIGNAL_HANDLER,
126 InstallOptions::BothHandlers => breakpad_sys::INSTALL_BOTH_HANDLERS,
127 };
128
129 let handler = breakpad_sys::attach_exception_handler(
130 path.as_ptr(),
131 path.len(),
132 crash_callback,
133 on_crash,
134 install_opts,
135 );
136
137 Ok(Self { handler, on_crash })
138 }
139 }
140}
141
142impl Drop for BreakpadHandler {
143 fn drop(&mut self) {
144 #[allow(unsafe_code)]
145 unsafe {
147 breakpad_sys::detach_exception_handler(self.handler);
148 let _: Box<Box<dyn CrashEvent>> = Box::from_raw(self.on_crash.cast());
149 HANDLER_ATTACHED.swap(false, atomic::Ordering::Relaxed);
150 }
151 }
152}