Skip to content

Commit 6645ca1

Browse files
committed
Auto merge of #28631 - ranma42:robust-panic, r=alexcrichton
This is mainly to avoid infinite recursion and make debugging more convenient in the anomalous case in which `on_panic` panics. I encountered such issues while changing libstd to debug/fix part of #28129. While writing this I was wondering about which functions belong to `panicking` and which to `unwind`. I placed them in this way mostly because of convenience, but I would strongly appreciate guidance.
2 parents 7ebfd85 + 54c0231 commit 6645ca1

File tree

2 files changed

+54
-41
lines changed

2 files changed

+54
-41
lines changed

src/libstd/panicking.rs

+50-23
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,24 @@ use prelude::v1::*;
1212
use io::prelude::*;
1313

1414
use any::Any;
15+
use cell::Cell;
1516
use cell::RefCell;
17+
use intrinsics;
1618
use sys::stdio::Stderr;
1719
use sys_common::backtrace;
1820
use sys_common::thread_info;
19-
use sys_common::unwind;
21+
use sys_common::util;
22+
23+
thread_local! { pub static PANIC_COUNT: Cell<usize> = Cell::new(0) }
2024

2125
thread_local! {
2226
pub static LOCAL_STDERR: RefCell<Option<Box<Write + Send>>> = {
2327
RefCell::new(None)
2428
}
2529
}
2630

27-
pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
31+
fn log_panic(obj: &(Any+Send), file: &'static str, line: u32,
32+
log_backtrace: bool) {
2833
let msg = match obj.downcast_ref::<&'static str>() {
2934
Some(s) => *s,
3035
None => match obj.downcast_ref::<String>() {
@@ -35,37 +40,59 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
3540
let mut err = Stderr::new().ok();
3641
let thread = thread_info::current_thread();
3742
let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
43+
44+
let write = |err: &mut ::io::Write| {
45+
let _ = writeln!(err, "thread '{}' panicked at '{}', {}:{}",
46+
name, msg, file, line);
47+
if log_backtrace {
48+
let _ = backtrace::write(err);
49+
}
50+
};
51+
3852
let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take());
3953
match (prev, err.as_mut()) {
4054
(Some(mut stderr), _) => {
41-
// FIXME: what to do when the thread printing panics?
42-
let _ = writeln!(stderr,
43-
"thread '{}' panicked at '{}', {}:{}\n",
44-
name, msg, file, line);
45-
if backtrace::log_enabled() {
46-
let _ = backtrace::write(&mut *stderr);
47-
}
55+
write(&mut *stderr);
4856
let mut s = Some(stderr);
4957
LOCAL_STDERR.with(|slot| {
5058
*slot.borrow_mut() = s.take();
5159
});
5260
}
53-
(None, Some(ref mut err)) => {
54-
let _ = writeln!(err, "thread '{}' panicked at '{}', {}:{}",
55-
name, msg, file, line);
56-
if backtrace::log_enabled() {
57-
let _ = backtrace::write(err);
58-
}
59-
}
61+
(None, Some(ref mut err)) => { write(err) }
6062
_ => {}
6163
}
64+
}
6265

63-
// If this is a double panic, make sure that we printed a backtrace
64-
// for this panic.
65-
match err {
66-
Some(ref mut err) if unwind::panicking() && !backtrace::log_enabled() => {
67-
let _ = backtrace::write(err);
68-
}
69-
_ => {}
66+
pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
67+
let panics = PANIC_COUNT.with(|s| {
68+
let count = s.get() + 1;
69+
s.set(count);
70+
count
71+
});
72+
73+
// If this is the third nested call, on_panic triggered the last panic,
74+
// otherwise the double-panic check would have aborted the process.
75+
// Even if it is likely that on_panic was unable to log the backtrace,
76+
// abort immediately to avoid infinite recursion, so that attaching a
77+
// debugger provides a useable stacktrace.
78+
if panics >= 3 {
79+
util::dumb_print(format_args!("thread panicked while processing \
80+
panic. aborting."));
81+
unsafe { intrinsics::abort() }
82+
}
83+
84+
// If this is a double panic, make sure that we print a backtrace
85+
// for this panic. Otherwise only print it if logging is enabled.
86+
let log_backtrace = panics >= 2 || backtrace::log_enabled();
87+
log_panic(obj, file, line, log_backtrace);
88+
89+
if panics >= 2 {
90+
// If a thread panics while it's already unwinding then we
91+
// have limited options. Currently our preference is to
92+
// just abort. In the future we may consider resuming
93+
// unwinding or otherwise exiting the thread cleanly.
94+
util::dumb_print(format_args!("thread panicked while panicking. \
95+
aborting."));
96+
unsafe { intrinsics::abort() }
7097
}
7198
}

src/libstd/sys/common/unwind/mod.rs

+4-18
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,8 @@ use prelude::v1::*;
6464

6565
use any::Any;
6666
use boxed;
67-
use cell::Cell;
6867
use cmp;
69-
use panicking;
68+
use panicking::{self,PANIC_COUNT};
7069
use fmt;
7170
use intrinsics;
7271
use mem;
@@ -92,8 +91,6 @@ pub mod imp;
9291
#[path = "gcc.rs"] #[doc(hidden)]
9392
pub mod imp;
9493

95-
thread_local! { static PANICKING: Cell<bool> = Cell::new(false) }
96-
9794
/// Invoke a closure, capturing the cause of panic if one occurs.
9895
///
9996
/// This function will return `Ok(())` if the closure did not panic, and will
@@ -131,9 +128,9 @@ pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
131128
// care of exposing correctly.
132129
unsafe fn inner_try(f: fn(*mut u8), data: *mut u8)
133130
-> Result<(), Box<Any + Send>> {
134-
PANICKING.with(|s| {
131+
PANIC_COUNT.with(|s| {
135132
let prev = s.get();
136-
s.set(false);
133+
s.set(0);
137134
let ep = intrinsics::try(f, data);
138135
s.set(prev);
139136
if ep.is_null() {
@@ -161,7 +158,7 @@ pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
161158

162159
/// Determines whether the current thread is unwinding because of panic.
163160
pub fn panicking() -> bool {
164-
PANICKING.with(|s| s.get())
161+
PANIC_COUNT.with(|s| s.get() != 0)
165162
}
166163

167164
// An uninlined, unmangled function upon which to slap yer breakpoints
@@ -234,17 +231,6 @@ fn begin_unwind_inner(msg: Box<Any + Send>,
234231
// First, invoke the default panic handler.
235232
panicking::on_panic(&*msg, file, line);
236233

237-
if panicking() {
238-
// If a thread panics while it's already unwinding then we
239-
// have limited options. Currently our preference is to
240-
// just abort. In the future we may consider resuming
241-
// unwinding or otherwise exiting the thread cleanly.
242-
super::util::dumb_print(format_args!("thread panicked while panicking. \
243-
aborting."));
244-
unsafe { intrinsics::abort() }
245-
}
246-
PANICKING.with(|s| s.set(true));
247-
248234
// Finally, perform the unwinding.
249235
rust_panic(msg);
250236
}

0 commit comments

Comments
 (0)