]> git.proxmox.com Git - rustc.git/blob - src/libstd/panicking.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / libstd / panicking.rs
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
11 use prelude::v1::*;
12 use io::prelude::*;
13
14 use any::Any;
15 use cell::Cell;
16 use cell::RefCell;
17 use intrinsics;
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;
23 use sys_common::util;
24 use thread;
25
26 thread_local! { pub static PANIC_COUNT: Cell<usize> = Cell::new(0) }
27
28 thread_local! {
29 pub static LOCAL_STDERR: RefCell<Option<Box<Write + Send>>> = {
30 RefCell::new(None)
31 }
32 }
33
34 #[derive(Copy, Clone)]
35 enum Hook {
36 Default,
37 Custom(*mut (Fn(&PanicInfo) + 'static + Sync + Send)),
38 }
39
40 static HOOK_LOCK: StaticRwLock = StaticRwLock::new();
41 static mut HOOK: Hook = Hook::Default;
42 static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
43
44 /// Registers a custom panic hook, replacing any that was previously registered.
45 ///
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.
50 ///
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.
54 ///
55 /// The panic hook is a global resource.
56 ///
57 /// # Panics
58 ///
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");
64 }
65
66 unsafe {
67 let lock = HOOK_LOCK.write();
68 let old_hook = HOOK;
69 HOOK = Hook::Custom(Box::into_raw(hook));
70 drop(lock);
71
72 if let Hook::Custom(ptr) = old_hook {
73 Box::from_raw(ptr);
74 }
75 }
76 }
77
78 /// Unregisters the current panic hook, returning it.
79 ///
80 /// If no custom hook is registered, the default hook will be returned.
81 ///
82 /// # Panics
83 ///
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");
89 }
90
91 unsafe {
92 let lock = HOOK_LOCK.write();
93 let hook = HOOK;
94 HOOK = Hook::Default;
95 drop(lock);
96
97 match hook {
98 Hook::Default => Box::new(default_hook),
99 Hook::Custom(ptr) => {Box::from_raw(ptr)} // FIXME #30530
100 }
101 }
102 }
103
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>,
109 }
110
111 impl<'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")]
133 pub struct Location<'a> {
134 file: &'a str,
135 line: u32,
136 }
137
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 {
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
152 fn default_hook(info: &PanicInfo) {
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>() {
163 Some(s) => *s,
164 None => match info.payload.downcast_ref::<String>() {
165 Some(s) => &s[..],
166 None => "Box<Any>",
167 }
168 };
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>");
172
173 let write = |err: &mut ::io::Write| {
174 let _ = writeln!(err, "thread '{}' panicked at '{}', {}:{}",
175 name, msg, file, line);
176
177 if log_backtrace {
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.");
181 }
182 };
183
184 let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take());
185 match (prev, err.as_mut()) {
186 (Some(mut stderr), _) => {
187 write(&mut *stderr);
188 let mut s = Some(stderr);
189 LOCAL_STDERR.with(|slot| {
190 *slot.borrow_mut() = s.take();
191 });
192 }
193 (None, Some(ref mut err)) => { write(err) }
194 _ => {}
195 }
196 }
197
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;
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 \
212 panic. aborting.\n"));
213 unsafe { intrinsics::abort() }
214 }
215
216 let info = PanicInfo {
217 payload: obj,
218 location: Location {
219 file: file,
220 line: line,
221 },
222 };
223
224 unsafe {
225 let _lock = HOOK_LOCK.read();
226 match HOOK {
227 Hook::Default => default_hook(&info),
228 Hook::Custom(ptr) => (*ptr)(&info),
229 }
230 }
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. \
238 aborting.\n"));
239 unsafe { intrinsics::abort() }
240 }
241 }