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