1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
18 use sync
::StaticRwLock
;
19 use sync
::atomic
::{AtomicBool, Ordering}
;
20 use sys
::stdio
::Stderr
;
21 use sys_common
::backtrace
;
22 use sys_common
::thread_info
;
26 thread_local
! { pub static PANIC_COUNT: Cell<usize> = Cell::new(0) }
29 pub static LOCAL_STDERR
: RefCell
<Option
<Box
<Write
+ Send
>>> = {
34 #[derive(Copy, Clone)]
37 Custom(*mut (Fn(&PanicInfo
) + '
static + Sync
+ Send
)),
40 static HOOK_LOCK
: StaticRwLock
= StaticRwLock
::new();
41 static mut HOOK
: Hook
= Hook
::Default
;
42 static FIRST_PANIC
: AtomicBool
= AtomicBool
::new(true);
44 /// Registers a custom panic hook, replacing any that was previously registered.
46 /// The panic hook is invoked when a thread panics, but before it begins
47 /// unwinding the stack. The default hook prints a message to standard error
48 /// and generates a backtrace if requested, but this behavior can be customized
49 /// with the `set_hook` and `take_hook` functions.
51 /// The hook is provided with a `PanicInfo` struct which contains information
52 /// about the origin of the panic, including the payload passed to `panic!` and
53 /// the source code location from which the panic originated.
55 /// The panic hook is a global resource.
59 /// Panics if called from a panicking thread.
60 #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
61 pub fn set_hook(hook
: Box
<Fn(&PanicInfo
) + '
static + Sync
+ Send
>) {
62 if thread
::panicking() {
63 panic
!("cannot modify the panic hook from a panicking thread");
67 let lock
= HOOK_LOCK
.write();
69 HOOK
= Hook
::Custom(Box
::into_raw(hook
));
72 if let Hook
::Custom(ptr
) = old_hook
{
78 /// Unregisters the current panic hook, returning it.
80 /// If no custom hook is registered, the default hook will be returned.
84 /// Panics if called from a panicking thread.
85 #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
86 pub fn take_hook() -> Box
<Fn(&PanicInfo
) + '
static + Sync
+ Send
> {
87 if thread
::panicking() {
88 panic
!("cannot modify the panic hook from a panicking thread");
92 let lock
= HOOK_LOCK
.write();
98 Hook
::Default
=> Box
::new(default_hook
),
99 Hook
::Custom(ptr
) => {Box::from_raw(ptr)}
// FIXME #30530
104 /// A struct providing information about a panic.
105 #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
106 pub struct PanicInfo
<'a
> {
107 payload
: &'
a (Any
+ Send
),
108 location
: Location
<'a
>,
111 impl<'a
> PanicInfo
<'a
> {
112 /// Returns the payload associated with the panic.
114 /// This will commonly, but not always, be a `&'static str` or `String`.
115 #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
116 pub fn payload(&self) -> &(Any
+ Send
) {
120 /// Returns information about the location from which the panic originated,
123 /// This method will currently always return `Some`, but this may change
124 /// in future versions.
125 #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
126 pub fn location(&self) -> Option
<&Location
> {
131 /// A struct containing information about the location of a panic.
132 #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
133 pub struct Location
<'a
> {
138 impl<'a
> Location
<'a
> {
139 /// Returns the name of the source file from which the panic originated.
140 #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
141 pub fn file(&self) -> &str {
145 /// Returns the line number from which the panic originated.
146 #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
147 pub fn line(&self) -> u32 {
152 fn default_hook(info
: &PanicInfo
) {
153 let panics
= PANIC_COUNT
.with(|s
| s
.get());
155 // If this is a double panic, make sure that we print a backtrace
156 // for this panic. Otherwise only print it if logging is enabled.
157 let log_backtrace
= panics
>= 2 || backtrace
::log_enabled();
159 let file
= info
.location
.file
;
160 let line
= info
.location
.line
;
162 let msg
= match info
.payload
.downcast_ref
::<&'
static str>() {
164 None
=> match info
.payload
.downcast_ref
::<String
>() {
169 let mut err
= Stderr
::new().ok();
170 let thread
= thread_info
::current_thread();
171 let name
= thread
.as_ref().and_then(|t
| t
.name()).unwrap_or("<unnamed>");
173 let write
= |err
: &mut ::io
::Write
| {
174 let _
= writeln
!(err
, "thread '{}' panicked at '{}', {}:{}",
175 name
, msg
, file
, line
);
178 let _
= backtrace
::write(err
);
179 } else if FIRST_PANIC
.compare_and_swap(true, false, Ordering
::SeqCst
) {
180 let _
= writeln
!(err
, "note: Run with `RUST_BACKTRACE=1` for a backtrace.");
184 let prev
= LOCAL_STDERR
.with(|s
| s
.borrow_mut().take());
185 match (prev
, err
.as_mut()) {
186 (Some(mut stderr
), _
) => {
188 let mut s
= Some(stderr
);
189 LOCAL_STDERR
.with(|slot
| {
190 *slot
.borrow_mut() = s
.take();
193 (None
, Some(ref mut err
)) => { write(err) }
198 pub fn on_panic(obj
: &(Any
+Send
), file
: &'
static str, line
: u32) {
199 let panics
= PANIC_COUNT
.with(|s
| {
200 let count
= s
.get() + 1;
205 // If this is the third nested call, on_panic triggered the last panic,
206 // otherwise the double-panic check would have aborted the process.
207 // Even if it is likely that on_panic was unable to log the backtrace,
208 // abort immediately to avoid infinite recursion, so that attaching a
209 // debugger provides a useable stacktrace.
211 util
::dumb_print(format_args
!("thread panicked while processing \
212 panic. aborting.\n"));
213 unsafe { intrinsics::abort() }
216 let info
= PanicInfo
{
225 let _lock
= HOOK_LOCK
.read();
227 Hook
::Default
=> default_hook(&info
),
228 Hook
::Custom(ptr
) => (*ptr
)(&info
),
233 // If a thread panics while it's already unwinding then we
234 // have limited options. Currently our preference is to
235 // just abort. In the future we may consider resuming
236 // unwinding or otherwise exiting the thread cleanly.
237 util
::dumb_print(format_args
!("thread panicked while panicking. \
239 unsafe { intrinsics::abort() }