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::{Location, PanicPayload}; |
13 | |
14 | // make sure to use the stderr output configured |
15 | // by libtest in the real copy of std |
16 | #[cfg(test)] |
17 | use realstd::io::try_set_output_capture; |
18 | |
19 | use crate::any::Any; |
20 | #[cfg(not(test))] |
21 | use crate::io::try_set_output_capture; |
22 | use crate::mem::{self, ManuallyDrop}; |
23 | use crate::panic::{BacktraceStyle, PanicHookInfo}; |
24 | use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; |
25 | use crate::sync::{PoisonError, RwLock}; |
26 | use crate::sys::backtrace; |
27 | use crate::sys::stdio::panic_output; |
28 | use crate::{fmt, intrinsics, process, thread}; |
29 | |
30 | // This forces codegen of the function called by panic!() inside the std crate, rather than in |
31 | // downstream crates. Primarily this is useful for rustc's codegen tests, which rely on noticing |
32 | // complete removal of panic from generated IR. Since begin_panic is inline(never), it's only |
33 | // codegen'd once per crate-graph so this pushes that to std rather than our codegen test crates. |
34 | // |
35 | // (See https://github.com/rust-lang/rust/pull/123244 for more info on why). |
36 | // |
37 | // If this is causing problems we can also modify those codegen tests to use a crate type like |
38 | // cdylib which doesn't export "Rust" symbols to downstream linkage units. |
39 | #[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")] |
40 | #[doc(hidden)] |
41 | #[allow(dead_code)] |
42 | #[used(compiler)] |
43 | pub static EMPTY_PANIC: fn(&'static str) -> ! = |
44 | begin_panic::<&'static str> as fn(&'static str) -> !; |
45 | |
46 | // Binary interface to the panic runtime that the standard library depends on. |
47 | // |
48 | // The standard library is tagged with `#![needs_panic_runtime]` (introduced in |
49 | // RFC 1513) to indicate that it requires some other crate tagged with |
50 | // `#![panic_runtime]` to exist somewhere. Each panic runtime is intended to |
51 | // implement these symbols (with the same signatures) so we can get matched up |
52 | // to them. |
53 | // |
54 | // One day this may look a little less ad-hoc with the compiler helping out to |
55 | // hook up these functions, but it is not this day! |
56 | #[allow(improper_ctypes)] |
57 | unsafe extern "C"{ |
58 | #[rustc_std_internal_symbol] |
59 | unsafefn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static); |
60 | } |
61 | |
62 | unsafe extern "Rust"{ |
63 | /// `PanicPayload` lazily performs allocation only when needed (this avoids |
64 | /// allocations when using the "abort" panic runtime). |
65 | #[rustc_std_internal_symbol] |
66 | unsafefn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32; |
67 | } |
68 | |
69 | /// This function is called by the panic runtime if FFI code catches a Rust |
70 | /// panic but doesn't rethrow it. We don't support this case since it messes |
71 | /// with our panic count. |
72 | #[cfg(not(test))] |
73 | #[rustc_std_internal_symbol] |
74 | extern "C"fn __rust_drop_panic() -> ! { |
75 | rtabort!("Rust panics must be rethrown"); |
76 | } |
77 | |
78 | /// This function is called by the panic runtime if it catches an exception |
79 | /// object which does not correspond to a Rust panic. |
80 | #[cfg(not(test))] |
81 | #[rustc_std_internal_symbol] |
82 | extern "C"fn __rust_foreign_exception() -> ! { |
83 | rtabort!("Rust cannot catch foreign exceptions"); |
84 | } |
85 | |
86 | #[derive(Default)] |
87 | enum Hook { |
88 | #[default] |
89 | Default, |
90 | Custom(Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send>), |
91 | } |
92 | |
93 | impl Hook { |
94 | #[inline] |
95 | fn into_box(self) -> Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send> { |
96 | match self { |
97 | Hook::Default => Box::new(default_hook), |
98 | Hook::Custom(hook: Box |
99 | } |
100 | } |
101 | } |
102 | |
103 | static HOOK: RwLock<Hook> = RwLock::new(Hook::Default); |
104 | |
105 | /// Registers a custom panic hook, replacing the previously registered hook. |
106 | /// |
107 | /// The panic hook is invoked when a thread panics, but before the panic runtime |
108 | /// is invoked. As such, the hook will run with both the aborting and unwinding |
109 | /// runtimes. |
110 | /// |
111 | /// The default hook, which is registered at startup, prints a message to standard error and |
112 | /// generates a backtrace if requested. This behavior can be customized using the `set_hook` function. |
113 | /// The current hook can be retrieved while reinstating the default hook with the [`take_hook`] |
114 | /// function. |
115 | /// |
116 | /// [`take_hook`]: ./fn.take_hook.html |
117 | /// |
118 | /// The hook is provided with a `PanicHookInfo` struct which contains information |
119 | /// about the origin of the panic, including the payload passed to `panic!` and |
120 | /// the source code location from which the panic originated. |
121 | /// |
122 | /// The panic hook is a global resource. |
123 | /// |
124 | /// # Panics |
125 | /// |
126 | /// Panics if called from a panicking thread. |
127 | /// |
128 | /// # Examples |
129 | /// |
130 | /// The following will print "Custom panic hook": |
131 | /// |
132 | /// ```should_panic |
133 | /// use std::panic; |
134 | /// |
135 | /// panic::set_hook(Box::new(|_| { |
136 | /// println!("Custom panic hook"); |
137 | /// })); |
138 | /// |
139 | /// panic!("Normal panic"); |
140 | /// ``` |
141 | #[stable(feature = "panic_hooks", since = "1.10.0")] |
142 | pub fn set_hook(hook: Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send>) { |
143 | if thread::panicking() { |
144 | panic!("cannot modify the panic hook from a panicking thread"); |
145 | } |
146 | |
147 | let new: Hook = Hook::Custom(hook); |
148 | let mut hook: RwLockWriteGuard<'_, Hook> = HOOK.write().unwrap_or_else(op:PoisonError::into_inner); |
149 | let old: Hook = mem::replace(&mut *hook, src:new); |
150 | drop(hook); |
151 | // Only drop the old hook after releasing the lock to avoid deadlocking |
152 | // if its destructor panics. |
153 | drop(old); |
154 | } |
155 | |
156 | /// Unregisters the current panic hook and returns it, registering the default hook |
157 | /// in its place. |
158 | /// |
159 | /// *See also the function [`set_hook`].* |
160 | /// |
161 | /// [`set_hook`]: ./fn.set_hook.html |
162 | /// |
163 | /// If the default hook is registered it will be returned, but remain registered. |
164 | /// |
165 | /// # Panics |
166 | /// |
167 | /// Panics if called from a panicking thread. |
168 | /// |
169 | /// # Examples |
170 | /// |
171 | /// The following will print "Normal panic": |
172 | /// |
173 | /// ```should_panic |
174 | /// use std::panic; |
175 | /// |
176 | /// panic::set_hook(Box::new(|_| { |
177 | /// println!("Custom panic hook"); |
178 | /// })); |
179 | /// |
180 | /// let _ = panic::take_hook(); |
181 | /// |
182 | /// panic!("Normal panic"); |
183 | /// ``` |
184 | #[must_use] |
185 | #[stable(feature = "panic_hooks", since = "1.10.0")] |
186 | pub fn take_hook() -> Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send> { |
187 | if thread::panicking() { |
188 | panic!("cannot modify the panic hook from a panicking thread"); |
189 | } |
190 | |
191 | let mut hook: RwLockWriteGuard<'_, Hook> = HOOK.write().unwrap_or_else(op:PoisonError::into_inner); |
192 | let old_hook: Hook = mem::take(&mut *hook); |
193 | drop(hook); |
194 | |
195 | old_hook.into_box() |
196 | } |
197 | |
198 | /// Atomic combination of [`take_hook`] and [`set_hook`]. Use this to replace the panic handler with |
199 | /// a new panic handler that does something and then executes the old handler. |
200 | /// |
201 | /// [`take_hook`]: ./fn.take_hook.html |
202 | /// [`set_hook`]: ./fn.set_hook.html |
203 | /// |
204 | /// # Panics |
205 | /// |
206 | /// Panics if called from a panicking thread. |
207 | /// |
208 | /// # Examples |
209 | /// |
210 | /// The following will print the custom message, and then the normal output of panic. |
211 | /// |
212 | /// ```should_panic |
213 | /// #![feature(panic_update_hook)] |
214 | /// use std::panic; |
215 | /// |
216 | /// // Equivalent to |
217 | /// // let prev = panic::take_hook(); |
218 | /// // panic::set_hook(move |info| { |
219 | /// // println!("..."); |
220 | /// // prev(info); |
221 | /// // ); |
222 | /// panic::update_hook(move |prev, info| { |
223 | /// println!("Print custom message and execute panic handler as usual"); |
224 | /// prev(info); |
225 | /// }); |
226 | /// |
227 | /// panic!("Custom and then normal"); |
228 | /// ``` |
229 | #[unstable(feature = "panic_update_hook", issue = "92649")] |
230 | pub fn update_hook<F>(hook_fn: F) |
231 | where |
232 | F: Fn(&(dyn Fn(&PanicHookInfo<'_>) + Send + Sync + 'static), &PanicHookInfo<'_>) |
233 | + Sync |
234 | + Send |
235 | + 'static, |
236 | { |
237 | if thread::panicking() { |
238 | panic!("cannot modify the panic hook from a panicking thread"); |
239 | } |
240 | |
241 | let mut hook: RwLockWriteGuard<'_, Hook> = HOOK.write().unwrap_or_else(op:PoisonError::into_inner); |
242 | let prev: Box |
243 | *hook = Hook::Custom(Box::new(move |info: &PanicHookInfo<'_>| hook_fn(&prev, info))); |
244 | } |
245 | |
246 | /// The default panic handler. |
247 | #[optimize(size)] |
248 | fn default_hook(info: &PanicHookInfo<'_>) { |
249 | // If this is a double panic, make sure that we print a backtrace |
250 | // for this panic. Otherwise only print it if logging is enabled. |
251 | let backtrace = if info.force_no_backtrace() { |
252 | None |
253 | } else if panic_count::get_count() >= 2 { |
254 | BacktraceStyle::full() |
255 | } else { |
256 | crate::panic::get_backtrace_style() |
257 | }; |
258 | |
259 | // The current implementation always returns `Some`. |
260 | let location = info.location().unwrap(); |
261 | |
262 | let msg = payload_as_str(info.payload()); |
263 | |
264 | let write = #[optimize(size)] |
265 | |err: &mut dyn crate::io::Write| { |
266 | // Use a lock to prevent mixed output in multithreading context. |
267 | // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows. |
268 | let mut lock = backtrace::lock(); |
269 | |
270 | thread::with_current_name(|name| { |
271 | let name = name.unwrap_or("<unnamed>"); |
272 | |
273 | // Try to write the panic message to a buffer first to prevent other concurrent outputs |
274 | // interleaving with it. |
275 | let mut buffer = [0u8; 512]; |
276 | let mut cursor = crate::io::Cursor::new(&mut buffer[..]); |
277 | |
278 | let write_msg = |dst: &mut dyn crate::io::Write| { |
279 | // We add a newline to ensure the panic message appears at the start of a line. |
280 | writeln!(dst, "\n thread '{name} ' panicked at{location} :\n{msg} ") |
281 | }; |
282 | |
283 | if write_msg(&mut cursor).is_ok() { |
284 | let pos = cursor.position() as usize; |
285 | let _ = err.write_all(&buffer[0..pos]); |
286 | } else { |
287 | // The message did not fit into the buffer, write it directly instead. |
288 | let _ = write_msg(err); |
289 | }; |
290 | }); |
291 | |
292 | static FIRST_PANIC: Atomic<bool> = AtomicBool::new(true); |
293 | |
294 | match backtrace { |
295 | // SAFETY: we took out a lock just a second ago. |
296 | Some(BacktraceStyle::Short) => { |
297 | drop(lock.print(err, crate::backtrace_rs::PrintFmt::Short)) |
298 | } |
299 | Some(BacktraceStyle::Full) => { |
300 | drop(lock.print(err, crate::backtrace_rs::PrintFmt::Full)) |
301 | } |
302 | Some(BacktraceStyle::Off) => { |
303 | if FIRST_PANIC.swap(false, Ordering::Relaxed) { |
304 | let _ = writeln!( |
305 | err, |
306 | "note: run with `RUST_BACKTRACE=1` environment variable to display a \ |
307 | backtrace" |
308 | ); |
309 | if cfg!(miri) { |
310 | let _ = writeln!( |
311 | err, |
312 | "note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` \ |
313 | for the environment variable to have an effect" |
314 | ); |
315 | } |
316 | } |
317 | } |
318 | // If backtraces aren't supported or are forced-off, do nothing. |
319 | None => {} |
320 | } |
321 | }; |
322 | |
323 | if let Ok(Some(local)) = try_set_output_capture(None) { |
324 | write(&mut *local.lock().unwrap_or_else(|e| e.into_inner())); |
325 | try_set_output_capture(Some(local)).ok(); |
326 | } else if let Some(mut out) = panic_output() { |
327 | write(&mut out); |
328 | } |
329 | } |
330 | |
331 | #[cfg(not(test))] |
332 | #[doc(hidden)] |
333 | #[cfg(feature = "panic_immediate_abort")] |
334 | #[unstable(feature = "update_panic_count", issue = "none")] |
335 | pub mod panic_count { |
336 | /// A reason for forcing an immediate abort on panic. |
337 | #[derive(Debug)] |
338 | pub enum MustAbort { |
339 | AlwaysAbort, |
340 | PanicInHook, |
341 | } |
342 | |
343 | #[inline] |
344 | pub fn increase(run_panic_hook: bool) -> Option<MustAbort> { |
345 | None |
346 | } |
347 | |
348 | #[inline] |
349 | pub fn finished_panic_hook() {} |
350 | |
351 | #[inline] |
352 | pub fn decrease() {} |
353 | |
354 | #[inline] |
355 | pub fn set_always_abort() {} |
356 | |
357 | // Disregards ALWAYS_ABORT_FLAG |
358 | #[inline] |
359 | #[must_use] |
360 | pub fn get_count() -> usize { |
361 | 0 |
362 | } |
363 | |
364 | #[must_use] |
365 | #[inline] |
366 | pub fn count_is_zero() -> bool { |
367 | true |
368 | } |
369 | } |
370 | |
371 | #[cfg(not(test))] |
372 | #[doc(hidden)] |
373 | #[cfg(not(feature = "panic_immediate_abort"))] |
374 | #[unstable(feature = "update_panic_count", issue = "none")] |
375 | pub mod panic_count { |
376 | use crate::cell::Cell; |
377 | use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; |
378 | |
379 | const ALWAYS_ABORT_FLAG: usize = 1 << (usize::BITS - 1); |
380 | |
381 | /// A reason for forcing an immediate abort on panic. |
382 | #[derive(Debug)] |
383 | pub enum MustAbort { |
384 | AlwaysAbort, |
385 | PanicInHook, |
386 | } |
387 | |
388 | // Panic count for the current thread and whether a panic hook is currently |
389 | // being executed.. |
390 | thread_local! { |
391 | static LOCAL_PANIC_COUNT: Cell<(usize, bool)> = const { Cell::new((0, false)) } |
392 | } |
393 | |
394 | // Sum of panic counts from all threads. The purpose of this is to have |
395 | // a fast path in `count_is_zero` (which is used by `panicking`). In any particular |
396 | // thread, if that thread currently views `GLOBAL_PANIC_COUNT` as being zero, |
397 | // then `LOCAL_PANIC_COUNT` in that thread is zero. This invariant holds before |
398 | // and after increase and decrease, but not necessarily during their execution. |
399 | // |
400 | // Additionally, the top bit of GLOBAL_PANIC_COUNT (GLOBAL_ALWAYS_ABORT_FLAG) |
401 | // records whether panic::always_abort() has been called. This can only be |
402 | // set, never cleared. |
403 | // panic::always_abort() is usually called to prevent memory allocations done by |
404 | // the panic handling in the child created by `libc::fork`. |
405 | // Memory allocations performed in a child created with `libc::fork` are undefined |
406 | // behavior in most operating systems. |
407 | // Accessing LOCAL_PANIC_COUNT in a child created by `libc::fork` would lead to a memory |
408 | // allocation. Only GLOBAL_PANIC_COUNT can be accessed in this situation. This is |
409 | // sufficient because a child process will always have exactly one thread only. |
410 | // See also #85261 for details. |
411 | // |
412 | // This could be viewed as a struct containing a single bit and an n-1-bit |
413 | // value, but if we wrote it like that it would be more than a single word, |
414 | // and even a newtype around usize would be clumsy because we need atomics. |
415 | // But we use such a tuple for the return type of increase(). |
416 | // |
417 | // Stealing a bit is fine because it just amounts to assuming that each |
418 | // panicking thread consumes at least 2 bytes of address space. |
419 | static GLOBAL_PANIC_COUNT: Atomic<usize> = AtomicUsize::new(0); |
420 | |
421 | // Increases the global and local panic count, and returns whether an |
422 | // immediate abort is required. |
423 | // |
424 | // This also updates thread-local state to keep track of whether a panic |
425 | // hook is currently executing. |
426 | pub fn increase(run_panic_hook: bool) -> Option<MustAbort> { |
427 | let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed); |
428 | if global_count & ALWAYS_ABORT_FLAG != 0 { |
429 | // Do *not* access thread-local state, we might be after a `fork`. |
430 | return Some(MustAbort::AlwaysAbort); |
431 | } |
432 | |
433 | LOCAL_PANIC_COUNT.with(|c| { |
434 | let (count, in_panic_hook) = c.get(); |
435 | if in_panic_hook { |
436 | return Some(MustAbort::PanicInHook); |
437 | } |
438 | c.set((count + 1, run_panic_hook)); |
439 | None |
440 | }) |
441 | } |
442 | |
443 | pub fn finished_panic_hook() { |
444 | LOCAL_PANIC_COUNT.with(|c| { |
445 | let (count, _) = c.get(); |
446 | c.set((count, false)); |
447 | }); |
448 | } |
449 | |
450 | pub fn decrease() { |
451 | GLOBAL_PANIC_COUNT.fetch_sub(1, Ordering::Relaxed); |
452 | LOCAL_PANIC_COUNT.with(|c| { |
453 | let (count, _) = c.get(); |
454 | c.set((count - 1, false)); |
455 | }); |
456 | } |
457 | |
458 | pub fn set_always_abort() { |
459 | GLOBAL_PANIC_COUNT.fetch_or(ALWAYS_ABORT_FLAG, Ordering::Relaxed); |
460 | } |
461 | |
462 | // Disregards ALWAYS_ABORT_FLAG |
463 | #[must_use] |
464 | pub fn get_count() -> usize { |
465 | LOCAL_PANIC_COUNT.with(|c| c.get().0) |
466 | } |
467 | |
468 | // Disregards ALWAYS_ABORT_FLAG |
469 | #[must_use] |
470 | #[inline] |
471 | pub fn count_is_zero() -> bool { |
472 | if GLOBAL_PANIC_COUNT.load(Ordering::Relaxed) & !ALWAYS_ABORT_FLAG == 0 { |
473 | // Fast path: if `GLOBAL_PANIC_COUNT` is zero, all threads |
474 | // (including the current one) will have `LOCAL_PANIC_COUNT` |
475 | // equal to zero, so TLS access can be avoided. |
476 | // |
477 | // In terms of performance, a relaxed atomic load is similar to a normal |
478 | // aligned memory read (e.g., a mov instruction in x86), but with some |
479 | // compiler optimization restrictions. On the other hand, a TLS access |
480 | // might require calling a non-inlinable function (such as `__tls_get_addr` |
481 | // when using the GD TLS model). |
482 | true |
483 | } else { |
484 | is_zero_slow_path() |
485 | } |
486 | } |
487 | |
488 | // Slow path is in a separate function to reduce the amount of code |
489 | // inlined from `count_is_zero`. |
490 | #[inline(never)] |
491 | #[cold] |
492 | fn is_zero_slow_path() -> bool { |
493 | LOCAL_PANIC_COUNT.with(|c| c.get().0 == 0) |
494 | } |
495 | } |
496 | |
497 | #[cfg(test)] |
498 | pub use realstd::rt::panic_count; |
499 | |
500 | /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. |
501 | #[cfg(feature = "panic_immediate_abort")] |
502 | pub unsafe fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> { |
503 | Ok(f()) |
504 | } |
505 | |
506 | /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. |
507 | #[cfg(not(feature = "panic_immediate_abort"))] |
508 | pub unsafe fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> { |
509 | union Data<F, R> { |
510 | f: ManuallyDrop<F>, |
511 | r: ManuallyDrop<R>, |
512 | p: ManuallyDrop<Box<dyn Any + Send>>, |
513 | } |
514 | |
515 | // We do some sketchy operations with ownership here for the sake of |
516 | // performance. We can only pass pointers down to `do_call` (can't pass |
517 | // objects by value), so we do all the ownership tracking here manually |
518 | // using a union. |
519 | // |
520 | // We go through a transition where: |
521 | // |
522 | // * First, we set the data field `f` to be the argumentless closure that we're going to call. |
523 | // * When we make the function call, the `do_call` function below, we take |
524 | // ownership of the function pointer. At this point the `data` union is |
525 | // entirely uninitialized. |
526 | // * If the closure successfully returns, we write the return value into the |
527 | // data's return slot (field `r`). |
528 | // * If the closure panics (`do_catch` below), we write the panic payload into field `p`. |
529 | // * Finally, when we come back out of the `try` intrinsic we're |
530 | // in one of two states: |
531 | // |
532 | // 1. The closure didn't panic, in which case the return value was |
533 | // filled in. We move it out of `data.r` and return it. |
534 | // 2. The closure panicked, in which case the panic payload was |
535 | // filled in. We move it out of `data.p` and return it. |
536 | // |
537 | // Once we stack all that together we should have the "most efficient' |
538 | // method of calling a catch panic whilst juggling ownership. |
539 | let mut data = Data { f: ManuallyDrop::new(f) }; |
540 | |
541 | let data_ptr = (&raw mut data) as *mut u8; |
542 | // SAFETY: |
543 | // |
544 | // Access to the union's fields: this is `std` and we know that the `catch_unwind` |
545 | // intrinsic fills in the `r` or `p` union field based on its return value. |
546 | // |
547 | // The call to `intrinsics::catch_unwind` is made safe by: |
548 | // - `do_call`, the first argument, can be called with the initial `data_ptr`. |
549 | // - `do_catch`, the second argument, can be called with the `data_ptr` as well. |
550 | // See their safety preconditions for more information |
551 | unsafe { |
552 | return if intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 { |
553 | Ok(ManuallyDrop::into_inner(data.r)) |
554 | } else { |
555 | Err(ManuallyDrop::into_inner(data.p)) |
556 | }; |
557 | } |
558 | |
559 | // We consider unwinding to be rare, so mark this function as cold. However, |
560 | // do not mark it no-inline -- that decision is best to leave to the |
561 | // optimizer (in most cases this function is not inlined even as a normal, |
562 | // non-cold function, though, as of the writing of this comment). |
563 | #[cold] |
564 | #[optimize(size)] |
565 | unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send + 'static> { |
566 | // SAFETY: The whole unsafe block hinges on a correct implementation of |
567 | // the panic handler `__rust_panic_cleanup`. As such we can only |
568 | // assume it returns the correct thing for `Box::from_raw` to work |
569 | // without undefined behavior. |
570 | let obj = unsafe { Box::from_raw(__rust_panic_cleanup(payload)) }; |
571 | panic_count::decrease(); |
572 | obj |
573 | } |
574 | |
575 | // SAFETY: |
576 | // data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>` |
577 | // Its must contains a valid `f` (type: F) value that can be use to fill |
578 | // `data.r`. |
579 | // |
580 | // This function cannot be marked as `unsafe` because `intrinsics::catch_unwind` |
581 | // expects normal function pointers. |
582 | #[inline] |
583 | fn do_call<F: FnOnce() -> R, R>(data: *mut u8) { |
584 | // SAFETY: this is the responsibility of the caller, see above. |
585 | unsafe { |
586 | let data = data as *mut Data<F, R>; |
587 | let data = &mut (*data); |
588 | let f = ManuallyDrop::take(&mut data.f); |
589 | data.r = ManuallyDrop::new(f()); |
590 | } |
591 | } |
592 | |
593 | // We *do* want this part of the catch to be inlined: this allows the |
594 | // compiler to properly track accesses to the Data union and optimize it |
595 | // away most of the time. |
596 | // |
597 | // SAFETY: |
598 | // data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>` |
599 | // Since this uses `cleanup` it also hinges on a correct implementation of |
600 | // `__rustc_panic_cleanup`. |
601 | // |
602 | // This function cannot be marked as `unsafe` because `intrinsics::catch_unwind` |
603 | // expects normal function pointers. |
604 | #[inline] |
605 | #[rustc_nounwind] // `intrinsic::catch_unwind` requires catch fn to be nounwind |
606 | fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) { |
607 | // SAFETY: this is the responsibility of the caller, see above. |
608 | // |
609 | // When `__rustc_panic_cleaner` is correctly implemented we can rely |
610 | // on `obj` being the correct thing to pass to `data.p` (after wrapping |
611 | // in `ManuallyDrop`). |
612 | unsafe { |
613 | let data = data as *mut Data<F, R>; |
614 | let data = &mut (*data); |
615 | let obj = cleanup(payload); |
616 | data.p = ManuallyDrop::new(obj); |
617 | } |
618 | } |
619 | } |
620 | |
621 | /// Determines whether the current thread is unwinding because of panic. |
622 | #[inline] |
623 | pub fn panicking() -> bool { |
624 | !panic_count::count_is_zero() |
625 | } |
626 | |
627 | /// Entry point of panics from the core crate (`panic_impl` lang item). |
628 | #[cfg(not(any(test, doctest)))] |
629 | #[panic_handler] |
630 | pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { |
631 | struct FormatStringPayload<'a> { |
632 | inner: &'a core::panic::PanicMessage<'a>, |
633 | string: Option<String>, |
634 | } |
635 | |
636 | impl FormatStringPayload<'_> { |
637 | fn fill(&mut self) -> &mut String { |
638 | let inner = self.inner; |
639 | // Lazily, the first time this gets called, run the actual string formatting. |
640 | self.string.get_or_insert_with(|| { |
641 | let mut s = String::new(); |
642 | let mut fmt = fmt::Formatter::new(&mut s, fmt::FormattingOptions::new()); |
643 | let _err = fmt::Display::fmt(&inner, &mut fmt); |
644 | s |
645 | }) |
646 | } |
647 | } |
648 | |
649 | unsafe impl PanicPayload for FormatStringPayload<'_> { |
650 | fn take_box(&mut self) -> *mut (dyn Any + Send) { |
651 | // We do two allocations here, unfortunately. But (a) they're required with the current |
652 | // scheme, and (b) we don't handle panic + OOM properly anyway (see comment in |
653 | // begin_panic below). |
654 | let contents = mem::take(self.fill()); |
655 | Box::into_raw(Box::new(contents)) |
656 | } |
657 | |
658 | fn get(&mut self) -> &(dyn Any + Send) { |
659 | self.fill() |
660 | } |
661 | } |
662 | |
663 | impl fmt::Display for FormatStringPayload<'_> { |
664 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
665 | if let Some(s) = &self.string { |
666 | f.write_str(s) |
667 | } else { |
668 | fmt::Display::fmt(&self.inner, f) |
669 | } |
670 | } |
671 | } |
672 | |
673 | struct StaticStrPayload(&'static str); |
674 | |
675 | unsafe impl PanicPayload for StaticStrPayload { |
676 | fn take_box(&mut self) -> *mut (dyn Any + Send) { |
677 | Box::into_raw(Box::new(self.0)) |
678 | } |
679 | |
680 | fn get(&mut self) -> &(dyn Any + Send) { |
681 | &self.0 |
682 | } |
683 | |
684 | fn as_str(&mut self) -> Option<&str> { |
685 | Some(self.0) |
686 | } |
687 | } |
688 | |
689 | impl fmt::Display for StaticStrPayload { |
690 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
691 | f.write_str(self.0) |
692 | } |
693 | } |
694 | |
695 | let loc = info.location().unwrap(); // The current implementation always returns Some |
696 | let msg = info.message(); |
697 | crate::sys::backtrace::__rust_end_short_backtrace(move || { |
698 | if let Some(s) = msg.as_str() { |
699 | rust_panic_with_hook( |
700 | &mut StaticStrPayload(s), |
701 | loc, |
702 | info.can_unwind(), |
703 | info.force_no_backtrace(), |
704 | ); |
705 | } else { |
706 | rust_panic_with_hook( |
707 | &mut FormatStringPayload { inner: &msg, string: None }, |
708 | loc, |
709 | info.can_unwind(), |
710 | info.force_no_backtrace(), |
711 | ); |
712 | } |
713 | }) |
714 | } |
715 | |
716 | /// This is the entry point of panicking for the non-format-string variants of |
717 | /// panic!() and assert!(). In particular, this is the only entry point that supports |
718 | /// arbitrary payloads, not just format strings. |
719 | #[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")] |
720 | #[cfg_attr(not(any(test, doctest)), lang = "begin_panic")] |
721 | // lang item for CTFE panic support |
722 | // never inline unless panic_immediate_abort to avoid code |
723 | // bloat at the call sites as much as possible |
724 | #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] |
725 | #[cfg_attr(feature = "panic_immediate_abort", inline)] |
726 | #[track_caller] |
727 | #[rustc_do_not_const_check] // hooked by const-eval |
728 | pub const fn begin_panic<M: Any + Send>(msg: M) -> ! { |
729 | if cfg!(feature = "panic_immediate_abort") { |
730 | intrinsics::abort() |
731 | } |
732 | |
733 | struct Payload<A> { |
734 | inner: Option<A>, |
735 | } |
736 | |
737 | unsafe impl<A: Send + 'static> PanicPayload for Payload<A> { |
738 | fn take_box(&mut self) -> *mut (dyn Any + Send) { |
739 | // Note that this should be the only allocation performed in this code path. Currently |
740 | // this means that panic!() on OOM will invoke this code path, but then again we're not |
741 | // really ready for panic on OOM anyway. If we do start doing this, then we should |
742 | // propagate this allocation to be performed in the parent of this thread instead of the |
743 | // thread that's panicking. |
744 | let data = match self.inner.take() { |
745 | Some(a) => Box::new(a) as Box<dyn Any + Send>, |
746 | None => process::abort(), |
747 | }; |
748 | Box::into_raw(data) |
749 | } |
750 | |
751 | fn get(&mut self) -> &(dyn Any + Send) { |
752 | match self.inner { |
753 | Some(ref a) => a, |
754 | None => process::abort(), |
755 | } |
756 | } |
757 | } |
758 | |
759 | impl<A: 'static> fmt::Display for Payload<A> { |
760 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
761 | match &self.inner { |
762 | Some(a) => f.write_str(payload_as_str(a)), |
763 | None => process::abort(), |
764 | } |
765 | } |
766 | } |
767 | |
768 | let loc = Location::caller(); |
769 | crate::sys::backtrace::__rust_end_short_backtrace(move || { |
770 | rust_panic_with_hook( |
771 | &mut Payload { inner: Some(msg) }, |
772 | loc, |
773 | /* can_unwind */ true, |
774 | /* force_no_backtrace */ false, |
775 | ) |
776 | }) |
777 | } |
778 | |
779 | fn payload_as_str(payload: &dyn Any) -> &str { |
780 | if let Some(&s: &str) = payload.downcast_ref::<&'static str>() { |
781 | s |
782 | } else if let Some(s: &String) = payload.downcast_ref::<String>() { |
783 | s.as_str() |
784 | } else { |
785 | "Box<dyn Any>" |
786 | } |
787 | } |
788 | |
789 | /// Central point for dispatching panics. |
790 | /// |
791 | /// Executes the primary logic for a panic, including checking for recursive |
792 | /// panics, panic hooks, and finally dispatching to the panic runtime to either |
793 | /// abort or unwind. |
794 | #[optimize(size)] |
795 | fn rust_panic_with_hook( |
796 | payload: &mut dyn PanicPayload, |
797 | location: &Location<'_>, |
798 | can_unwind: bool, |
799 | force_no_backtrace: bool, |
800 | ) -> ! { |
801 | let must_abort = panic_count::increase(true); |
802 | |
803 | // Check if we need to abort immediately. |
804 | if let Some(must_abort) = must_abort { |
805 | match must_abort { |
806 | panic_count::MustAbort::PanicInHook => { |
807 | // Don't try to format the message in this case, perhaps that is causing the |
808 | // recursive panics. However if the message is just a string, no user-defined |
809 | // code is involved in printing it, so that is risk-free. |
810 | let message: &str = payload.as_str().unwrap_or_default(); |
811 | rtprintpanic!( |
812 | "panicked at{location} :\n{message}\n thread panicked while processing panic. aborting.\n " |
813 | ); |
814 | } |
815 | panic_count::MustAbort::AlwaysAbort => { |
816 | // Unfortunately, this does not print a backtrace, because creating |
817 | // a `Backtrace` will allocate, which we must avoid here. |
818 | rtprintpanic!("aborting due to panic at{location} :\n{payload}\n "); |
819 | } |
820 | } |
821 | crate::sys::abort_internal(); |
822 | } |
823 | |
824 | match *HOOK.read().unwrap_or_else(PoisonError::into_inner) { |
825 | // Some platforms (like wasm) know that printing to stderr won't ever actually |
826 | // print anything, and if that's the case we can skip the default |
827 | // hook. Since string formatting happens lazily when calling `payload` |
828 | // methods, this means we avoid formatting the string at all! |
829 | // (The panic runtime might still call `payload.take_box()` though and trigger |
830 | // formatting.) |
831 | Hook::Default if panic_output().is_none() => {} |
832 | Hook::Default => { |
833 | default_hook(&PanicHookInfo::new( |
834 | location, |
835 | payload.get(), |
836 | can_unwind, |
837 | force_no_backtrace, |
838 | )); |
839 | } |
840 | Hook::Custom(ref hook) => { |
841 | hook(&PanicHookInfo::new(location, payload.get(), can_unwind, force_no_backtrace)); |
842 | } |
843 | } |
844 | |
845 | // Indicate that we have finished executing the panic hook. After this point |
846 | // it is fine if there is a panic while executing destructors, as long as it |
847 | // it contained within a `catch_unwind`. |
848 | panic_count::finished_panic_hook(); |
849 | |
850 | if !can_unwind { |
851 | // If a thread panics while running destructors or tries to unwind |
852 | // through a nounwind function (e.g. extern "C") then we cannot continue |
853 | // unwinding and have to abort immediately. |
854 | rtprintpanic!("thread caused non-unwinding panic. aborting.\n "); |
855 | crate::sys::abort_internal(); |
856 | } |
857 | |
858 | rust_panic(payload) |
859 | } |
860 | |
861 | /// This is the entry point for `resume_unwind`. |
862 | /// It just forwards the payload to the panic runtime. |
863 | #[cfg_attr(feature = "panic_immediate_abort", inline)] |
864 | pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! { |
865 | panic_count::increase(run_panic_hook:false); |
866 | |
867 | struct RewrapBox(Box<dyn Any + Send>); |
868 | |
869 | unsafe impl PanicPayload for RewrapBox { |
870 | fn take_box(&mut self) -> *mut (dyn Any + Send) { |
871 | Box::into_raw(mem::replace(&mut self.0, src:Box::new(()))) |
872 | } |
873 | |
874 | fn get(&mut self) -> &(dyn Any + Send) { |
875 | &*self.0 |
876 | } |
877 | } |
878 | |
879 | impl fmt::Display for RewrapBox { |
880 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
881 | f.write_str(data:payload_as_str(&self.0)) |
882 | } |
883 | } |
884 | |
885 | rust_panic(&mut RewrapBox(payload)) |
886 | } |
887 | |
888 | /// An unmangled function (through `rustc_std_internal_symbol`) on which to slap |
889 | /// yer breakpoints. |
890 | #[inline(never)] |
891 | #[cfg_attr(not(test), rustc_std_internal_symbol)] |
892 | #[cfg(not(feature = "panic_immediate_abort"))] |
893 | fn rust_panic(msg: &mut dyn PanicPayload) -> ! { |
894 | let code: u32 = unsafe { __rust_start_panic(payload:msg) }; |
895 | rtabort!("failed to initiate panic, error{code} ") |
896 | } |
897 | |
898 | #[cfg_attr(not(test), rustc_std_internal_symbol)] |
899 | #[cfg(feature = "panic_immediate_abort")] |
900 | fn rust_panic(_: &mut dyn PanicPayload) -> ! { |
901 | unsafe { |
902 | crate::intrinsics::abort(); |
903 | } |
904 | } |
905 |
Definitions
- __rust_panic_cleanup
- __rust_start_panic
- __rust_drop_panic
- __rust_foreign_exception
- Hook
- Default
- Custom
- into_box
- set_hook
- take_hook
- update_hook
- default_hook
- MustAbort
- AlwaysAbort
- PanicInHook
- increase
- finished_panic_hook
- decrease
- set_always_abort
- get_count
- count_is_zero
- is_zero_slow_path
- catch_unwind
- Data
- f
- r
- p
- cleanup
- do_call
- do_catch
- panicking
- begin_panic_handler
- FormatStringPayload
- inner
- string
- fill
- take_box
- get
- fmt
- StaticStrPayload
- take_box
- get
- as_str
- fmt
- begin_panic
- Payload
- inner
- take_box
- get
- fmt
- payload_as_str
- rust_panic_with_hook
- rust_panic_without_hook
- RewrapBox
- take_box
- get
- fmt
Learn Rust with the experts
Find out more