]> git.lizzy.rs Git - rust.git/blob - library/std/src/panicking.rs
Auto merge of #75813 - petrochenkov:feature/incr-def-path-table, r=Aaron1011
[rust.git] / library / std / src / panicking.rs
1 //! Implementation of various bits and pieces of the `panic!` macro and
2 //! associated runtime pieces.
3 //!
4 //! Specifically, this module contains the implementation of:
5 //!
6 //! * Panic hooks
7 //! * Executing a panic up to doing the actual implementation
8 //! * Shims around "try"
9
10 #![deny(unsafe_op_in_unsafe_fn)]
11
12 use core::panic::{BoxMeUp, Location, PanicInfo};
13
14 use crate::any::Any;
15 use crate::fmt;
16 use crate::intrinsics;
17 use crate::mem::{self, ManuallyDrop};
18 use crate::process;
19 use crate::sync::atomic::{AtomicBool, Ordering};
20 use crate::sys::stdio::panic_output;
21 use crate::sys_common::backtrace::{self, RustBacktrace};
22 use crate::sys_common::rwlock::RWLock;
23 use crate::sys_common::{thread_info, util};
24 use crate::thread;
25
26 #[cfg(not(test))]
27 use crate::io::set_panic;
28 // make sure to use the stderr output configured
29 // by libtest in the real copy of std
30 #[cfg(test)]
31 use realstd::io::set_panic;
32
33 // Binary interface to the panic runtime that the standard library depends on.
34 //
35 // The standard library is tagged with `#![needs_panic_runtime]` (introduced in
36 // RFC 1513) to indicate that it requires some other crate tagged with
37 // `#![panic_runtime]` to exist somewhere. Each panic runtime is intended to
38 // implement these symbols (with the same signatures) so we can get matched up
39 // to them.
40 //
41 // One day this may look a little less ad-hoc with the compiler helping out to
42 // hook up these functions, but it is not this day!
43 #[allow(improper_ctypes)]
44 extern "C" {
45     fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
46
47     /// `payload` is actually a `*mut &mut dyn BoxMeUp` but that would cause FFI warnings.
48     /// It cannot be `Box<dyn BoxMeUp>` because the other end of this call does not depend
49     /// on liballoc, and thus cannot use `Box`.
50     #[unwind(allowed)]
51     fn __rust_start_panic(payload: usize) -> u32;
52 }
53
54 /// This function is called by the panic runtime if FFI code catches a Rust
55 /// panic but doesn't rethrow it. We don't support this case since it messes
56 /// with our panic count.
57 #[cfg(not(test))]
58 #[rustc_std_internal_symbol]
59 extern "C" fn __rust_drop_panic() -> ! {
60     rtabort!("Rust panics must be rethrown");
61 }
62
63 #[derive(Copy, Clone)]
64 enum Hook {
65     Default,
66     Custom(*mut (dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send)),
67 }
68
69 static HOOK_LOCK: RWLock = RWLock::new();
70 static mut HOOK: Hook = Hook::Default;
71
72 /// Registers a custom panic hook, replacing any that was previously registered.
73 ///
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.
79 ///
80 /// [`take_hook`]: ./fn.take_hook.html
81 ///
82 /// The hook is provided with a `PanicInfo` struct which contains information
83 /// about the origin of the panic, including the payload passed to `panic!` and
84 /// the source code location from which the panic originated.
85 ///
86 /// The panic hook is a global resource.
87 ///
88 /// # Panics
89 ///
90 /// Panics if called from a panicking thread.
91 ///
92 /// # Examples
93 ///
94 /// The following will print "Custom panic hook":
95 ///
96 /// ```should_panic
97 /// use std::panic;
98 ///
99 /// panic::set_hook(Box::new(|_| {
100 ///     println!("Custom panic hook");
101 /// }));
102 ///
103 /// panic!("Normal panic");
104 /// ```
105 #[stable(feature = "panic_hooks", since = "1.10.0")]
106 pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) {
107     if thread::panicking() {
108         panic!("cannot modify the panic hook from a panicking thread");
109     }
110
111     unsafe {
112         HOOK_LOCK.write();
113         let old_hook = HOOK;
114         HOOK = Hook::Custom(Box::into_raw(hook));
115         HOOK_LOCK.write_unlock();
116
117         if let Hook::Custom(ptr) = old_hook {
118             #[allow(unused_must_use)]
119             {
120                 Box::from_raw(ptr);
121             }
122         }
123     }
124 }
125
126 /// Unregisters the current panic hook, returning it.
127 ///
128 /// *See also the function [`set_hook`].*
129 ///
130 /// [`set_hook`]: ./fn.set_hook.html
131 ///
132 /// If no custom hook is registered, the default hook will be returned.
133 ///
134 /// # Panics
135 ///
136 /// Panics if called from a panicking thread.
137 ///
138 /// # Examples
139 ///
140 /// The following will print "Normal panic":
141 ///
142 /// ```should_panic
143 /// use std::panic;
144 ///
145 /// panic::set_hook(Box::new(|_| {
146 ///     println!("Custom panic hook");
147 /// }));
148 ///
149 /// let _ = panic::take_hook();
150 ///
151 /// panic!("Normal panic");
152 /// ```
153 #[stable(feature = "panic_hooks", since = "1.10.0")]
154 pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
155     if thread::panicking() {
156         panic!("cannot modify the panic hook from a panicking thread");
157     }
158
159     unsafe {
160         HOOK_LOCK.write();
161         let hook = HOOK;
162         HOOK = Hook::Default;
163         HOOK_LOCK.write_unlock();
164
165         match hook {
166             Hook::Default => Box::new(default_hook),
167             Hook::Custom(ptr) => Box::from_raw(ptr),
168         }
169     }
170 }
171
172 fn default_hook(info: &PanicInfo<'_>) {
173     // If this is a double panic, make sure that we print a backtrace
174     // for this panic. Otherwise only print it if logging is enabled.
175     let backtrace_env = if panic_count::get() >= 2 {
176         RustBacktrace::Print(crate::backtrace_rs::PrintFmt::Full)
177     } else {
178         backtrace::rust_backtrace_env()
179     };
180
181     // The current implementation always returns `Some`.
182     let location = info.location().unwrap();
183
184     let msg = match info.payload().downcast_ref::<&'static str>() {
185         Some(s) => *s,
186         None => match info.payload().downcast_ref::<String>() {
187             Some(s) => &s[..],
188             None => "Box<Any>",
189         },
190     };
191     let thread = thread_info::current_thread();
192     let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
193
194     let write = |err: &mut dyn crate::io::Write| {
195         let _ = writeln!(err, "thread '{}' panicked at '{}', {}", name, msg, location);
196
197         static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
198
199         match backtrace_env {
200             RustBacktrace::Print(format) => drop(backtrace::print(err, format)),
201             RustBacktrace::Disabled => {}
202             RustBacktrace::RuntimeDisabled => {
203                 if FIRST_PANIC.swap(false, Ordering::SeqCst) {
204                     let _ = writeln!(
205                         err,
206                         "note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace"
207                     );
208                 }
209             }
210         }
211     };
212
213     if let Some(mut local) = set_panic(None) {
214         // NB. In `cfg(test)` this uses the forwarding impl
215         // for `Box<dyn (::realstd::io::Write) + Send>`.
216         write(&mut local);
217         set_panic(Some(local));
218     } else if let Some(mut out) = panic_output() {
219         write(&mut out);
220     }
221 }
222
223 #[cfg(not(test))]
224 #[doc(hidden)]
225 #[unstable(feature = "update_panic_count", issue = "none")]
226 pub mod panic_count {
227     use crate::cell::Cell;
228     use crate::sync::atomic::{AtomicUsize, Ordering};
229
230     // Panic count for the current thread.
231     thread_local! { static LOCAL_PANIC_COUNT: Cell<usize> = Cell::new(0) }
232
233     // Sum of panic counts from all threads. The purpose of this is to have
234     // a fast path in `is_zero` (which is used by `panicking`). In any particular
235     // thread, if that thread currently views `GLOBAL_PANIC_COUNT` as being zero,
236     // then `LOCAL_PANIC_COUNT` in that thread is zero. This invariant holds before
237     // and after increase and decrease, but not necessarily during their execution.
238     static GLOBAL_PANIC_COUNT: AtomicUsize = AtomicUsize::new(0);
239
240     pub fn increase() -> usize {
241         GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed);
242         LOCAL_PANIC_COUNT.with(|c| {
243             let next = c.get() + 1;
244             c.set(next);
245             next
246         })
247     }
248
249     pub fn decrease() -> usize {
250         GLOBAL_PANIC_COUNT.fetch_sub(1, Ordering::Relaxed);
251         LOCAL_PANIC_COUNT.with(|c| {
252             let next = c.get() - 1;
253             c.set(next);
254             next
255         })
256     }
257
258     pub fn get() -> usize {
259         LOCAL_PANIC_COUNT.with(|c| c.get())
260     }
261
262     #[inline]
263     pub fn is_zero() -> bool {
264         if GLOBAL_PANIC_COUNT.load(Ordering::Relaxed) == 0 {
265             // Fast path: if `GLOBAL_PANIC_COUNT` is zero, all threads
266             // (including the current one) will have `LOCAL_PANIC_COUNT`
267             // equal to zero, so TLS access can be avoided.
268             //
269             // In terms of performance, a relaxed atomic load is similar to a normal
270             // aligned memory read (e.g., a mov instruction in x86), but with some
271             // compiler optimization restrictions. On the other hand, a TLS access
272             // might require calling a non-inlinable function (such as `__tls_get_addr`
273             // when using the GD TLS model).
274             true
275         } else {
276             is_zero_slow_path()
277         }
278     }
279
280     // Slow path is in a separate function to reduce the amount of code
281     // inlined from `is_zero`.
282     #[inline(never)]
283     #[cold]
284     fn is_zero_slow_path() -> bool {
285         LOCAL_PANIC_COUNT.with(|c| c.get() == 0)
286     }
287 }
288
289 #[cfg(test)]
290 pub use realstd::rt::panic_count;
291
292 /// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
293 pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
294     union Data<F, R> {
295         f: ManuallyDrop<F>,
296         r: ManuallyDrop<R>,
297         p: ManuallyDrop<Box<dyn Any + Send>>,
298     }
299
300     // We do some sketchy operations with ownership here for the sake of
301     // performance. We can only pass pointers down to `do_call` (can't pass
302     // objects by value), so we do all the ownership tracking here manually
303     // using a union.
304     //
305     // We go through a transition where:
306     //
307     // * First, we set the data field `f` to be the argumentless closure that we're going to call.
308     // * When we make the function call, the `do_call` function below, we take
309     //   ownership of the function pointer. At this point the `data` union is
310     //   entirely uninitialized.
311     // * If the closure successfully returns, we write the return value into the
312     //   data's return slot (field `r`).
313     // * If the closure panics (`do_catch` below), we write the panic payload into field `p`.
314     // * Finally, when we come back out of the `try` intrinsic we're
315     //   in one of two states:
316     //
317     //      1. The closure didn't panic, in which case the return value was
318     //         filled in. We move it out of `data.r` and return it.
319     //      2. The closure panicked, in which case the panic payload was
320     //         filled in. We move it out of `data.p` and return it.
321     //
322     // Once we stack all that together we should have the "most efficient'
323     // method of calling a catch panic whilst juggling ownership.
324     let mut data = Data { f: ManuallyDrop::new(f) };
325
326     let data_ptr = &mut data as *mut _ as *mut u8;
327     // SAFETY:
328     //
329     // Access to the union's fields: this is `std` and we know that the `r#try`
330     // intrinsic fills in the `r` or `p` union field based on its return value.
331     //
332     // The call to `intrinsics::r#try` is made safe by:
333     // - `do_call`, the first argument, can be called with the initial `data_ptr`.
334     // - `do_catch`, the second argument, can be called with the `data_ptr` as well.
335     // See their safety preconditions for more informations
336     unsafe {
337         return if intrinsics::r#try(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
338             Ok(ManuallyDrop::into_inner(data.r))
339         } else {
340             Err(ManuallyDrop::into_inner(data.p))
341         };
342     }
343
344     // We consider unwinding to be rare, so mark this function as cold. However,
345     // do not mark it no-inline -- that decision is best to leave to the
346     // optimizer (in most cases this function is not inlined even as a normal,
347     // non-cold function, though, as of the writing of this comment).
348     #[cold]
349     unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send + 'static> {
350         // SAFETY: The whole unsafe block hinges on a correct implementation of
351         // the panic handler `__rust_panic_cleanup`. As such we can only
352         // assume it returns the correct thing for `Box::from_raw` to work
353         // without undefined behavior.
354         let obj = unsafe { Box::from_raw(__rust_panic_cleanup(payload)) };
355         panic_count::decrease();
356         obj
357     }
358
359     // SAFETY:
360     // data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>`
361     // Its must contains a valid `f` (type: F) value that can be use to fill
362     // `data.r`.
363     //
364     // This function cannot be marked as `unsafe` because `intrinsics::r#try`
365     // expects normal function pointers.
366     #[inline]
367     fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
368         // SAFETY: this is the responsibilty of the caller, see above.
369         unsafe {
370             let data = data as *mut Data<F, R>;
371             let data = &mut (*data);
372             let f = ManuallyDrop::take(&mut data.f);
373             data.r = ManuallyDrop::new(f());
374         }
375     }
376
377     // We *do* want this part of the catch to be inlined: this allows the
378     // compiler to properly track accesses to the Data union and optimize it
379     // away most of the time.
380     //
381     // SAFETY:
382     // data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>`
383     // Since this uses `cleanup` it also hinges on a correct implementation of
384     // `__rustc_panic_cleanup`.
385     //
386     // This function cannot be marked as `unsafe` because `intrinsics::r#try`
387     // expects normal function pointers.
388     #[inline]
389     fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) {
390         // SAFETY: this is the responsibilty of the caller, see above.
391         //
392         // When `__rustc_panic_cleaner` is correctly implemented we can rely
393         // on `obj` being the correct thing to pass to `data.p` (after wrapping
394         // in `ManuallyDrop`).
395         unsafe {
396             let data = data as *mut Data<F, R>;
397             let data = &mut (*data);
398             let obj = cleanup(payload);
399             data.p = ManuallyDrop::new(obj);
400         }
401     }
402 }
403
404 /// Determines whether the current thread is unwinding because of panic.
405 #[inline]
406 pub fn panicking() -> bool {
407     !panic_count::is_zero()
408 }
409
410 /// The entry point for panicking with a formatted message.
411 ///
412 /// This is designed to reduce the amount of code required at the call
413 /// site as much as possible (so that `panic!()` has as low an impact
414 /// on (e.g.) the inlining of other functions as possible), by moving
415 /// the actual formatting into this shared place.
416 #[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")]
417 #[cold]
418 // If panic_immediate_abort, inline the abort call,
419 // otherwise avoid inlining because of it is cold path.
420 #[cfg_attr(not(feature = "panic_immediate_abort"), track_caller)]
421 #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
422 #[cfg_attr(feature = "panic_immediate_abort", inline)]
423 pub fn begin_panic_fmt(msg: &fmt::Arguments<'_>) -> ! {
424     if cfg!(feature = "panic_immediate_abort") {
425         intrinsics::abort()
426     }
427
428     let info = PanicInfo::internal_constructor(Some(msg), Location::caller());
429     begin_panic_handler(&info)
430 }
431
432 /// Entry point of panics from the libcore crate (`panic_impl` lang item).
433 #[cfg_attr(not(test), panic_handler)]
434 #[unwind(allowed)]
435 pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
436     struct PanicPayload<'a> {
437         inner: &'a fmt::Arguments<'a>,
438         string: Option<String>,
439     }
440
441     impl<'a> PanicPayload<'a> {
442         fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> {
443             PanicPayload { inner, string: None }
444         }
445
446         fn fill(&mut self) -> &mut String {
447             use crate::fmt::Write;
448
449             let inner = self.inner;
450             // Lazily, the first time this gets called, run the actual string formatting.
451             self.string.get_or_insert_with(|| {
452                 let mut s = String::new();
453                 drop(s.write_fmt(*inner));
454                 s
455             })
456         }
457     }
458
459     unsafe impl<'a> BoxMeUp for PanicPayload<'a> {
460         fn take_box(&mut self) -> *mut (dyn Any + Send) {
461             // We do two allocations here, unfortunately. But (a) they're required with the current
462             // scheme, and (b) we don't handle panic + OOM properly anyway (see comment in
463             // begin_panic below).
464             let contents = mem::take(self.fill());
465             Box::into_raw(Box::new(contents))
466         }
467
468         fn get(&mut self) -> &(dyn Any + Send) {
469             self.fill()
470         }
471     }
472
473     let loc = info.location().unwrap(); // The current implementation always returns Some
474     let msg = info.message().unwrap(); // The current implementation always returns Some
475     crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
476         rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc);
477     })
478 }
479
480 /// This is the entry point of panicking for the non-format-string variants of
481 /// panic!() and assert!(). In particular, this is the only entry point that supports
482 /// arbitrary payloads, not just format strings.
483 #[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")]
484 #[cfg_attr(not(test), lang = "begin_panic")]
485 // lang item for CTFE panic support
486 // never inline unless panic_immediate_abort to avoid code
487 // bloat at the call sites as much as possible
488 #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
489 #[cold]
490 #[track_caller]
491 pub fn begin_panic<M: Any + Send>(msg: M) -> ! {
492     if cfg!(feature = "panic_immediate_abort") {
493         intrinsics::abort()
494     }
495
496     let loc = Location::caller();
497     return crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
498         rust_panic_with_hook(&mut PanicPayload::new(msg), None, loc)
499     });
500
501     struct PanicPayload<A> {
502         inner: Option<A>,
503     }
504
505     impl<A: Send + 'static> PanicPayload<A> {
506         fn new(inner: A) -> PanicPayload<A> {
507             PanicPayload { inner: Some(inner) }
508         }
509     }
510
511     unsafe impl<A: Send + 'static> BoxMeUp for PanicPayload<A> {
512         fn take_box(&mut self) -> *mut (dyn Any + Send) {
513             // Note that this should be the only allocation performed in this code path. Currently
514             // this means that panic!() on OOM will invoke this code path, but then again we're not
515             // really ready for panic on OOM anyway. If we do start doing this, then we should
516             // propagate this allocation to be performed in the parent of this thread instead of the
517             // thread that's panicking.
518             let data = match self.inner.take() {
519                 Some(a) => Box::new(a) as Box<dyn Any + Send>,
520                 None => process::abort(),
521             };
522             Box::into_raw(data)
523         }
524
525         fn get(&mut self) -> &(dyn Any + Send) {
526             match self.inner {
527                 Some(ref a) => a,
528                 None => process::abort(),
529             }
530         }
531     }
532 }
533
534 /// Central point for dispatching panics.
535 ///
536 /// Executes the primary logic for a panic, including checking for recursive
537 /// panics, panic hooks, and finally dispatching to the panic runtime to either
538 /// abort or unwind.
539 fn rust_panic_with_hook(
540     payload: &mut dyn BoxMeUp,
541     message: Option<&fmt::Arguments<'_>>,
542     location: &Location<'_>,
543 ) -> ! {
544     let panics = panic_count::increase();
545
546     // If this is the third nested call (e.g., panics == 2, this is 0-indexed),
547     // the panic hook probably triggered the last panic, otherwise the
548     // double-panic check would have aborted the process. In this case abort the
549     // process real quickly as we don't want to try calling it again as it'll
550     // probably just panic again.
551     if panics > 2 {
552         util::dumb_print(format_args!("thread panicked while processing panic. aborting.\n"));
553         intrinsics::abort()
554     }
555
556     unsafe {
557         let mut info = PanicInfo::internal_constructor(message, location);
558         HOOK_LOCK.read();
559         match HOOK {
560             // Some platforms (like wasm) know that printing to stderr won't ever actually
561             // print anything, and if that's the case we can skip the default
562             // hook. Since string formatting happens lazily when calling `payload`
563             // methods, this means we avoid formatting the string at all!
564             // (The panic runtime might still call `payload.take_box()` though and trigger
565             // formatting.)
566             Hook::Default if panic_output().is_none() => {}
567             Hook::Default => {
568                 info.set_payload(payload.get());
569                 default_hook(&info);
570             }
571             Hook::Custom(ptr) => {
572                 info.set_payload(payload.get());
573                 (*ptr)(&info);
574             }
575         };
576         HOOK_LOCK.read_unlock();
577     }
578
579     if panics > 1 {
580         // If a thread panics while it's already unwinding then we
581         // have limited options. Currently our preference is to
582         // just abort. In the future we may consider resuming
583         // unwinding or otherwise exiting the thread cleanly.
584         util::dumb_print(format_args!("thread panicked while panicking. aborting.\n"));
585         intrinsics::abort()
586     }
587
588     rust_panic(payload)
589 }
590
591 /// This is the entry point for `resume_unwind`.
592 /// It just forwards the payload to the panic runtime.
593 pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! {
594     panic_count::increase();
595
596     struct RewrapBox(Box<dyn Any + Send>);
597
598     unsafe impl BoxMeUp for RewrapBox {
599         fn take_box(&mut self) -> *mut (dyn Any + Send) {
600             Box::into_raw(mem::replace(&mut self.0, Box::new(())))
601         }
602
603         fn get(&mut self) -> &(dyn Any + Send) {
604             &*self.0
605         }
606     }
607
608     rust_panic(&mut RewrapBox(payload))
609 }
610
611 /// An unmangled function (through `rustc_std_internal_symbol`) on which to slap
612 /// yer breakpoints.
613 #[inline(never)]
614 #[cfg_attr(not(test), rustc_std_internal_symbol)]
615 fn rust_panic(mut msg: &mut dyn BoxMeUp) -> ! {
616     let code = unsafe {
617         let obj = &mut msg as *mut &mut dyn BoxMeUp;
618         __rust_start_panic(obj as usize)
619     };
620     rtabort!("failed to initiate panic, error {}", code)
621 }