Implement custom panic handlers
This commit is contained in:
parent
27d551142f
commit
f1148a540a
@ -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
|
||||
|
@ -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
|
||||
|
22
src/test/run-fail/panic-set-handler.rs
Normal file
22
src/test/run-fail/panic-set-handler.rs
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// error-pattern:greetings from the panic handler
|
||||
|
||||
#![feature(std_panic, panic_handler)]
|
||||
use std::panic;
|
||||
use std::io::{self, Write};
|
||||
|
||||
fn main() {
|
||||
panic::set_handler(|i| {
|
||||
write!(io::stderr(), "greetings from the panic handler");
|
||||
});
|
||||
panic!("foobar");
|
||||
}
|
23
src/test/run-fail/panic-set-unset-handler.rs
Normal file
23
src/test/run-fail/panic-set-unset-handler.rs
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// error-pattern:thread '<main>' panicked at 'foobar'
|
||||
|
||||
#![feature(std_panic, panic_handler)]
|
||||
use std::panic;
|
||||
use std::io::{self, Write};
|
||||
|
||||
fn main() {
|
||||
panic::set_handler(|i| {
|
||||
write!(io::stderr(), "greetings from the panic handler");
|
||||
});
|
||||
panic::take_handler();
|
||||
panic!("foobar");
|
||||
}
|
19
src/test/run-fail/panic-take-handler-nop.rs
Normal file
19
src/test/run-fail/panic-take-handler-nop.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// error-pattern:thread '<main>' panicked at 'foobar'
|
||||
|
||||
#![feature(std_panic, panic_handler)]
|
||||
use std::panic;
|
||||
|
||||
fn main() {
|
||||
panic::take_handler();
|
||||
panic!("foobar");
|
||||
}
|
33
src/test/run-pass/panic-handler-chain.rs
Normal file
33
src/test/run-pass/panic-handler-chain.rs
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2015 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.
|
||||
#![feature(panic_handler, const_fn, std_panic)]
|
||||
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::panic;
|
||||
use std::thread;
|
||||
|
||||
static A: AtomicUsize = AtomicUsize::new(0);
|
||||
static B: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
fn main() {
|
||||
panic::set_handler(|_| { A.fetch_add(1, Ordering::SeqCst); });
|
||||
let handler = panic::take_handler();
|
||||
panic::set_handler(move |info| {
|
||||
B.fetch_add(1, Ordering::SeqCst);
|
||||
handler(info);
|
||||
});
|
||||
|
||||
let _ = thread::spawn(|| {
|
||||
panic!();
|
||||
}).join();
|
||||
|
||||
assert_eq!(1, A.load(Ordering::SeqCst));
|
||||
assert_eq!(1, B.load(Ordering::SeqCst));
|
||||
}
|
57
src/test/run-pass/panic-handler-flail-wildly.rs
Normal file
57
src/test/run-pass/panic-handler-flail-wildly.rs
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2015 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.
|
||||
#![feature(panic_handler, std_panic)]
|
||||
|
||||
use std::panic;
|
||||
use std::thread;
|
||||
|
||||
fn a() {
|
||||
panic::set_handler(|_| println!("hello yes this is a"));
|
||||
panic::take_handler();
|
||||
panic::set_handler(|_| println!("hello yes this is a part 2"));
|
||||
panic::take_handler();
|
||||
}
|
||||
|
||||
fn b() {
|
||||
panic::take_handler();
|
||||
panic::take_handler();
|
||||
panic::take_handler();
|
||||
panic::take_handler();
|
||||
panic::take_handler();
|
||||
panic!();
|
||||
}
|
||||
|
||||
fn c() {
|
||||
panic::set_handler(|_| ());
|
||||
panic::set_handler(|_| ());
|
||||
panic::set_handler(|_| ());
|
||||
panic::set_handler(|_| ());
|
||||
panic::set_handler(|_| ());
|
||||
panic::set_handler(|_| ());
|
||||
panic!();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
for _ in 0..10 {
|
||||
let mut handles = vec![];
|
||||
for _ in 0..10 {
|
||||
handles.push(thread::spawn(a));
|
||||
}
|
||||
for _ in 0..10 {
|
||||
handles.push(thread::spawn(b));
|
||||
}
|
||||
for _ in 0..10 {
|
||||
handles.push(thread::spawn(c));
|
||||
}
|
||||
for handle in handles {
|
||||
let _ = handle.join();
|
||||
}
|
||||
}
|
||||
}
|
27
src/test/run-pass/panic-handler-set-twice.rs
Normal file
27
src/test/run-pass/panic-handler-set-twice.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2015 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.
|
||||
#![feature(panic_handler, const_fn, std_panic)]
|
||||
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::panic;
|
||||
use std::thread;
|
||||
|
||||
static A: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
fn main() {
|
||||
panic::set_handler(|_| ());
|
||||
panic::set_handler(|info| { A.fetch_add(1, Ordering::SeqCst); });
|
||||
|
||||
let _ = thread::spawn(|| {
|
||||
panic!();
|
||||
}).join();
|
||||
|
||||
assert_eq!(1, A.load(Ordering::SeqCst));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user