]> git.proxmox.com Git - rustc.git/blame - src/libstd/panicking.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / libstd / panicking.rs
CommitLineData
1a4d82fc
JJ
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.
4//
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.
10
1a4d82fc 11use prelude::v1::*;
c34b1796 12use io::prelude::*;
1a4d82fc
JJ
13
14use any::Any;
b039eaaf 15use cell::Cell;
1a4d82fc 16use cell::RefCell;
b039eaaf 17use intrinsics;
9cc50fc6 18use sync::StaticRwLock;
7453a54e 19use sync::atomic::{AtomicBool, Ordering};
c34b1796 20use sys::stdio::Stderr;
e9174d1e 21use sys_common::backtrace;
d9579d0f 22use sys_common::thread_info;
b039eaaf 23use sys_common::util;
9cc50fc6 24use thread;
b039eaaf
SL
25
26thread_local! { pub static PANIC_COUNT: Cell<usize> = Cell::new(0) }
1a4d82fc 27
1a4d82fc 28thread_local! {
c34b1796 29 pub static LOCAL_STDERR: RefCell<Option<Box<Write + Send>>> = {
1a4d82fc
JJ
30 RefCell::new(None)
31 }
32}
33
9cc50fc6 34#[derive(Copy, Clone)]
54a0048b 35enum Hook {
9cc50fc6
SL
36 Default,
37 Custom(*mut (Fn(&PanicInfo) + 'static + Sync + Send)),
38}
39
54a0048b
SL
40static HOOK_LOCK: StaticRwLock = StaticRwLock::new();
41static mut HOOK: Hook = Hook::Default;
7453a54e 42static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
9cc50fc6 43
54a0048b 44/// Registers a custom panic hook, replacing any that was previously registered.
9cc50fc6 45///
54a0048b
SL
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
9cc50fc6 48/// and generates a backtrace if requested, but this behavior can be customized
54a0048b 49/// with the `set_hook` and `take_hook` functions.
9cc50fc6 50///
54a0048b 51/// The hook is provided with a `PanicInfo` struct which contains information
9cc50fc6
SL
52/// about the origin of the panic, including the payload passed to `panic!` and
53/// the source code location from which the panic originated.
54///
54a0048b 55/// The panic hook is a global resource.
9cc50fc6
SL
56///
57/// # Panics
58///
59/// Panics if called from a panicking thread.
60#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
54a0048b 61pub fn set_hook(hook: Box<Fn(&PanicInfo) + 'static + Sync + Send>) {
9cc50fc6 62 if thread::panicking() {
54a0048b 63 panic!("cannot modify the panic hook from a panicking thread");
9cc50fc6
SL
64 }
65
9cc50fc6 66 unsafe {
54a0048b
SL
67 let lock = HOOK_LOCK.write();
68 let old_hook = HOOK;
69 HOOK = Hook::Custom(Box::into_raw(hook));
9cc50fc6
SL
70 drop(lock);
71
54a0048b 72 if let Hook::Custom(ptr) = old_hook {
9cc50fc6
SL
73 Box::from_raw(ptr);
74 }
75 }
76}
77
54a0048b 78/// Unregisters the current panic hook, returning it.
9cc50fc6 79///
54a0048b 80/// If no custom hook is registered, the default hook will be returned.
9cc50fc6
SL
81///
82/// # Panics
83///
84/// Panics if called from a panicking thread.
85#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
54a0048b 86pub fn take_hook() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
9cc50fc6 87 if thread::panicking() {
54a0048b 88 panic!("cannot modify the panic hook from a panicking thread");
9cc50fc6
SL
89 }
90
91 unsafe {
54a0048b
SL
92 let lock = HOOK_LOCK.write();
93 let hook = HOOK;
94 HOOK = Hook::Default;
9cc50fc6
SL
95 drop(lock);
96
54a0048b
SL
97 match hook {
98 Hook::Default => Box::new(default_hook),
99 Hook::Custom(ptr) => {Box::from_raw(ptr)} // FIXME #30530
9cc50fc6
SL
100 }
101 }
102}
103
104/// A struct providing information about a panic.
105#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
106pub struct PanicInfo<'a> {
107 payload: &'a (Any + Send),
108 location: Location<'a>,
109}
110
111impl<'a> PanicInfo<'a> {
112 /// Returns the payload associated with the panic.
113 ///
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) {
117 self.payload
118 }
119
120 /// Returns information about the location from which the panic originated,
121 /// if available.
122 ///
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> {
127 Some(&self.location)
128 }
129}
130
131/// A struct containing information about the location of a panic.
132#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
133pub struct Location<'a> {
134 file: &'a str,
135 line: u32,
136}
137
138impl<'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 {
142 self.file
143 }
144
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 {
148 self.line
149 }
150}
151
54a0048b 152fn default_hook(info: &PanicInfo) {
9cc50fc6
SL
153 let panics = PANIC_COUNT.with(|s| s.get());
154
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();
158
159 let file = info.location.file;
160 let line = info.location.line;
161
162 let msg = match info.payload.downcast_ref::<&'static str>() {
1a4d82fc 163 Some(s) => *s,
9cc50fc6 164 None => match info.payload.downcast_ref::<String>() {
85aaf69f 165 Some(s) => &s[..],
1a4d82fc
JJ
166 None => "Box<Any>",
167 }
168 };
62682a34 169 let mut err = Stderr::new().ok();
d9579d0f
AL
170 let thread = thread_info::current_thread();
171 let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
b039eaaf
SL
172
173 let write = |err: &mut ::io::Write| {
174 let _ = writeln!(err, "thread '{}' panicked at '{}', {}:{}",
175 name, msg, file, line);
7453a54e 176
b039eaaf
SL
177 if log_backtrace {
178 let _ = backtrace::write(err);
7453a54e
SL
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.");
b039eaaf
SL
181 }
182 };
183
1a4d82fc 184 let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take());
62682a34
SL
185 match (prev, err.as_mut()) {
186 (Some(mut stderr), _) => {
b039eaaf 187 write(&mut *stderr);
1a4d82fc
JJ
188 let mut s = Some(stderr);
189 LOCAL_STDERR.with(|slot| {
190 *slot.borrow_mut() = s.take();
191 });
192 }
b039eaaf 193 (None, Some(ref mut err)) => { write(err) }
62682a34 194 _ => {}
1a4d82fc 195 }
b039eaaf 196}
1a4d82fc 197
b039eaaf
SL
198pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
199 let panics = PANIC_COUNT.with(|s| {
200 let count = s.get() + 1;
201 s.set(count);
202 count
203 });
204
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.
210 if panics >= 3 {
211 util::dumb_print(format_args!("thread panicked while processing \
9cc50fc6 212 panic. aborting.\n"));
b039eaaf
SL
213 unsafe { intrinsics::abort() }
214 }
215
9cc50fc6
SL
216 let info = PanicInfo {
217 payload: obj,
218 location: Location {
219 file: file,
220 line: line,
221 },
222 };
223
224 unsafe {
54a0048b
SL
225 let _lock = HOOK_LOCK.read();
226 match HOOK {
227 Hook::Default => default_hook(&info),
228 Hook::Custom(ptr) => (*ptr)(&info),
9cc50fc6
SL
229 }
230 }
b039eaaf
SL
231
232 if panics >= 2 {
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. \
9cc50fc6 238 aborting.\n"));
b039eaaf 239 unsafe { intrinsics::abort() }
1a4d82fc
JJ
240 }
241}