diff options
| author | Manish Goregaokar <manishsmail@gmail.com> | 2015-12-25 16:54:30 +0530 |
|---|---|---|
| committer | Manish Goregaokar <manishsmail@gmail.com> | 2015-12-25 16:54:30 +0530 |
| commit | 12f171b052c07d60760cabbc109d15188ce285ec (patch) | |
| tree | 8afb6543bf472286e76868fdf6c3078fc990aa16 /src/libstd | |
| parent | 711f11e8d607b3ecf297366fa704b5200709d12d (diff) | |
| parent | f1148a540a16ba4ba2635403a9b014da9683851b (diff) | |
| download | rust-12f171b052c07d60760cabbc109d15188ce285ec.tar.gz rust-12f171b052c07d60760cabbc109d15188ce285ec.zip | |
Rollup merge of #30485 - sfackler:panic-handler, r=alexcrichton
r? @alexcrichton
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/panic.rs | 2 | ||||
| -rw-r--r-- | src/libstd/panicking.rs | 156 |
2 files changed, 150 insertions, 8 deletions
diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 6e4ba337b08..1550d55d177 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -21,6 +21,8 @@ use sync::{Arc, Mutex, RwLock}; use sys_common::unwind; use thread::Result; +pub use panicking::{take_handler, set_handler, PanicInfo, Location}; + /// A marker trait which represents "panic safe" types in Rust. /// /// This trait is implemented by default for many types and behaves similarly in diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 2b2af350c99..3f9a1c30ef4 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -15,10 +15,12 @@ use any::Any; use cell::Cell; use cell::RefCell; use intrinsics; +use sync::StaticRwLock; use sys::stdio::Stderr; use sys_common::backtrace; use sys_common::thread_info; use sys_common::util; +use thread; thread_local! { pub static PANIC_COUNT: Cell<usize> = Cell::new(0) } @@ -28,11 +30,138 @@ thread_local! { } } -fn log_panic(obj: &(Any+Send), file: &'static str, line: u32, - log_backtrace: bool) { - let msg = match obj.downcast_ref::<&'static str>() { +#[derive(Copy, Clone)] +enum Handler { + Default, + Custom(*mut (Fn(&PanicInfo) + 'static + Sync + Send)), +} + +static HANDLER_LOCK: StaticRwLock = StaticRwLock::new(); +static mut HANDLER: Handler = Handler::Default; + +/// Registers a custom panic handler, replacing any that was previously +/// registered. +/// +/// The panic handler is invoked when a thread panics, but before it begins +/// unwinding the stack. The default handler prints a message to standard error +/// and generates a backtrace if requested, but this behavior can be customized +/// with the `set_handler` and `take_handler` functions. +/// +/// The handler is provided with a `PanicInfo` struct which contains information +/// about the origin of the panic, including the payload passed to `panic!` and +/// the source code location from which the panic originated. +/// +/// The panic handler is a global resource. +/// +/// # Panics +/// +/// Panics if called from a panicking thread. +#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +pub fn set_handler<F>(handler: F) where F: Fn(&PanicInfo) + 'static + Sync + Send { + if thread::panicking() { + panic!("cannot modify the panic handler from a panicking thread"); + } + + let handler = Box::new(handler); + unsafe { + let lock = HANDLER_LOCK.write(); + let old_handler = HANDLER; + HANDLER = Handler::Custom(Box::into_raw(handler)); + drop(lock); + + if let Handler::Custom(ptr) = old_handler { + Box::from_raw(ptr); + } + } +} + +/// Unregisters the current panic handler, returning it. +/// +/// If no custom handler is registered, the default handler will be returned. +/// +/// # Panics +/// +/// Panics if called from a panicking thread. +#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +pub fn take_handler() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> { + if thread::panicking() { + panic!("cannot modify the panic handler from a panicking thread"); + } + + unsafe { + let lock = HANDLER_LOCK.write(); + let handler = HANDLER; + HANDLER = Handler::Default; + drop(lock); + + match handler { + Handler::Default => Box::new(default_handler), + Handler::Custom(ptr) => {Box::from_raw(ptr)} // FIXME #30530 + } + } +} + +/// A struct providing information about a panic. +#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +pub struct PanicInfo<'a> { + payload: &'a (Any + Send), + location: Location<'a>, +} + +impl<'a> PanicInfo<'a> { + /// Returns the payload associated with the panic. + /// + /// This will commonly, but not always, be a `&'static str` or `String`. + #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + pub fn payload(&self) -> &(Any + Send) { + self.payload + } + + /// Returns information about the location from which the panic originated, + /// if available. + /// + /// This method will currently always return `Some`, but this may change + /// in future versions. + #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + pub fn location(&self) -> Option<&Location> { + Some(&self.location) + } +} + +/// A struct containing information about the location of a panic. +#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +pub struct Location<'a> { + file: &'a str, + line: u32, +} + +impl<'a> Location<'a> { + /// Returns the name of the source file from which the panic originated. + #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + pub fn file(&self) -> &str { + self.file + } + + /// Returns the line number from which the panic originated. + #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + pub fn line(&self) -> u32 { + self.line + } +} + +fn default_handler(info: &PanicInfo) { + let panics = PANIC_COUNT.with(|s| s.get()); + + // If this is a double panic, make sure that we print a backtrace + // for this panic. Otherwise only print it if logging is enabled. + let log_backtrace = panics >= 2 || backtrace::log_enabled(); + + let file = info.location.file; + let line = info.location.line; + + let msg = match info.payload.downcast_ref::<&'static str>() { Some(s) => *s, - None => match obj.downcast_ref::<String>() { + None => match info.payload.downcast_ref::<String>() { Some(s) => &s[..], None => "Box<Any>", } @@ -81,10 +210,21 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) { unsafe { intrinsics::abort() } } - // If this is a double panic, make sure that we print a backtrace - // for this panic. Otherwise only print it if logging is enabled. - let log_backtrace = panics >= 2 || backtrace::log_enabled(); - log_panic(obj, file, line, log_backtrace); + let info = PanicInfo { + payload: obj, + location: Location { + file: file, + line: line, + }, + }; + + unsafe { + let _lock = HANDLER_LOCK.read(); + match HANDLER { + Handler::Default => default_handler(&info), + Handler::Custom(ptr) => (*ptr)(&info), + } + } if panics >= 2 { // If a thread panics while it's already unwinding then we |
