1 //! Implementation of various bits and pieces of the `panic!` macro and
2 //! associated runtime pieces.
4 //! Specifically, this module contains the implementation of:
7 //! * Executing a panic up to doing the actual implementation
8 //! * Shims around "try"
10 use core
::panic
::BoxMeUp
;
16 use core
::panic
::{PanicInfo, Location}
;
22 use sys
::stdio
::panic_output
;
23 use sys_common
::rwlock
::RWLock
;
24 use sys_common
::thread_info
;
29 pub static LOCAL_STDERR
: RefCell
<Option
<Box
<dyn Write
+ Send
>>> = {
34 // Binary interface to the panic runtime that the standard library depends on.
36 // The standard library is tagged with `#![needs_panic_runtime]` (introduced in
37 // RFC 1513) to indicate that it requires some other crate tagged with
38 // `#![panic_runtime]` to exist somewhere. Each panic runtime is intended to
39 // implement these symbols (with the same signatures) so we can get matched up
42 // One day this may look a little less ad-hoc with the compiler helping out to
43 // hook up these functions, but it is not this day!
44 #[allow(improper_ctypes)]
46 fn __rust_maybe_catch_panic(f
: fn(*mut u8),
49 vtable_ptr
: *mut usize) -> u32;
51 fn __rust_start_panic(payload
: usize) -> u32;
54 #[derive(Copy, Clone)]
57 Custom(*mut (dyn Fn(&PanicInfo
) + '
static + Sync
+ Send
)),
60 static HOOK_LOCK
: RWLock
= RWLock
::new();
61 static mut HOOK
: Hook
= Hook
::Default
;
63 /// Registers a custom panic hook, replacing any that was previously registered.
65 /// The panic hook is invoked when a thread panics, but before the panic runtime
66 /// is invoked. As such, the hook will run with both the aborting and unwinding
67 /// runtimes. The default hook prints a message to standard error and generates
68 /// a backtrace if requested, but this behavior can be customized with the
69 /// `set_hook` and [`take_hook`] functions.
71 /// [`take_hook`]: ./fn.take_hook.html
73 /// The hook is provided with a `PanicInfo` struct which contains information
74 /// about the origin of the panic, including the payload passed to `panic!` and
75 /// the source code location from which the panic originated.
77 /// The panic hook is a global resource.
81 /// Panics if called from a panicking thread.
85 /// The following will print "Custom panic hook":
90 /// panic::set_hook(Box::new(|_| {
91 /// println!("Custom panic hook");
94 /// panic!("Normal panic");
96 #[stable(feature = "panic_hooks", since = "1.10.0")]
97 pub fn set_hook(hook
: Box
<dyn Fn(&PanicInfo
) + '
static + Sync
+ Send
>) {
98 if thread
::panicking() {
99 panic
!("cannot modify the panic hook from a panicking thread");
105 HOOK
= Hook
::Custom(Box
::into_raw(hook
));
106 HOOK_LOCK
.write_unlock();
108 if let Hook
::Custom(ptr
) = old_hook
{
114 /// Unregisters the current panic hook, returning it.
116 /// *See also the function [`set_hook`].*
118 /// [`set_hook`]: ./fn.set_hook.html
120 /// If no custom hook is registered, the default hook will be returned.
124 /// Panics if called from a panicking thread.
128 /// The following will print "Normal panic":
133 /// panic::set_hook(Box::new(|_| {
134 /// println!("Custom panic hook");
137 /// let _ = panic::take_hook();
139 /// panic!("Normal panic");
141 #[stable(feature = "panic_hooks", since = "1.10.0")]
142 pub fn take_hook() -> Box
<dyn Fn(&PanicInfo
) + '
static + Sync
+ Send
> {
143 if thread
::panicking() {
144 panic
!("cannot modify the panic hook from a panicking thread");
150 HOOK
= Hook
::Default
;
151 HOOK_LOCK
.write_unlock();
154 Hook
::Default
=> Box
::new(default_hook
),
155 Hook
::Custom(ptr
) => Box
::from_raw(ptr
),
160 fn default_hook(info
: &PanicInfo
) {
161 #[cfg(feature = "backtrace")]
162 use sys_common
::backtrace
;
164 // If this is a double panic, make sure that we print a backtrace
165 // for this panic. Otherwise only print it if logging is enabled.
166 #[cfg(feature = "backtrace")]
167 let log_backtrace
= {
168 let panics
= update_panic_count(0);
171 Some(backtrace
::PrintFormat
::Full
)
173 backtrace
::log_enabled()
177 let location
= info
.location().unwrap(); // The current implementation always returns Some
179 let msg
= match info
.payload().downcast_ref
::<&'
static str>() {
181 None
=> match info
.payload().downcast_ref
::<String
>() {
186 let thread
= thread_info
::current_thread();
187 let name
= thread
.as_ref().and_then(|t
| t
.name()).unwrap_or("<unnamed>");
189 let write
= |err
: &mut dyn (::io
::Write
)| {
190 let _
= writeln
!(err
, "thread '{}' panicked at '{}', {}",
191 name
, msg
, location
);
193 #[cfg(feature = "backtrace")]
195 use sync
::atomic
::{AtomicBool, Ordering}
;
197 static FIRST_PANIC
: AtomicBool
= AtomicBool
::new(true);
199 if let Some(format
) = log_backtrace
{
200 let _
= backtrace
::print(err
, format
);
201 } else if FIRST_PANIC
.compare_and_swap(true, false, Ordering
::SeqCst
) {
202 let _
= writeln
!(err
, "note: Run with `RUST_BACKTRACE=1` \
203 environment variable to display a backtrace.");
208 if let Some(mut local
) = LOCAL_STDERR
.with(|s
| s
.borrow_mut().take()) {
210 let mut s
= Some(local
);
211 LOCAL_STDERR
.with(|slot
| {
212 *slot
.borrow_mut() = s
.take();
214 } else if let Some(mut out
) = panic_output() {
222 #[unstable(feature = "update_panic_count", issue = "0")]
223 pub fn update_panic_count(amt
: isize) -> usize {
225 thread_local
! { static PANIC_COUNT: Cell<usize> = Cell::new(0) }
227 PANIC_COUNT
.with(|c
| {
228 let next
= (c
.get() as isize + amt
) as usize;
235 pub use realstd
::rt
::update_panic_count
;
237 /// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
238 pub unsafe fn try
<R
, F
: FnOnce() -> R
>(f
: F
) -> Result
<R
, Box
<dyn Any
+ Send
>> {
239 #[allow(unions_with_drop_fields)]
245 // We do some sketchy operations with ownership here for the sake of
246 // performance. We can only pass pointers down to
247 // `__rust_maybe_catch_panic` (can't pass objects by value), so we do all
248 // the ownership tracking here manually using a union.
250 // We go through a transition where:
252 // * First, we set the data to be the closure that we're going to call.
253 // * When we make the function call, the `do_call` function below, we take
254 // ownership of the function pointer. At this point the `Data` union is
255 // entirely uninitialized.
256 // * If the closure successfully returns, we write the return value into the
257 // data's return slot. Note that `ptr::write` is used as it's overwriting
258 // uninitialized data.
259 // * Finally, when we come back out of the `__rust_maybe_catch_panic` we're
260 // in one of two states:
262 // 1. The closure didn't panic, in which case the return value was
263 // filled in. We move it out of `data` and return it.
264 // 2. The closure panicked, in which case the return value wasn't
265 // filled in. In this case the entire `data` union is invalid, so
266 // there is no need to drop anything.
268 // Once we stack all that together we should have the "most efficient'
269 // method of calling a catch panic whilst juggling ownership.
270 let mut any_data
= 0;
271 let mut any_vtable
= 0;
272 let mut data
= Data
{
276 let r
= __rust_maybe_catch_panic(do_call
::<F
, R
>,
277 &mut data
as *mut _
as *mut u8,
282 debug_assert
!(update_panic_count(0) == 0);
285 update_panic_count(-1);
286 debug_assert
!(update_panic_count(0) == 0);
287 Err(mem
::transmute(raw
::TraitObject
{
288 data
: any_data
as *mut _
,
289 vtable
: any_vtable
as *mut _
,
293 fn do_call
<F
: FnOnce() -> R
, R
>(data
: *mut u8) {
295 let data
= data
as *mut Data
<F
, R
>;
296 let f
= ptr
::read(&mut (*data
).f
);
297 ptr
::write(&mut (*data
).r
, f());
302 /// Determines whether the current thread is unwinding because of panic.
303 pub fn panicking() -> bool
{
304 update_panic_count(0) != 0
307 /// Entry point of panic from the libcore crate.
311 pub fn rust_begin_panic(info
: &PanicInfo
) -> ! {
312 continue_panic_fmt(&info
)
315 /// The entry point for panicking with a formatted message.
317 /// This is designed to reduce the amount of code required at the call
318 /// site as much as possible (so that `panic!()` has as low an impact
319 /// on (e.g.) the inlining of other functions as possible), by moving
320 /// the actual formatting into this shared place.
321 #[unstable(feature = "libstd_sys_internals",
322 reason
= "used by the panic! macro",
325 // If panic_immediate_abort, inline the abort call,
326 // otherwise avoid inlining because of it is cold path.
327 #[cfg_attr(not(feature="panic_immediate_abort"),inline(never))]
328 #[cfg_attr( feature="panic_immediate_abort" ,inline)]
329 pub fn begin_panic_fmt(msg
: &fmt
::Arguments
,
330 file_line_col
: &(&'
static str, u32, u32)) -> ! {
331 if cfg
!(feature
= "panic_immediate_abort") {
332 unsafe { intrinsics::abort() }
335 let (file
, line
, col
) = *file_line_col
;
336 let info
= PanicInfo
::internal_constructor(
338 Location
::internal_constructor(file
, line
, col
),
340 continue_panic_fmt(&info
)
343 fn continue_panic_fmt(info
: &PanicInfo
) -> ! {
344 struct PanicPayload
<'a
> {
345 inner
: &'a fmt
::Arguments
<'a
>,
346 string
: Option
<String
>,
349 impl<'a
> PanicPayload
<'a
> {
350 fn new(inner
: &'a fmt
::Arguments
<'a
>) -> PanicPayload
<'a
> {
351 PanicPayload { inner, string: None }
354 fn fill(&mut self) -> &mut String
{
357 let inner
= self.inner
;
358 self.string
.get_or_insert_with(|| {
359 let mut s
= String
::new();
360 drop(s
.write_fmt(*inner
));
366 unsafe impl<'a
> BoxMeUp
for PanicPayload
<'a
> {
367 fn box_me_up(&mut self) -> *mut (dyn Any
+ Send
) {
368 let contents
= mem
::replace(self.fill(), String
::new());
369 Box
::into_raw(Box
::new(contents
))
372 fn get(&mut self) -> &(dyn Any
+ Send
) {
377 // We do two allocations here, unfortunately. But (a) they're
378 // required with the current scheme, and (b) we don't handle
379 // panic + OOM properly anyway (see comment in begin_panic
382 let loc
= info
.location().unwrap(); // The current implementation always returns Some
383 let msg
= info
.message().unwrap(); // The current implementation always returns Some
384 let file_line_col
= (loc
.file(), loc
.line(), loc
.column());
385 rust_panic_with_hook(
386 &mut PanicPayload
::new(msg
),
391 /// This is the entry point of panicking for panic!() and assert!().
392 #[unstable(feature = "libstd_sys_internals",
393 reason
= "used by the panic! macro",
395 #[cfg_attr(not(test), lang = "begin_panic")]
396 // never inline unless panic_immediate_abort to avoid code
397 // bloat at the call sites as much as possible
398 #[cfg_attr(not(feature="panic_immediate_abort"),inline(never))]
400 pub fn begin_panic
<M
: Any
+ Send
>(msg
: M
, file_line_col
: &(&'
static str, u32, u32)) -> ! {
401 if cfg
!(feature
= "panic_immediate_abort") {
402 unsafe { intrinsics::abort() }
405 // Note that this should be the only allocation performed in this code path.
406 // Currently this means that panic!() on OOM will invoke this code path,
407 // but then again we're not really ready for panic on OOM anyway. If
408 // we do start doing this, then we should propagate this allocation to
409 // be performed in the parent of this thread instead of the thread that's
412 rust_panic_with_hook(&mut PanicPayload
::new(msg
), None
, file_line_col
);
414 struct PanicPayload
<A
> {
418 impl<A
: Send
+ '
static> PanicPayload
<A
> {
419 fn new(inner
: A
) -> PanicPayload
<A
> {
420 PanicPayload { inner: Some(inner) }
424 unsafe impl<A
: Send
+ '
static> BoxMeUp
for PanicPayload
<A
> {
425 fn box_me_up(&mut self) -> *mut (dyn Any
+ Send
) {
426 let data
= match self.inner
.take() {
427 Some(a
) => Box
::new(a
) as Box
<dyn Any
+ Send
>,
428 None
=> Box
::new(()),
433 fn get(&mut self) -> &(dyn Any
+ Send
) {
442 /// Central point for dispatching panics.
444 /// Executes the primary logic for a panic, including checking for recursive
445 /// panics, panic hooks, and finally dispatching to the panic runtime to either
447 fn rust_panic_with_hook(payload
: &mut dyn BoxMeUp
,
448 message
: Option
<&fmt
::Arguments
>,
449 file_line_col
: &(&str, u32, u32)) -> ! {
450 let (file
, line
, col
) = *file_line_col
;
452 let panics
= update_panic_count(1);
454 // If this is the third nested call (e.g., panics == 2, this is 0-indexed),
455 // the panic hook probably triggered the last panic, otherwise the
456 // double-panic check would have aborted the process. In this case abort the
457 // process real quickly as we don't want to try calling it again as it'll
458 // probably just panic again.
460 util
::dumb_print(format_args
!("thread panicked while processing \
461 panic. aborting.\n"));
462 unsafe { intrinsics::abort() }
466 let mut info
= PanicInfo
::internal_constructor(
468 Location
::internal_constructor(file
, line
, col
),
472 // Some platforms know that printing to stderr won't ever actually
473 // print anything, and if that's the case we can skip the default
475 Hook
::Default
if panic_output().is_none() => {}
477 info
.set_payload(payload
.get());
480 Hook
::Custom(ptr
) => {
481 info
.set_payload(payload
.get());
485 HOOK_LOCK
.read_unlock();
489 // If a thread panics while it's already unwinding then we
490 // have limited options. Currently our preference is to
491 // just abort. In the future we may consider resuming
492 // unwinding or otherwise exiting the thread cleanly.
493 util
::dumb_print(format_args
!("thread panicked while panicking. \
495 unsafe { intrinsics::abort() }
501 /// Shim around rust_panic. Called by resume_unwind.
502 pub fn update_count_then_panic(msg
: Box
<dyn Any
+ Send
>) -> ! {
503 update_panic_count(1);
505 struct RewrapBox(Box
<dyn Any
+ Send
>);
507 unsafe impl BoxMeUp
for RewrapBox
{
508 fn box_me_up(&mut self) -> *mut (dyn Any
+ Send
) {
509 Box
::into_raw(mem
::replace(&mut self.0, Box
::new(())))
512 fn get(&mut self) -> &(dyn Any
+ Send
) {
517 rust_panic(&mut RewrapBox(msg
))
520 /// An unmangled function (through `rustc_std_internal_symbol`) on which to slap
523 #[cfg_attr(not(test), rustc_std_internal_symbol)]
524 fn rust_panic(mut msg
: &mut dyn BoxMeUp
) -> ! {
526 let obj
= &mut msg
as *mut &mut dyn BoxMeUp
;
527 __rust_start_panic(obj
as usize)
529 rtabort
!("failed to initiate panic, error {}", code
)