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.
11 //! Implementation of various bits and pieces of the `panic!` macro and
12 //! associated runtime pieces.
14 //! Specifically, this module contains the implementation of:
17 //! * Executing a panic up to doing the actual implementation
18 //! * Shims around "try"
24 use core
::panic
::{PanicInfo, Location}
;
30 use sys
::stdio
::Stderr
;
31 use sys_common
::rwlock
::RWLock
;
32 use sys_common
::thread_info
;
37 pub static LOCAL_STDERR
: RefCell
<Option
<Box
<Write
+ Send
>>> = {
42 // Binary interface to the panic runtime that the standard library depends on.
44 // The standard library is tagged with `#![needs_panic_runtime]` (introduced in
45 // RFC 1513) to indicate that it requires some other crate tagged with
46 // `#![panic_runtime]` to exist somewhere. Each panic runtime is intended to
47 // implement these symbols (with the same signatures) so we can get matched up
50 // One day this may look a little less ad-hoc with the compiler helping out to
51 // hook up these functions, but it is not this day!
52 #[allow(improper_ctypes)]
54 fn __rust_maybe_catch_panic(f
: fn(*mut u8),
57 vtable_ptr
: *mut usize) -> u32;
58 #[cfg_attr(stage0, unwind)]
59 #[cfg_attr(not(stage0), unwind(allowed))]
60 fn __rust_start_panic(data
: usize, vtable
: usize) -> u32;
63 #[derive(Copy, Clone)]
66 Custom(*mut (Fn(&PanicInfo
) + '
static + Sync
+ Send
)),
69 static HOOK_LOCK
: RWLock
= RWLock
::new();
70 static mut HOOK
: Hook
= Hook
::Default
;
72 /// Registers a custom panic hook, replacing any that was previously registered.
74 /// The panic hook is invoked when a thread panics, but before the panic runtime
75 /// is invoked. As such, the hook will run with both the aborting and unwinding
76 /// runtimes. The default hook prints a message to standard error and generates
77 /// a backtrace if requested, but this behavior can be customized with the
78 /// `set_hook` and `take_hook` functions.
80 /// The hook is provided with a `PanicInfo` struct which contains information
81 /// about the origin of the panic, including the payload passed to `panic!` and
82 /// the source code location from which the panic originated.
84 /// The panic hook is a global resource.
88 /// Panics if called from a panicking thread.
92 /// The following will print "Custom panic hook":
97 /// panic::set_hook(Box::new(|_| {
98 /// println!("Custom panic hook");
101 /// panic!("Normal panic");
103 #[stable(feature = "panic_hooks", since = "1.10.0")]
104 pub fn set_hook(hook
: Box
<Fn(&PanicInfo
) + '
static + Sync
+ Send
>) {
105 if thread
::panicking() {
106 panic
!("cannot modify the panic hook from a panicking thread");
112 HOOK
= Hook
::Custom(Box
::into_raw(hook
));
113 HOOK_LOCK
.write_unlock();
115 if let Hook
::Custom(ptr
) = old_hook
{
121 /// Unregisters the current panic hook, returning it.
123 /// If no custom hook is registered, the default hook will be returned.
127 /// Panics if called from a panicking thread.
131 /// The following will print "Normal panic":
136 /// panic::set_hook(Box::new(|_| {
137 /// println!("Custom panic hook");
140 /// let _ = panic::take_hook();
142 /// panic!("Normal panic");
144 #[stable(feature = "panic_hooks", since = "1.10.0")]
145 pub fn take_hook() -> Box
<Fn(&PanicInfo
) + '
static + Sync
+ Send
> {
146 if thread
::panicking() {
147 panic
!("cannot modify the panic hook from a panicking thread");
153 HOOK
= Hook
::Default
;
154 HOOK_LOCK
.write_unlock();
157 Hook
::Default
=> Box
::new(default_hook
),
158 Hook
::Custom(ptr
) => Box
::from_raw(ptr
),
163 fn default_hook(info
: &PanicInfo
) {
164 #[cfg(feature = "backtrace")]
165 use sys_common
::backtrace
;
167 // If this is a double panic, make sure that we print a backtrace
168 // for this panic. Otherwise only print it if logging is enabled.
169 #[cfg(feature = "backtrace")]
170 let log_backtrace
= {
171 let panics
= update_panic_count(0);
174 Some(backtrace
::PrintFormat
::Full
)
176 backtrace
::log_enabled()
180 let location
= info
.location().unwrap(); // The current implementation always returns Some
181 let file
= location
.file();
182 let line
= location
.line();
183 let col
= location
.column();
185 let msg
= match info
.payload().downcast_ref
::<&'
static str>() {
187 None
=> match info
.payload().downcast_ref
::<String
>() {
192 let mut err
= Stderr
::new().ok();
193 let thread
= thread_info
::current_thread();
194 let name
= thread
.as_ref().and_then(|t
| t
.name()).unwrap_or("<unnamed>");
196 let write
= |err
: &mut ::io
::Write
| {
197 let _
= writeln
!(err
, "thread '{}' panicked at '{}', {}:{}:{}",
198 name
, msg
, file
, line
, col
);
200 #[cfg(feature = "backtrace")]
202 use sync
::atomic
::{AtomicBool, Ordering}
;
204 static FIRST_PANIC
: AtomicBool
= AtomicBool
::new(true);
206 if let Some(format
) = log_backtrace
{
207 let _
= backtrace
::print(err
, format
);
208 } else if FIRST_PANIC
.compare_and_swap(true, false, Ordering
::SeqCst
) {
209 let _
= writeln
!(err
, "note: Run with `RUST_BACKTRACE=1` for a backtrace.");
214 let prev
= LOCAL_STDERR
.with(|s
| s
.borrow_mut().take());
215 match (prev
, err
.as_mut()) {
216 (Some(mut stderr
), _
) => {
218 let mut s
= Some(stderr
);
219 LOCAL_STDERR
.with(|slot
| {
220 *slot
.borrow_mut() = s
.take();
223 (None
, Some(ref mut err
)) => { write(err) }
231 #[unstable(feature = "update_panic_count", issue = "0")]
232 pub fn update_panic_count(amt
: isize) -> usize {
234 thread_local
! { static PANIC_COUNT: Cell<usize> = Cell::new(0) }
236 PANIC_COUNT
.with(|c
| {
237 let next
= (c
.get() as isize + amt
) as usize;
244 pub use realstd
::rt
::update_panic_count
;
246 /// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
247 pub unsafe fn try
<R
, F
: FnOnce() -> R
>(f
: F
) -> Result
<R
, Box
<Any
+ Send
>> {
248 #[allow(unions_with_drop_fields)]
254 // We do some sketchy operations with ownership here for the sake of
255 // performance. We can only pass pointers down to
256 // `__rust_maybe_catch_panic` (can't pass objects by value), so we do all
257 // the ownership tracking here manually using a union.
259 // We go through a transition where:
261 // * First, we set the data to be the closure that we're going to call.
262 // * When we make the function call, the `do_call` function below, we take
263 // ownership of the function pointer. At this point the `Data` union is
264 // entirely uninitialized.
265 // * If the closure successfully returns, we write the return value into the
266 // data's return slot. Note that `ptr::write` is used as it's overwriting
267 // uninitialized data.
268 // * Finally, when we come back out of the `__rust_maybe_catch_panic` we're
269 // in one of two states:
271 // 1. The closure didn't panic, in which case the return value was
272 // filled in. We move it out of `data` and return it.
273 // 2. The closure panicked, in which case the return value wasn't
274 // filled in. In this case the entire `data` union is invalid, so
275 // there is no need to drop anything.
277 // Once we stack all that together we should have the "most efficient'
278 // method of calling a catch panic whilst juggling ownership.
279 let mut any_data
= 0;
280 let mut any_vtable
= 0;
281 let mut data
= Data
{
285 let r
= __rust_maybe_catch_panic(do_call
::<F
, R
>,
286 &mut data
as *mut _
as *mut u8,
291 debug_assert
!(update_panic_count(0) == 0);
294 update_panic_count(-1);
295 debug_assert
!(update_panic_count(0) == 0);
296 Err(mem
::transmute(raw
::TraitObject
{
297 data
: any_data
as *mut _
,
298 vtable
: any_vtable
as *mut _
,
302 fn do_call
<F
: FnOnce() -> R
, R
>(data
: *mut u8) {
304 let data
= data
as *mut Data
<F
, R
>;
305 let f
= ptr
::read(&mut (*data
).f
);
306 ptr
::write(&mut (*data
).r
, f());
311 /// Determines whether the current thread is unwinding because of panic.
312 pub fn panicking() -> bool
{
313 update_panic_count(0) != 0
316 /// Entry point of panic from the libcore crate.
318 #[lang = "panic_fmt"]
319 #[cfg_attr(stage0, unwind)]
320 #[cfg_attr(not(stage0), unwind(allowed))]
321 pub extern fn rust_begin_panic(msg
: fmt
::Arguments
,
325 begin_panic_fmt(&msg
, &(file
, line
, col
))
328 /// The entry point for panicking with a formatted message.
330 /// This is designed to reduce the amount of code required at the call
331 /// site as much as possible (so that `panic!()` has as low an impact
332 /// on (e.g.) the inlining of other functions as possible), by moving
333 /// the actual formatting into this shared place.
334 #[unstable(feature = "libstd_sys_internals",
335 reason
= "used by the panic! macro",
337 #[inline(never)] #[cold]
338 pub fn begin_panic_fmt(msg
: &fmt
::Arguments
,
339 file_line_col
: &(&'
static str, u32, u32)) -> ! {
342 // We do two allocations here, unfortunately. But (a) they're
343 // required with the current scheme, and (b) we don't handle
344 // panic + OOM properly anyway (see comment in begin_panic
347 let mut s
= String
::new();
348 let _
= s
.write_fmt(*msg
);
349 rust_panic_with_hook(Box
::new(s
), Some(msg
), file_line_col
)
352 /// This is the entry point of panicking for panic!() and assert!().
353 #[unstable(feature = "libstd_sys_internals",
354 reason
= "used by the panic! macro",
356 #[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
357 pub fn begin_panic
<M
: Any
+ Send
>(msg
: M
, file_line_col
: &(&'
static str, u32, u32)) -> ! {
358 // Note that this should be the only allocation performed in this code path.
359 // Currently this means that panic!() on OOM will invoke this code path,
360 // but then again we're not really ready for panic on OOM anyway. If
361 // we do start doing this, then we should propagate this allocation to
362 // be performed in the parent of this thread instead of the thread that's
365 rust_panic_with_hook(Box
::new(msg
), None
, file_line_col
)
368 /// Executes the primary logic for a panic, including checking for recursive
369 /// panics and panic hooks.
371 /// This is the entry point or panics from libcore, formatted panics, and
372 /// `Box<Any>` panics. Here we'll verify that we're not panicking recursively,
373 /// run panic hooks, and then delegate to the actual implementation of panics.
376 fn rust_panic_with_hook(payload
: Box
<Any
+ Send
>,
377 message
: Option
<&fmt
::Arguments
>,
378 file_line_col
: &(&'
static str, u32, u32)) -> ! {
379 let (file
, line
, col
) = *file_line_col
;
381 let panics
= update_panic_count(1);
383 // If this is the third nested call (e.g. panics == 2, this is 0-indexed),
384 // the panic hook probably triggered the last panic, otherwise the
385 // double-panic check would have aborted the process. In this case abort the
386 // process real quickly as we don't want to try calling it again as it'll
387 // probably just panic again.
389 util
::dumb_print(format_args
!("thread panicked while processing \
390 panic. aborting.\n"));
391 unsafe { intrinsics::abort() }
395 let info
= PanicInfo
::internal_constructor(
398 Location
::internal_constructor(file
, line
, col
),
402 Hook
::Default
=> default_hook(&info
),
403 Hook
::Custom(ptr
) => (*ptr
)(&info
),
405 HOOK_LOCK
.read_unlock();
409 // If a thread panics while it's already unwinding then we
410 // have limited options. Currently our preference is to
411 // just abort. In the future we may consider resuming
412 // unwinding or otherwise exiting the thread cleanly.
413 util
::dumb_print(format_args
!("thread panicked while panicking. \
415 unsafe { intrinsics::abort() }
421 /// Shim around rust_panic. Called by resume_unwind.
422 pub fn update_count_then_panic(msg
: Box
<Any
+ Send
>) -> ! {
423 update_panic_count(1);
427 /// A private no-mangle function on which to slap yer breakpoints.
429 #[allow(private_no_mangle_fns)] // yes we get it, but we like breakpoints
430 pub fn rust_panic(msg
: Box
<Any
+ Send
>) -> ! {
432 let obj
= mem
::transmute
::<_
, raw
::TraitObject
>(msg
);
433 __rust_start_panic(obj
.data
as usize, obj
.vtable
as usize)
435 rtabort
!("failed to initiate panic, error {}", code
)