Skip to content

Commit 32053f7

Browse files
authored
Rollup merge of #115280 - RalfJung:panic-cleanup-triple-backtrace, r=Amanieu
avoid triple-backtrace due to panic-during-cleanup Supersedes #115020 Cc #114954 r? ``@Amanieu``
2 parents 9fb586c + 1087e90 commit 32053f7

File tree

10 files changed

+90
-31
lines changed

10 files changed

+90
-31
lines changed

library/alloc/src/alloc.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -408,9 +408,10 @@ pub mod __alloc_error_handler {
408408
if unsafe { __rust_alloc_error_handler_should_panic != 0 } {
409409
panic!("memory allocation of {size} bytes failed")
410410
} else {
411-
core::panicking::panic_nounwind_fmt(format_args!(
412-
"memory allocation of {size} bytes failed"
413-
))
411+
core::panicking::panic_nounwind_fmt(
412+
format_args!("memory allocation of {size} bytes failed"),
413+
/* force_no_backtrace */ false,
414+
)
414415
}
415416
}
416417
}

library/core/src/panic/panic_info.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub struct PanicInfo<'a> {
2828
message: Option<&'a fmt::Arguments<'a>>,
2929
location: &'a Location<'a>,
3030
can_unwind: bool,
31+
force_no_backtrace: bool,
3132
}
3233

3334
impl<'a> PanicInfo<'a> {
@@ -42,9 +43,10 @@ impl<'a> PanicInfo<'a> {
4243
message: Option<&'a fmt::Arguments<'a>>,
4344
location: &'a Location<'a>,
4445
can_unwind: bool,
46+
force_no_backtrace: bool,
4547
) -> Self {
4648
struct NoPayload;
47-
PanicInfo { location, message, payload: &NoPayload, can_unwind }
49+
PanicInfo { location, message, payload: &NoPayload, can_unwind, force_no_backtrace }
4850
}
4951

5052
#[unstable(
@@ -141,6 +143,17 @@ impl<'a> PanicInfo<'a> {
141143
pub fn can_unwind(&self) -> bool {
142144
self.can_unwind
143145
}
146+
147+
#[unstable(
148+
feature = "panic_internals",
149+
reason = "internal details of the implementation of the `panic!` and related macros",
150+
issue = "none"
151+
)]
152+
#[doc(hidden)]
153+
#[inline]
154+
pub fn force_no_backtrace(&self) -> bool {
155+
self.force_no_backtrace
156+
}
144157
}
145158

146159
#[stable(feature = "panic_hook_display", since = "1.26.0")]

library/core/src/panicking.rs

+29-8
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
6161
fn panic_impl(pi: &PanicInfo<'_>) -> !;
6262
}
6363

64-
let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), true);
64+
let pi = PanicInfo::internal_constructor(
65+
Some(&fmt),
66+
Location::caller(),
67+
/* can_unwind */ true,
68+
/* force_no_backtrace */ false,
69+
);
6570

6671
// SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
6772
unsafe { panic_impl(&pi) }
@@ -77,7 +82,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
7782
// and unwinds anyway, we will hit the "unwinding out of nounwind function" guard,
7883
// which causes a "panic in a function that cannot unwind".
7984
#[rustc_nounwind]
80-
pub fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>) -> ! {
85+
pub fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! {
8186
if cfg!(feature = "panic_immediate_abort") {
8287
super::intrinsics::abort()
8388
}
@@ -90,7 +95,12 @@ pub fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>) -> ! {
9095
}
9196

9297
// PanicInfo with the `can_unwind` flag set to false forces an abort.
93-
let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), false);
98+
let pi = PanicInfo::internal_constructor(
99+
Some(&fmt),
100+
Location::caller(),
101+
/* can_unwind */ false,
102+
force_no_backtrace,
103+
);
94104

95105
// SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
96106
unsafe { panic_impl(&pi) }
@@ -123,7 +133,15 @@ pub const fn panic(expr: &'static str) -> ! {
123133
#[lang = "panic_nounwind"] // needed by codegen for non-unwinding panics
124134
#[rustc_nounwind]
125135
pub fn panic_nounwind(expr: &'static str) -> ! {
126-
panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]));
136+
panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ false);
137+
}
138+
139+
/// Like `panic_nounwind`, but also inhibits showing a backtrace.
140+
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
141+
#[cfg_attr(feature = "panic_immediate_abort", inline)]
142+
#[rustc_nounwind]
143+
pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! {
144+
panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ true);
127145
}
128146

129147
#[inline]
@@ -172,9 +190,12 @@ fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! {
172190
super::intrinsics::abort()
173191
}
174192

