| // Copyright 2014 The Rust Project Developers. See the COPYRIGHT |
| // file at the top-level directory of this distribution and at |
| // http://rust-lang.org/COPYRIGHT. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| use prelude::v1::*; |
| use io::prelude::*; |
| |
| 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) } |
| |
| thread_local! { |
| pub static LOCAL_STDERR: RefCell<Option<Box<Write + Send>>> = { |
| RefCell::new(None) |
| } |
| } |
| |
| #[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 info.payload.downcast_ref::<String>() { |
| Some(s) => &s[..], |
| None => "Box<Any>", |
| } |
| }; |
| let mut err = Stderr::new().ok(); |
| let thread = thread_info::current_thread(); |
| let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>"); |
| |
| let write = |err: &mut ::io::Write| { |
| let _ = writeln!(err, "thread '{}' panicked at '{}', {}:{}", |
| name, msg, file, line); |
| if log_backtrace { |
| let _ = backtrace::write(err); |
| } |
| }; |
| |
| let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take()); |
| match (prev, err.as_mut()) { |
| (Some(mut stderr), _) => { |
| write(&mut *stderr); |
| let mut s = Some(stderr); |
| LOCAL_STDERR.with(|slot| { |
| *slot.borrow_mut() = s.take(); |
| }); |
| } |
| (None, Some(ref mut err)) => { write(err) } |
| _ => {} |
| } |
| } |
| |
| pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) { |
| let panics = PANIC_COUNT.with(|s| { |
| let count = s.get() + 1; |
| s.set(count); |
| count |
| }); |
| |
| // If this is the third nested call, on_panic triggered the last panic, |
| // otherwise the double-panic check would have aborted the process. |
| // Even if it is likely that on_panic was unable to log the backtrace, |
| // abort immediately to avoid infinite recursion, so that attaching a |
| // debugger provides a useable stacktrace. |
| if panics >= 3 { |
| util::dumb_print(format_args!("thread panicked while processing \ |
| panic. aborting.\n")); |
| unsafe { intrinsics::abort() } |
| } |
| |
| 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 |
| // have limited options. Currently our preference is to |
| // just abort. In the future we may consider resuming |
| // unwinding or otherwise exiting the thread cleanly. |
| util::dumb_print(format_args!("thread panicked while panicking. \ |
| aborting.\n")); |
| unsafe { intrinsics::abort() } |
| } |
| } |