1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
//! `panic!` 宏的各个部分以及相关运行时块的实现。
//!
//!
//! 具体来说,该模块包含以下内容的实现:
//!
//! * Panic hooks
//! * 执行 panic 直到完成实际实现
//! * "try" 周围的垫片
#![deny(unsafe_op_in_unsafe_fn)]
use crate::panic::BacktraceStyle;
use core::panic::{BoxMeUp, Location, PanicInfo};
use crate::any::Any;
use crate::fmt;
use crate::intrinsics;
use crate::mem::{self, ManuallyDrop};
use crate::process;
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sync::{PoisonError, RwLock};
use crate::sys::stdio::panic_output;
use crate::sys_common::backtrace;
use crate::sys_common::thread_info;
use crate::thread;
#[cfg(not(test))]
use crate::io::set_output_capture;
// 确保在 std 的真实副本中使用 libtest 配置的 stderr 输出
//
#[cfg(test)]
use realstd::io::set_output_capture;
// 标准库所依赖的 panic 运行时的二进制接口。
//
// 标准库用 `#![needs_panic_runtime]` 标记 (在 RFC 1513 中引入),以表明它需要一些其他用 `#![panic_runtime]` 标记的 crate 才能存在。
// 每个 panic 运行时都旨在实现这些符号 (具有相同的签名),因此我们可以对其进行匹配。
//
// 有一天,编译器可以帮助连接这些函数,这可能会看起来不那么特别,但今天不会了!
//
//
//
//
#[allow(improper_ctypes)]
extern "C" {
fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
}
extern "Rust" {
/// `BoxMeUp` 仅在需要时延迟执行分配 (这避免了使用 "abort" panic 运行时进行分配)。
///
fn __rust_start_panic(payload: &mut dyn BoxMeUp) -> u32;
}
/// 如果 FFI 代码捕获到 Rust panic 但不将其抛出,则 panic 运行时将调用此函数。
/// 我们不支持这种情况,因为它与我们的 panic 计数弄混了。
///
#[cfg(not(test))]
#[rustc_std_internal_symbol]
extern "C" fn __rust_drop_panic() -> ! {
rtabort!("Rust panics must be rethrown");
}
/// 如果 panic 运行时捕获到一个与 Rust panic 不对应的异常对象,则该函数由 panic 运行时调用。
///
#[cfg(not(test))]
#[rustc_std_internal_symbol]
extern "C" fn __rust_foreign_exception() -> ! {
rtabort!("Rust cannot catch foreign exceptions");
}
enum Hook {
Default,
Custom(Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>),
}
impl Hook {
#[inline]
fn into_box(self) -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
match self {
Hook::Default => Box::new(default_hook),
Hook::Custom(hook) => hook,
}
}
}
impl Default for Hook {
#[inline]
fn default() -> Hook {
Hook::Default
}
}
static HOOK: RwLock<Hook> = RwLock::new(Hook::Default);
/// 注册一个自定义的 panic hook,替换之前注册的 hook。
///
/// 当线程发生 panic 时,但在调用 panic 运行时之前,会调用 panic 钩子。这样,钩子将与中止和展开运行时一起运行。
///
/// 默认钩子在启动时注册,将消息打印到标准错误并在请求时生成回溯。可以使用 `set_hook` 函数自定义此行为。
/// 可以在使用 [`take_hook`] 函数恢复默认钩子的同时检索当前钩子。
///
/// [`take_hook`]: ./fn.take_hook.html
///
/// 该钩子提供了一个 `PanicInfo` 结构体,该结构体包含有关 panic 的起源的信息,包括传递给 `panic!` 的有效载荷和包含 panic 起源的源代码位置。
///
///
/// panic 钩子是一个全局资源。
///
/// # Panics
///
/// 如果从 panic 线程调用,就会出现 panic。
///
/// # Examples
///
/// 以下将打印 "Custom panic hook":
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|_| {
/// println!("Custom panic hook");
/// }));
///
/// panic!("Normal panic");
/// ```
///
///
///
///
///
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) {
if thread::panicking() {
panic!("cannot modify the panic hook from a panicking thread");
}
let new = Hook::Custom(hook);
let mut hook = HOOK.write().unwrap_or_else(PoisonError::into_inner);
let old = mem::replace(&mut *hook, new);
drop(hook);
// 仅在释放锁后丢弃旧的钩子,以避免在其析构函数 panic 时发生死锁。
//
drop(old);
}
/// 注销当前的 panic 钩子,并返回它,在其位置注册默认,钩子。
///
///
/// *另请参见函数 [`set_hook`]。*
///
/// [`set_hook`]: ./fn.set_hook.html
///
/// 如果默认钩子已注册,它将被返回,但保持注册状态。
///
/// # Panics
///
/// 如果从 panic 线程调用,就会出现 panic。
///
/// # Examples
///
/// 以下将打印 "Normal panic":
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|_| {
/// println!("Custom panic hook");
/// }));
///
/// let _ = panic::take_hook();
///
/// panic!("Normal panic");
/// ```
#[must_use]
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
if thread::panicking() {
panic!("cannot modify the panic hook from a panicking thread");
}
let mut hook = HOOK.write().unwrap_or_else(PoisonError::into_inner);
let old_hook = mem::take(&mut *hook);
drop(hook);
old_hook.into_box()
}
/// [`take_hook`] 和 [`set_hook`] 的原子组合。
/// 使用它来用一个新的 panic 处理程序替换 panic 处理程序,该处理程序会做一些事情然后执行旧的处理程序。
///
/// [`take_hook`]: ./fn.take_hook.html
/// [`set_hook`]: ./fn.set_hook.html
///
/// # Panics
///
/// 如果从 panic 线程调用,就会出现 panic。
///
/// # Examples
///
/// 下面会打印自定义消息,然后正常输出 panic。
///
/// ```should_panic
/// #![feature(panic_update_hook)]
/// use std::panic;
///
/// // 相当于
/// // let prev = panic::take_hook();
/// // panic::set_hook(move |info| {
/// // println!("...");
/// // prev(info);
/// // );
/// panic::update_hook(move |prev, info| {
/// println!("Print custom message and execute panic handler as usual");
/// prev(info);
/// });
///
/// panic!("Custom and then normal");
/// ```
#[unstable(feature = "panic_update_hook", issue = "92649")]
pub fn update_hook<F>(hook_fn: F)
where
F: Fn(&(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static), &PanicInfo<'_>)
+ Sync
+ Send
+ 'static,
{
if thread::panicking() {
panic!("cannot modify the panic hook from a panicking thread");
}
let mut hook = HOOK.write().unwrap_or_else(PoisonError::into_inner);
let prev = mem::take(&mut *hook).into_box();
*hook = Hook::Custom(Box::new(move |info| hook_fn(&prev, info)));
}
fn default_hook(info: &PanicInfo<'_>) {
// 如果这是 panic 的两倍,请确保我们为此 panic 打印回溯。
// 否则,仅在启用日志记录的情况下才打印它。
let backtrace = if panic_count::get_count() >= 2 {
BacktraceStyle::full()
} else {
crate::panic::get_backtrace_style()
};
// 当前实现始终返回 `Some`。
let location = info.location().unwrap();
let msg = match info.payload().downcast_ref::<&'static str>() {
Some(s) => *s,
None => match info.payload().downcast_ref::<String>() {
Some(s) => &s[..],
None => "Box<dyn Any>",
},
};
let thread = thread_info::current_thread();
let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
let write = |err: &mut dyn crate::io::Write| {
let _ = writeln!(err, "thread '{name}' panicked at '{msg}', {location}");
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
match backtrace {
Some(BacktraceStyle::Short) => {
drop(backtrace::print(err, crate::backtrace_rs::PrintFmt::Short))
}
Some(BacktraceStyle::Full) => {
drop(backtrace::print(err, crate::backtrace_rs::PrintFmt::Full))
}
Some(BacktraceStyle::Off) => {
if FIRST_PANIC.swap(false, Ordering::SeqCst) {
let _ = writeln!(
err,
"note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace"
);
}
}
// 如果不支持回溯,则什么也不做。
None => {}
}
};
if let Some(local) = set_output_capture(None) {
write(&mut *local.lock().unwrap_or_else(|e| e.into_inner()));
set_output_capture(Some(local));
} else if let Some(mut out) = panic_output() {
write(&mut out);
}
}
#[cfg(not(test))]
#[doc(hidden)]
#[unstable(feature = "update_panic_count", issue = "none")]
pub mod panic_count {
use crate::cell::Cell;
use crate::sync::atomic::{AtomicUsize, Ordering};
pub const ALWAYS_ABORT_FLAG: usize = 1 << (usize::BITS - 1);
/// 强制立即中止 panic 的原因。
#[derive(Debug)]
pub enum MustAbort {
AlwaysAbort,
PanicInHook,
}
// 当前线程的 panic 计数以及当前是否正在执行 panic 钩子。
//
thread_local! {
static LOCAL_PANIC_COUNT: Cell<(usize, bool)> = const { Cell::new((0, false)) }
}
// 所有线程的 panic 计数总和。这样做的目的是在 `count_is_zero` (`panicking` 使用) 中有一个快速路径。
// 在任何特定线程中,如果该线程当前将 `GLOBAL_PANIC_COUNT` 视为零,则该线程中的 `LOCAL_PANIC_COUNT` 为零。
// 该不变在增加和减少之前和之后均成立,但不一定在其执行期间成立。
//
// 此外,GLOBAL_PANIC_COUNT (GLOBAL_ALWAYS_ABORT_FLAG) 的最高位记录 panic::always_abort () 是否已被调用。这只能设置,永远不会清除。
// panic::always_abort () 通常被调用来防止由 `libc::fork` 创建的子进程中的 panic 处理完成的内存分配。
// 在大多数操作系统中,在使用 `libc::fork` 创建的子进程中执行的内存分配是未定义的行为。
// 在 `libc::fork` 创建的子节点中访问 LOCAL_PANIC_COUNT 会导致内存分配。在这种情况下,只能访问 GLOBAL_PANIC_COUNT。
// 这已经足够了,因为子进程总是只有一个线程。
// 有关详细信息,另请参见 #85261。
//
// 这可以看作是一个包含一个位和一个 n-1 位值的结构体,但是如果我们这样写它就不仅仅是一个单词,甚至一个围绕 usize 的 newtype 也会很笨拙,因为我们需要原子.
//
// 但是我们使用这样一个元组作为 increase() 的返回类型。
//
// 窃取一点是可以的,因为它只是假设每个 panicking 线程至少消耗 2 字节的地址空间。
//
//
//
//
//
//
//
//
//
static GLOBAL_PANIC_COUNT: AtomicUsize = AtomicUsize::new(0);
// 增加全局和局部 panic 计数,并返回是否需要立即中止。
//
// 这也会更新线程本地状态以跟踪当前是否正在执行 panic hook。
//
//
pub fn increase(run_panic_hook: bool) -> Option<MustAbort> {
let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed);
if global_count & ALWAYS_ABORT_FLAG != 0 {
return Some(MustAbort::AlwaysAbort);
}
LOCAL_PANIC_COUNT.with(|c| {
let (count, in_panic_hook) = c.get();
if in_panic_hook {
return Some(MustAbort::PanicInHook);
}
c.set((count + 1, run_panic_hook));
None
})
}
pub fn finished_panic_hook() {
LOCAL_PANIC_COUNT.with(|c| {
let (count, _) = c.get();
c.set((count, false));
});
}
pub fn decrease() {
GLOBAL_PANIC_COUNT.fetch_sub(1, Ordering::Relaxed);
LOCAL_PANIC_COUNT.with(|c| {
let (count, _) = c.get();
c.set((count - 1, false));
});
}
pub fn set_always_abort() {
GLOBAL_PANIC_COUNT.fetch_or(ALWAYS_ABORT_FLAG, Ordering::Relaxed);
}
// 忽略 ALWAYS_ABORT_FLAG
#[must_use]
pub fn get_count() -> usize {
LOCAL_PANIC_COUNT.with(|c| c.get().0)
}
// 忽略 ALWAYS_ABORT_FLAG
#[must_use]
#[inline]
pub fn count_is_zero() -> bool {
if GLOBAL_PANIC_COUNT.load(Ordering::Relaxed) & !ALWAYS_ABORT_FLAG == 0 {
// 快速路径:如果 `GLOBAL_PANIC_COUNT` 为零,则所有线程 (包括当前线程) 的 `LOCAL_PANIC_COUNT` 均等于零,因此可以避免 TLS 访问。
//
// 在性能方面,宽松的原子负载类似于正常对齐的内存读取 (例如,x86 中的 mov 指令),但有一些编译器优化限制。
// 另一方面,TLS 访问可能需要调用不可插入的函数 (例如,使用 GD TLS 模型时为 `__tls_get_addr`)。
//
//
//
//
//
true
} else {
is_zero_slow_path()
}
}
// 慢路径在一个单独的函数中,以减少从 `count_is_zero` 内联的代码量。
//
#[inline(never)]
#[cold]
fn is_zero_slow_path() -> bool {
LOCAL_PANIC_COUNT.with(|c| c.get().0 == 0)
}
}
#[cfg(test)]
pub use realstd::rt::panic_count;
/// 调用一个闭包,如果发生,请捕获展开 panic 的原因。
pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
union Data<F, R> {
f: ManuallyDrop<F>,
r: ManuallyDrop<R>,
p: ManuallyDrop<Box<dyn Any + Send>>,
}
// 为了性能起见,我们在此处使用所有权进行一些粗略的操作。
// 我们只能将指针向下传递到 `do_call` (不能按值传递对象),所以我们在此处使用 union 进行手动的所有的所有权跟踪。
//
//
// 我们经历了一个过渡,其中:
//
// * 首先,我们将数据字段 `f` 设置为要调用的无参数闭包。
// * 在进行下面的 `do_call` 函数的函数调用时,我们将拥有函数指针的所有权。
// 此时,`data` union 完全没有初始化。
// * 如果闭包成功返回,则将返回值写入数据的返回插槽 (字段 `r`)。
// * 如果闭包 panics (下面的 `do_catch`),我们将 panic 有效载荷写入字段 `p`。
// * 最后,当我们从 `try` 内部函数返回时,我们处于以下两种状态之一:
//
// 1. 闭包不是 panic,在这种情况下将填写返回值。我们将其移出 `data.r` 并返回。
// 2. 闭包发生 panic 后,在这种情况下,panic 有效载荷会被填充。我们将其移出 `data.p` 并返回。
//
// 一旦我们把所有这些都堆在一起,我们就有了最有效的方法,可以在兼顾所有权的同时触发 panic。
//
//
//
//
//
//
//
let mut data = Data { f: ManuallyDrop::new(f) };
let data_ptr = &mut data as *mut _ as *mut u8;
// SAFETY:
//
// 访问 union 的字段:这是 `std`,我们知道 `r#try` 内部函数根据其返回值填充 `r` 或 `p` union 字段。
//
//
// 通过以下方法可以安全地调用 `intrinsics::r#try`:
// - `do_call`,第一个参数,可以用初始的 `data_ptr` 调用。
// - `do_catch`,第二个参数,也可以用 `data_ptr` 调用。
// 有关更多信息,请参见他们的安全先决条件
unsafe {
return if intrinsics::r#try(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
Ok(ManuallyDrop::into_inner(data.r))
} else {
Err(ManuallyDrop::into_inner(data.p))
};
}
// 我们认为展开很少见,因此将此函数标记为 `cold`。
// 但是,请勿将其标记为非内联 - 最好将决定权留给优化器 (在撰写此评论之时,大多数情况下,即使作为普通的非冷函数,也不会内联此函数)。
//
//
#[cold]
unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send + 'static> {
// SAFETY: 整个不安全块取决于 panic 处理程序 `__rust_panic_cleanup` 的正确实现。
// 因此,我们只能假设它为 `Box::from_raw` 工作时返回正确的东西,而没有未定义的行为。
//
//
let obj = unsafe { Box::from_raw(__rust_panic_cleanup(payload)) };
panic_count::decrease();
obj
}
// SAFETY:
// 数据必须为非 NUL,正确对齐且指向 `Data<F, R>` 的指针,其必须包含可用于填充 `data.r` 的有效 `f` (类型: F) 值。
//
//
// 此函数不能标记为 `unsafe`,因为 `intrinsics::r#try` 需要正常的函数指针。
//
//
#[inline]
fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
// SAFETY: 这是调用者的责任,见上文。
unsafe {
let data = data as *mut Data<F, R>;
let data = &mut (*data);
let f = ManuallyDrop::take(&mut data.f);
data.r = ManuallyDrop::new(f());
}
}
// 我们 *确实* 希望这部分的 catch 被内联:这允许编译器正确跟踪对 Data union 的访问,并在大多数情况下对其进行优化。
//
// SAFETY:
// 数据必须为非 NUL,正确对齐并指向 `Data<F, R>` 的指针。由于它使用 `cleanup`,因此还取决于 `__rustc_panic_cleanup` 的正确实现。
//
//
// 此函数不能标记为 `unsafe`,因为 `intrinsics::r#try` 需要正常的函数指针。
//
//
//
//
#[inline]
#[rustc_nounwind] // `intrinsic::r#try` 要求 catch fn 为 nounwind
fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) {
// SAFETY: 这是调用者的责任,见上文。
//
// 正确实现 `__rustc_panic_cleaner` 后,我们可以依靠 `obj` 作为传递给 `data.p` 的正确对象 (在 `ManuallyDrop` 中包装)。
//
//
unsafe {
let data = data as *mut Data<F, R>;
let data = &mut (*data);
let obj = cleanup(payload);
data.p = ManuallyDrop::new(obj);
}
}
}
/// 确定当前线程是否由于 panic 而处于展开状态。
#[inline]
pub fn panicking() -> bool {
!panic_count::count_is_zero()
}
/// 来自 core crate (`panic_impl` lang 项) 的 panic 入口点。
#[cfg(not(test))]
#[panic_handler]
pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
struct PanicPayload<'a> {
inner: &'a fmt::Arguments<'a>,
string: Option<String>,
}
impl<'a> PanicPayload<'a> {
fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> {
PanicPayload { inner, string: None }
}
fn fill(&mut self) -> &mut String {
use crate::fmt::Write;
let inner = self.inner;
// 懒惰的是,第一次调用此方法时,请运行实际的字符串格式。
self.string.get_or_insert_with(|| {
let mut s = String::new();
let _err = s.write_fmt(*inner);
s
})
}
}
unsafe impl<'a> BoxMeUp for PanicPayload<'a> {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
// 不幸的是,我们在这里做了两个分配。
// 但是 (a) 是当前方案所必需的,而 (b) 我们无论如何都无法正确处理 panic + OOM (请参见下面 begin_panic 中的注释)。
//
let contents = mem::take(self.fill());
Box::into_raw(Box::new(contents))
}
fn get(&mut self) -> &(dyn Any + Send) {
self.fill()
}
}
struct StrPanicPayload(&'static str);
unsafe impl BoxMeUp for StrPanicPayload {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
Box::into_raw(Box::new(self.0))
}
fn get(&mut self) -> &(dyn Any + Send) {
&self.0
}
}
let loc = info.location().unwrap(); // 当前的实现总是返回 Some
let msg = info.message().unwrap(); // 当前的实现总是返回 Some
crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
if let Some(msg) = msg.as_str() {
rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc, info.can_unwind());
} else {
rust_panic_with_hook(
&mut PanicPayload::new(msg),
info.message(),
loc,
info.can_unwind(),
);
}
})
}
/// 这是 panic!() 和 assert!() 的非格式字符串变体 panic 的入口点。
/// 特别是,这是唯一支持任意的有效载荷的入口点,而不仅仅是格式字符串。
///
#[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")]
#[cfg_attr(not(test), lang = "begin_panic")]
// 除非 panic_immediate_abort 尽可能避免调用站点上的代码膨胀,否则 CTFE panic 支持的 lang 项永远不会内联
//
//
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
#[rustc_do_not_const_check] // 被 const-eval 钩住了
pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
if cfg!(feature = "panic_immediate_abort") {
intrinsics::abort()
}
let loc = Location::caller();
return crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
rust_panic_with_hook(&mut PanicPayload::new(msg), None, loc, true)
});
struct PanicPayload<A> {
inner: Option<A>,
}
impl<A: Send + 'static> PanicPayload<A> {
fn new(inner: A) -> PanicPayload<A> {
PanicPayload { inner: Some(inner) }
}
}
unsafe impl<A: Send + 'static> BoxMeUp for PanicPayload<A> {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
// 请注意,这应该是在此代码路径中执行的唯一分配。
// 当前,这意味着 OOM 上的 panic! () 将调用此代码路径,但是无论如何,我们仍然没有真正为 OOM 上的 panic 做好准备。
// 如果确实开始执行此操作,则应将此分配传播给要在此线程的父级中执行,而不是在发生 panic 的线程中执行。
//
//
let data = match self.inner.take() {
Some(a) => Box::new(a) as Box<dyn Any + Send>,
None => process::abort(),
};
Box::into_raw(data)
}
fn get(&mut self) -> &(dyn Any + Send) {
match self.inner {
Some(ref a) => a,
None => process::abort(),
}
}
}
}
/// 调度 panics 的中心点。
///
/// 执行 panic 的主要逻辑,包括检查递归 panic,panic 钩子,最后将其分发到 panic 运行时以中止或 unwind。
///
///
fn rust_panic_with_hook(
payload: &mut dyn BoxMeUp,
message: Option<&fmt::Arguments<'_>>,
location: &Location<'_>,
can_unwind: bool,
) -> ! {
let must_abort = panic_count::increase(true);
// 检查我们是否需要立即中止。
if let Some(must_abort) = must_abort {
match must_abort {
panic_count::MustAbort::PanicInHook => {
// 在这种情况下不要尝试打印消息
// - 也许这导致了递归的 panics。
rtprintpanic!("thread panicked while processing panic. aborting.\n");
}
panic_count::MustAbort::AlwaysAbort => {
// 不幸的是,这不会打印回溯,因为创建 `Backtrace` 将分配,我们必须在这里避免。
//
let panicinfo = PanicInfo::internal_constructor(message, location, can_unwind);
rtprintpanic!("{panicinfo}\npanicked after panic::always_abort(), aborting.\n");
}
}
crate::sys::abort_internal();
}
let mut info = PanicInfo::internal_constructor(message, location, can_unwind);
let hook = HOOK.read().unwrap_or_else(PoisonError::into_inner);
match *hook {
// 一些平台 (例如 wasm) 知道打印到 stderr 实际上不会打印任何内容,如果是这种情况,我们可以跳过默认的钩子。
// 由于字符串格式化在调用 `payload` 方法时发生延迟,这意味着我们完全避免格式化字符串!
// (不过,panic 运行时可能仍然会调用 `payload.take_box()` 并触发格式化。)
//
//
//
Hook::Default if panic_output().is_none() => {}
Hook::Default => {
info.set_payload(payload.get());
default_hook(&info);
}
Hook::Custom(ref hook) => {
info.set_payload(payload.get());
hook(&info);
}
};
drop(hook);
// 表明我们已经完成了 panic hook 的执行。
// 在这一点之后,如果在执行析构函数时出现 panic 就没有问题,只要它包含在 `catch_unwind` 中即可。
//
panic_count::finished_panic_hook();
if !can_unwind {
// 如果一个线程在运行析构函数或尝试通过 nounwind 函数 (例如
// extern "C") 那么我们不能继续展开,必须立即中止。
//
rtprintpanic!("thread caused non-unwinding panic. aborting.\n");
crate::sys::abort_internal();
}
rust_panic(payload)
}
/// 这是 `resume_unwind` 的入口点。
/// 它只是将有效载荷转发到 panic 运行时。
pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! {
panic_count::increase(false);
struct RewrapBox(Box<dyn Any + Send>);
unsafe impl BoxMeUp for RewrapBox {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
Box::into_raw(mem::replace(&mut self.0, Box::new(())))
}
fn get(&mut self) -> &(dyn Any + Send) {
&*self.0
}
}
rust_panic(&mut RewrapBox(payload))
}
/// 一个无残缺的函数 (通过 `rustc_std_internal_symbol`),可以在其上施加断点。
///
#[inline(never)]
#[cfg_attr(not(test), rustc_std_internal_symbol)]
fn rust_panic(msg: &mut dyn BoxMeUp) -> ! {
let code = unsafe { __rust_start_panic(msg) };
rtabort!("failed to initiate panic, error {code}")
}