175-
panic_nounwind_fmt(format_args!(
176-
"misaligned pointer dereference: address must be a multiple of {required:#x} but is {found:#x}"
177-
))
193+
panic_nounwind_fmt(
194+
format_args!(
195+
"misaligned pointer dereference: address must be a multiple of {required:#x} but is {found:#x}"
196+
),
197+
/* force_no_backtrace */ false,
198+
)
178199
}
179200

180201
/// Panic because we cannot unwind out of a function.
@@ -205,7 +226,7 @@ fn panic_cannot_unwind() -> ! {
205226
#[rustc_nounwind]
206227
fn panic_in_cleanup() -> ! {
207228
// Keep the text in sync with `UnwindTerminateReason::as_str` in `rustc_middle`.
208-
panic_nounwind("panic in a destructor during cleanup")
229+
panic_nounwind_nobacktrace("panic in a destructor during cleanup")
209230
}
210231

211232
/// This function is used instead of panic_fmt in const eval.

library/std/src/panicking.rs

+30-6
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,9 @@ fn default_hook(info: &PanicInfo<'_>) {
246246
pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path::Path>) {
247247
// If this is a double panic, make sure that we print a backtrace
248248
// for this panic. Otherwise only print it if logging is enabled.
249-
let backtrace = if panic_count::get_count() >= 2 {
249+
let backtrace = if info.force_no_backtrace() {
250+
None
251+
} else if panic_count::get_count() >= 2 {
250252
BacktraceStyle::full()
251253
} else {
252254
crate::panic::get_backtrace_style()
@@ -294,7 +296,7 @@ pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path
294296
}
295297
}
296298
}
297-
// If backtraces aren't supported, do nothing.
299+
// If backtraces aren't supported or are forced-off, do nothing.
298300
None => {}
299301
}
300302
};
@@ -615,14 +617,23 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
615617
let loc = info.location().unwrap(); // The current implementation always returns Some
616618
let msg = info.message().unwrap(); // The current implementation always returns Some
617619
crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
620+
// FIXME: can we just pass `info` along rather than taking it apart here, only to have
621+
// `rust_panic_with_hook` construct a new `PanicInfo`?
618622
if let Some(msg) = msg.as_str() {
619-
rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc, info.can_unwind());
623+
rust_panic_with_hook(
624+
&mut StrPanicPayload(msg),
625+
info.message(),
626+
loc,
627+
info.can_unwind(),
628+
info.force_no_backtrace(),
629+
);
620630
} else {
621631
rust_panic_with_hook(
622632
&mut PanicPayload::new(msg),
623633
info.message(),
624634
loc,
625635
info.can_unwind(),
636+
info.force_no_backtrace(),
626637
);
627638
}
628639
})
@@ -647,7 +658,13 @@ pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
647658

648659
let loc = Location::caller();
649660
return crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
650-
rust_panic_with_hook(&mut PanicPayload::new(msg), None, loc, true)
661+
rust_panic_with_hook(
662+
&mut PanicPayload::new(msg),
663+
None,
664+
loc,
665+
/* can_unwind */ true,
666+
/* force_no_backtrace */ false,
667+
)
651668
});
652669

