Rollup merge of #115280 - RalfJung:panic-cleanup-triple-backtrace, r=Amanieu

avoid triple-backtrace due to panic-during-cleanup

Supersedes https://github.com/rust-lang/rust/pull/115020
Cc https://github.com/rust-lang/rust/issues/114954
r? ``@Amanieu``
This commit is contained in:
Matthias Krüger 2023-08-28 08:13:59 +02:00 committed by GitHub
commit 32053f7602
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 90 additions and 31 deletions

View File

@ -408,9 +408,10 @@ pub mod __alloc_error_handler {
if unsafe { __rust_alloc_error_handler_should_panic != 0 } { if unsafe { __rust_alloc_error_handler_should_panic != 0 } {
panic!("memory allocation of {size} bytes failed") panic!("memory allocation of {size} bytes failed")
} else { } else {
core::panicking::panic_nounwind_fmt(format_args!( core::panicking::panic_nounwind_fmt(
"memory allocation of {size} bytes failed" format_args!("memory allocation of {size} bytes failed"),
)) /* force_no_backtrace */ false,
)
} }
} }
} }

View File

@ -28,6 +28,7 @@ pub struct PanicInfo<'a> {
message: Option<&'a fmt::Arguments<'a>>, message: Option<&'a fmt::Arguments<'a>>,
location: &'a Location<'a>, location: &'a Location<'a>,
can_unwind: bool, can_unwind: bool,
force_no_backtrace: bool,
} }
impl<'a> PanicInfo<'a> { impl<'a> PanicInfo<'a> {
@ -42,9 +43,10 @@ impl<'a> PanicInfo<'a> {
message: Option<&'a fmt::Arguments<'a>>, message: Option<&'a fmt::Arguments<'a>>,
location: &'a Location<'a>, location: &'a Location<'a>,
can_unwind: bool, can_unwind: bool,
force_no_backtrace: bool,
) -> Self { ) -> Self {
struct NoPayload; struct NoPayload;
PanicInfo { location, message, payload: &NoPayload, can_unwind } PanicInfo { location, message, payload: &NoPayload, can_unwind, force_no_backtrace }
} }
#[unstable( #[unstable(
@ -141,6 +143,17 @@ impl<'a> PanicInfo<'a> {
pub fn can_unwind(&self) -> bool { pub fn can_unwind(&self) -> bool {
self.can_unwind self.can_unwind
} }
#[unstable(
feature = "panic_internals",
reason = "internal details of the implementation of the `panic!` and related macros",
issue = "none"
)]
#[doc(hidden)]
#[inline]
pub fn force_no_backtrace(&self) -> bool {
self.force_no_backtrace
}
} }
#[stable(feature = "panic_hook_display", since = "1.26.0")] #[stable(feature = "panic_hook_display", since = "1.26.0")]

View File

