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