// 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 or the MIT license // , 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 sync::atomic::{AtomicBool, Ordering}; 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 = Cell::new(0) } thread_local! { pub static LOCAL_STDERR: RefCell>> = { 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; static FIRST_PANIC: AtomicBool = AtomicBool::new(true); /// 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(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 { 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::() { Some(s) => &s[..], None => "Box", } }; let mut err = Stderr::new().ok(); let thread = thread_info::current_thread(); let name = thread.as_ref().and_then(|t| t.name()).unwrap_or(""); let write = |err: &mut ::io::Write| { let _ = writeln!(err, "thread '{}' panicked at '{}', {}:{}", name, msg, file, line); if log_backtrace { let _ = backtrace::write(err); } else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) { let _ = writeln!(err, "note: Run with `RUST_BACKTRACE=1` for a backtrace."); } }; 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() } } }