@ -61,7 +61,12 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
fn panic_impl(pi: &PanicInfo<'_>) -> !; fn panic_impl(pi: &PanicInfo<'_>) -> !;
} }
let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), true); let pi = PanicInfo::internal_constructor(
Some(&fmt),
Location::caller(),
/* can_unwind */ true,
/* force_no_backtrace */ false,
);
// SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call. // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
unsafe { panic_impl(&pi) } unsafe { panic_impl(&pi) }
@ -77,7 +82,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
// and unwinds anyway, we will hit the "unwinding out of nounwind function" guard, // and unwinds anyway, we will hit the "unwinding out of nounwind function" guard,
// which causes a "panic in a function that cannot unwind". // which causes a "panic in a function that cannot unwind".
#[rustc_nounwind] #[rustc_nounwind]
pub fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>) -> ! { pub fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! {
if cfg!(feature = "panic_immediate_abort") { if cfg!(feature = "panic_immediate_abort") {
super::intrinsics::abort() super::intrinsics::abort()
} }
@ -90,7 +95,12 @@ pub fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>) -> ! {
} }
// PanicInfo with the `can_unwind` flag set to false forces an abort. // PanicInfo with the `can_unwind` flag set to false forces an abort.
let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), false); let pi = PanicInfo::internal_constructor(
Some(&fmt),
Location::caller(),
/* can_unwind */ false,
force_no_backtrace,
);
// SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call. // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
unsafe { panic_impl(&pi) } unsafe { panic_impl(&pi) }
@ -123,7 +133,15 @@ pub const fn panic(expr: &'static str) -> ! {
#[lang = "panic_nounwind"] // needed by codegen for non-unwinding panics #[lang = "panic_nounwind"] // needed by codegen for non-unwinding panics
#[rustc_nounwind] #[rustc_nounwind]
pub fn panic_nounwind(expr: &'static str) -> ! { pub fn panic_nounwind(expr: &'static str) -> ! {
panic_nounwind_fmt(fmt::Arguments::new_const(&[expr])); panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ false);
}
/// Like `panic_nounwind`, but also inhibits showing a backtrace.
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[rustc_nounwind]
pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! {
panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ true);
} }
#[inline] #[inline]
@ -172,9 +190,12 @@ fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! {
super::intrinsics::abort() super::intrinsics::abort()
} }
panic_nounwind_fmt(format_args!( panic_nounwind_fmt(
"misaligned pointer dereference: address must be a multiple of {required:#x} but is {found:#x}" format_args!(
)) "misaligned pointer dereference: address must be a multiple of {required:#x} but is {found:#x}"
),
/* force_no_backtrace */ false,
)
} }
/// Panic because we cannot unwind out of a function. /// Panic because we cannot unwind out of a function.
@ -205,7 +226,7 @@ fn panic_cannot_unwind() -> ! {
#[rustc_nounwind] #[rustc_nounwind]
fn panic_in_cleanup() -> ! { fn panic_in_cleanup() -> ! {
// Keep the text in sync with `UnwindTerminateReason::as_str` in `rustc_middle`. // Keep the text in sync with `UnwindTerminateReason::as_str` in `rustc_middle`.
panic_nounwind("panic in a destructor during cleanup") panic_nounwind_nobacktrace("panic in a destructor during cleanup")
} }
/// This function is used instead of panic_fmt in const eval. /// This function is used instead of panic_fmt in const eval.

View File

@ -246,7 +246,9 @@ fn default_hook(info: &PanicInfo<'_>) {
pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path::Path>) { pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path::Path>) {
// If this is a double panic, make sure that we print a backtrace // If this is a double panic, make sure that we print a backtrace
// for this panic. Otherwise only print it if logging is enabled. // for this panic. Otherwise only print it if logging is enabled.
let backtrace = if panic_count::get_count() >= 2 { let backtrace = if info.force_no_backtrace() {
None
} else if panic_count::get_count() >= 2 {
BacktraceStyle::full() BacktraceStyle::full()
} else { } else {
crate::panic::get_backtrace_style() crate::panic::get_backtrace_style()
@ -294,7 +296,7 @@ pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path
} }
} }
} }
// If backtraces aren't supported, do nothing. // If backtraces aren't supported or are forced-off, do nothing.
None => {} None => {}
} }
}; };
@ -615,14 +617,23 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
let loc = info.location().unwrap(); // The current implementation always returns Some let loc = info.location().unwrap(); // The current implementation always returns Some
let msg = info.message().unwrap(); // The current implementation always returns Some let msg = info.message().unwrap(); // The current implementation always returns Some
crate::sys_common::backtrace::__rust_end_short_backtrace(move || { crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
// FIXME: can we just pass `info` along rather than taking it apart here, only to have
// `rust_panic_with_hook` construct a new `PanicInfo`?
if let Some(msg) = msg.as_str() { if let Some(msg) = msg.as_str() {
rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc, info.can_unwind()); rust_panic_with_hook(
&mut StrPanicPayload(msg),
info.message(),
loc,
info.can_unwind(),
info.force_no_backtrace(),
);
} else { } else {
rust_panic_with_hook( rust_panic_with_hook(
&mut PanicPayload::new(msg), &mut PanicPayload::new(msg),
info.message(), info.message(),
loc, loc,
info.can_unwind(), info.can_unwind(),
info.force_no_backtrace(),
); );
} }
}) })
@ -647,7 +658,13 @@ pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
let loc = Location::caller(); let loc = Location::caller();
return crate::sys_common::backtrace::__rust_end_short_backtrace(move || { return crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
rust_panic_with_hook(&mut PanicPayload::new(msg), None, loc, true) rust_panic_with_hook(
&mut PanicPayload::new(msg),
None,
loc,
/* can_unwind */ true,
/* force_no_backtrace */ false,
)
}); });
struct PanicPayload<A> { struct PanicPayload<A> {
@ -693,6 +710,7 @@ fn rust_panic_with_hook(
message: Option<&fmt::Arguments<'_>>, message: Option<&fmt::Arguments<'_>>,
location: &Location<'_>, location: &Location<'_>,
can_unwind: bool, can_unwind: bool,
force_no_backtrace: bool,
) -> ! { ) -> ! {
let must_abort = panic_count::increase(true); let must_abort = panic_count::increase(true);
@ -707,14 +725,20 @@ fn rust_panic_with_hook(
panic_count::MustAbort::AlwaysAbort => { panic_count::MustAbort::AlwaysAbort => {
// Unfortunately, this does not print a backtrace, because creating // Unfortunately, this does not print a backtrace, because creating
// a `Backtrace` will allocate, which we must to avoid here. // a `Backtrace` will allocate, which we must to avoid here.
let panicinfo = PanicInfo::internal_constructor(message, location, can_unwind); let panicinfo = PanicInfo::internal_constructor(
message,
location,
can_unwind,
force_no_backtrace,
);
rtprintpanic!("{panicinfo}\npanicked after panic::always_abort(), aborting.\n"); rtprintpanic!("{panicinfo}\npanicked after panic::always_abort(), aborting.\n");
} }
} }
crate::sys::abort_internal(); crate::sys::abort_internal();
} }
let mut info = PanicInfo::internal_constructor(message, location, can_unwind); let mut info =
PanicInfo::internal_constructor(message, location, can_unwind, force_no_backtrace);
let hook = HOOK.read().unwrap_or_else(PoisonError::into_inner); let hook = HOOK.read().unwrap_or_else(PoisonError::into_inner);
match *hook { match *hook {
// Some platforms (like wasm) know that printing to stderr won't ever actually // Some platforms (like wasm) know that printing to stderr won't ever actually

View File

@ -6,7 +6,6 @@ second
stack backtrace: stack backtrace:
thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
panic in a destructor during cleanup panic in a destructor during cleanup
stack backtrace:
thread caused non-unwinding panic. aborting. thread caused non-unwinding panic. aborting.
error: abnormal termination: the program aborted execution error: abnormal termination: the program aborted execution
--> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC --> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC
@ -19,7 +18,7 @@ LL | ABORT();
= note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
= note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::panicking::begin_panic_handler::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::panicking::begin_panic_handler::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC
= note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC
= note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_nounwind_nobacktrace` at RUSTLIB/core/src/panicking.rs:LL:CC
= note: inside `core::panicking::panic_in_cleanup` at RUSTLIB/core/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_in_cleanup` at RUSTLIB/core/src/panicking.rs:LL:CC
note: inside `main` note: inside `main`
--> $DIR/double_panic.rs:LL:CC --> $DIR/double_panic.rs:LL:CC

View File

@ -94,7 +94,7 @@ fn runtest(me: &str) {
#[cfg(not(panic = "abort"))] #[cfg(not(panic = "abort"))]
{ {
// Make sure a stack trace is printed // Make sure a stack trace is printed
let p = template(me).arg("double-fail").spawn().unwrap(); let p = template(me).arg("double-fail").env("RUST_BACKTRACE","0").spawn().unwrap();
let out = p.wait_with_output().unwrap(); let out = p.wait_with_output().unwrap();
assert!(!out.status.success()); assert!(!out.status.success());
let s = str::from_utf8(&out.stderr).unwrap(); let s = str::from_utf8(&out.stderr).unwrap();
@ -106,18 +106,18 @@ fn runtest(me: &str) {
contains_verbose_expected(s, "double"), contains_verbose_expected(s, "double"),
"bad output3: {}", s "bad output3: {}", s
); );
// Make sure it's only one stack trace.
assert_eq!(s.split("stack backtrace").count(), 2);
// Make sure a stack trace isn't printed too many times // Make sure a stack trace isn't printed too many times
// // even with RUST_BACKTRACE=1. It should be printed twice.
// Currently it is printed 3 times ("once", "twice" and "panic in a destructor during
// cleanup") but in the future the last one may be removed.
let p = template(me).arg("double-fail") let p = template(me).arg("double-fail")
.env("RUST_BACKTRACE", "1").spawn().unwrap(); .env("RUST_BACKTRACE", "1").spawn().unwrap();
let out = p.wait_with_output().unwrap(); let out = p.wait_with_output().unwrap();
assert!(!out.status.success()); assert!(!out.status.success());
let s = str::from_utf8(&out.stderr).unwrap(); let s = str::from_utf8(&out.stderr).unwrap();
let mut i = 0; let mut i = 0;
for _ in 0..3 { for _ in 0..2 {
i += s[i + 10..].find("stack backtrace").unwrap() + 10; i += s[i + 10..].find("stack backtrace").unwrap() + 10;
} }
assert!(s[i + 10..].find("stack backtrace").is_none(), assert!(s[i + 10..].find("stack backtrace").is_none(),

View File

@ -4,6 +4,7 @@
// error-pattern: panic in a destructor during cleanup // error-pattern: panic in a destructor during cleanup
// normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" // normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> ""
// normalize-stderr-test: "\n +at [^\n]+" -> "" // normalize-stderr-test: "\n +at [^\n]+" -> ""
// normalize-stderr-test: "(core/src/panicking\.rs):[0-9]+:[0-9]+" -> "$1:$$LINE:$$COL"
// needs-unwind // needs-unwind
// ignore-emscripten "RuntimeError" junk in output // ignore-emscripten "RuntimeError" junk in output
// ignore-msvc SEH doesn't do panic-during-cleanup the same way as everyone else // ignore-msvc SEH doesn't do panic-during-cleanup the same way as everyone else

View File

@ -1,10 +1,9 @@
thread 'main' panicked at $DIR/panic-in-cleanup.rs:21:5: thread 'main' panicked at $DIR/panic-in-cleanup.rs:22:5:
explicit panic explicit panic
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at $DIR/panic-in-cleanup.rs:15:9: thread 'main' panicked at $DIR/panic-in-cleanup.rs:16:9:
BOOM BOOM
stack backtrace: stack backtrace:
thread 'main' panicked at library/core/src/panicking.rs:126:5: thread 'main' panicked at library/core/src/panicking.rs:$LINE:$COL:
panic in a destructor during cleanup panic in a destructor during cleanup
stack backtrace:
thread caused non-unwinding panic. aborting. thread caused non-unwinding panic. aborting.

View File

@ -4,6 +4,7 @@
// error-pattern: panic in a function that cannot unwind // error-pattern: panic in a function that cannot unwind
// normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" // normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> ""
// normalize-stderr-test: "\n +at [^\n]+" -> "" // normalize-stderr-test: "\n +at [^\n]+" -> ""
// normalize-stderr-test: "(core/src/panicking\.rs):[0-9]+:[0-9]+" -> "$1:$$LINE:$$COL"
// needs-unwind // needs-unwind
// ignore-emscripten "RuntimeError" junk in output // ignore-emscripten "RuntimeError" junk in output
#![feature(c_unwind)] #![feature(c_unwind)]

View File

@ -1,7 +1,7 @@
thread 'main' panicked at $DIR/panic-in-ffi.rs:12:5: thread 'main' panicked at $DIR/panic-in-ffi.rs:13:5:
Test Test
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at library/core/src/panicking.rs:126:5: thread 'main' panicked at library/core/src/panicking.rs:$LINE:$COL:
panic in a function that cannot unwind panic in a function that cannot unwind
stack backtrace: stack backtrace:
thread caused non-unwinding panic. aborting. thread caused non-unwinding panic. aborting.