mod error;
pub use error::Error;
use std::sync::atomic;
pub trait CrashEvent: Sync + Send {
fn on_crash(&self, minidump_path: std::path::PathBuf);
}
impl<F> CrashEvent for F
where
F: Fn(std::path::PathBuf) + Send + Sync,
{
fn on_crash(&self, minidump_path: std::path::PathBuf) {
self(minidump_path);
}
}
static HANDLER_ATTACHED: atomic::AtomicBool = atomic::AtomicBool::new(false);
pub enum InstallOptions {
NoHandlers,
ExceptionHandler,
SignalHandler,
BothHandlers,
}
pub struct BreakpadHandler {
handler: *mut breakpad_sys::ExceptionHandler,
on_crash: *mut std::ffi::c_void,
}
#[allow(unsafe_code)]
unsafe impl Send for BreakpadHandler {}
#[allow(unsafe_code)]
unsafe impl Sync for BreakpadHandler {}
impl BreakpadHandler {
pub fn attach<P: AsRef<std::path::Path>>(
crash_dir: P,
install_opts: InstallOptions,
on_crash: Box<dyn CrashEvent>,
) -> Result<Self, Error> {
match HANDLER_ATTACHED.compare_exchange(
false,
true,
atomic::Ordering::Relaxed,
atomic::Ordering::Relaxed,
) {
Ok(true) | Err(true) => return Err(Error::HandlerAlreadyRegistered),
_ => {}
}
let on_crash = Box::into_raw(Box::new(on_crash)).cast();
#[allow(unsafe_code)]
unsafe {
let os_str = crash_dir.as_ref().as_os_str();
let path: Vec<breakpad_sys::PathChar> = {
#[cfg(windows)]
{
use std::os::windows::ffi::OsStrExt;
os_str.encode_wide().collect()
}
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
Vec::from(os_str.as_bytes())
}
};
extern "C" fn crash_callback(
path: *const breakpad_sys::PathChar,
path_len: usize,
ctx: *mut std::ffi::c_void,
) {
let path_slice = unsafe { std::slice::from_raw_parts(path, path_len) };
let path = {
#[cfg(windows)]
{
use std::os::windows::ffi::OsStringExt;
std::path::PathBuf::from(std::ffi::OsString::from_wide(path_slice))
}
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
std::path::PathBuf::from(std::ffi::OsStr::from_bytes(path_slice).to_owned())
}
};
let context: Box<Box<dyn CrashEvent>> = unsafe { Box::from_raw(ctx.cast()) };
context.on_crash(path);
Box::leak(context);
}
let install_opts = match install_opts {
InstallOptions::NoHandlers => breakpad_sys::INSTALL_NO_HANDLER,
InstallOptions::ExceptionHandler => breakpad_sys::INSTALL_EXCEPTION_HANDLER,
InstallOptions::SignalHandler => breakpad_sys::INSTALL_SIGNAL_HANDLER,
InstallOptions::BothHandlers => breakpad_sys::INSTALL_BOTH_HANDLERS,
};
let handler = breakpad_sys::attach_exception_handler(
path.as_ptr(),
path.len(),
crash_callback,
on_crash,
install_opts,
);
Ok(Self { handler, on_crash })
}
}
}
impl Drop for BreakpadHandler {
fn drop(&mut self) {
#[allow(unsafe_code)]
unsafe {
breakpad_sys::detach_exception_handler(self.handler);
let _: Box<Box<dyn CrashEvent>> = Box::from_raw(self.on_crash.cast());
HANDLER_ATTACHED.swap(false, atomic::Ordering::Relaxed);
}
}
}