653670
struct PanicPayload<A> {
@@ -693,6 +710,7 @@ fn rust_panic_with_hook(
693710
message: Option<&fmt::Arguments<'_>>,
694711
location: &Location<'_>,
695712
can_unwind: bool,
713+
force_no_backtrace: bool,
696714
) -> ! {
697715
let must_abort = panic_count::increase(true);
698716

@@ -707,14 +725,20 @@ fn rust_panic_with_hook(
707725
panic_count::MustAbort::AlwaysAbort => {
708726
// Unfortunately, this does not print a backtrace, because creating
709727
// a `Backtrace` will allocate, which we must to avoid here.
710-
let panicinfo = PanicInfo::internal_constructor(message, location, can_unwind);
728+
let panicinfo = PanicInfo::internal_constructor(
729+
message,
730+
location,
731+
can_unwind,
732+
force_no_backtrace,
733+
);
711734
rtprintpanic!("{panicinfo}\npanicked after panic::always_abort(), aborting.\n");
712735
}
713736
}
714737
crate::sys::abort_internal();
715738
}
716739

717-
let mut info = PanicInfo::internal_constructor(message, location, can_unwind);
740+
let mut info =
741+
PanicInfo::internal_constructor(message, location, can_unwind, force_no_backtrace);
718742
let hook = HOOK.read().unwrap_or_else(PoisonError::into_inner);
719743
match *hook {
720744
// Some platforms (like wasm) know that printing to stderr won't ever actually

src/tools/miri/tests/fail/panic/double_panic.stderr

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ second
66
stack backtrace:
77
thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
88
panic in a destructor during cleanup
9-
stack backtrace:
109
thread caused non-unwinding panic. aborting.
1110
error: abnormal termination: the program aborted execution
1211
--> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC
@@ -19,7 +18,7 @@ LL | ABORT();
1918
= note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
2019
= 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
2120
= note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC
22-
= note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC
21+
= note: inside `core::panicking::panic_nounwind_nobacktrace` at RUSTLIB/core/src/panicking.rs:LL:CC
2322
= note: inside `core::panicking::panic_in_cleanup` at RUSTLIB/core/src/panicking.rs:LL:CC
2423
note: inside `main`
2524
--> $DIR/double_panic.rs:LL:CC

tests/ui/backtrace.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ fn runtest(me: &str) {
9494
#[cfg(not(panic = "abort"))]
9595
{
9696
// Make sure a stack trace is printed
97-
let p = template(me).arg("double-fail").spawn().unwrap();
97+
let p = template(me).arg("double-fail").env("RUST_BACKTRACE","0").spawn().unwrap();
9898
let out = p.wait_with_output().unwrap();
9999
assert!(!out.status.success());
100100
let s = str::from_utf8(&out.stderr).unwrap();
@@ -106,18 +106,18 @@ fn runtest(me: &str) {
106106
contains_verbose_expected(s, "double"),
107107
"bad output3: {}", s
108108
);
109+
// Make sure it's only one stack trace.
110+
assert_eq!(s.split("stack backtrace").count(), 2);
109111

110112
// Make sure a stack trace isn't printed too many times
111-
//
112-
// Currently it is printed 3 times ("once", "twice" and "panic in a destructor during
113-
// cleanup") but in the future the last one may be removed.
113+
// even with RUST_BACKTRACE=1. It should be printed twice.
114114
let p = template(me).arg("double-fail")
115115
.env("RUST_BACKTRACE", "1").spawn().unwrap();
116116
let out = p.wait_with_output().unwrap();
117117
assert!(!out.status.success());
118118
let s = str::from_utf8(&out.stderr).unwrap();
119119
let mut i = 0;
120-
for _ in 0..3 {
120+
for _ in 0..2 {
121121
i += s[i + 10..].find("stack backtrace").unwrap() + 10;
122122
}
123123
assert!(s[i + 10..].find("stack backtrace").is_none(),

tests/ui/panics/panic-in-cleanup.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// error-pattern: panic in a destructor during cleanup
55
// normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> ""
66
// normalize-stderr-test: "\n +at [^\n]+" -> ""
7+
// normalize-stderr-test: "(core/src/panicking\.rs):[0-9]+:[0-9]+" -> "$1:$$LINE:$$COL"
78
// needs-unwind
89
// ignore-emscripten "RuntimeError" junk in output
910
// ignore-msvc SEH doesn't do panic-during-cleanup the same way as everyone else
+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
thread 'main' panicked at $DIR/panic-in-cleanup.rs:21:5:
1+
thread 'main' panicked at $DIR/panic-in-cleanup.rs:22:5:
22
explicit panic
33
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
4-
thread 'main' panicked at $DIR/panic-in-cleanup.rs:15:9:
4+
thread 'main' panicked at $DIR/panic-in-cleanup.rs:16:9:
55
BOOM
66
stack backtrace:
7-
thread 'main' panicked at library/core/src/panicking.rs:126:5:
7+
thread 'main' panicked at library/core/src/panicking.rs:$LINE:$COL:
88
panic in a destructor during cleanup
9-
stack backtrace:
109
thread caused non-unwinding panic. aborting.

tests/ui/panics/panic-in-ffi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// error-pattern: panic in a function that cannot unwind
55
// normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> ""
66
// normalize-stderr-test: "\n +at [^\n]+" -> ""
7+
// normalize-stderr-test: "(core/src/panicking\.rs):[0-9]+:[0-9]+" -> "$1:$$LINE:$$COL"
78
// needs-unwind
89
// ignore-emscripten "RuntimeError" junk in output
910
#![feature(c_unwind)]
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
thread 'main' panicked at $DIR/panic-in-ffi.rs:12:5:
1+
thread 'main' panicked at $DIR/panic-in-ffi.rs:13:5:
22
Test
33
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
4-
thread 'main' panicked at library/core/src/panicking.rs:126:5:
4+
thread 'main' panicked at library/core/src/panicking.rs:$LINE:$COL:
55
panic in a function that cannot unwind
66
stack backtrace:
77
thread caused non-unwinding panic. aborting.

0 commit comments

Comments
 (0)