Auto merge of #3472 - RalfJung:deadlock, r=RalfJung
deadlock: show backtrace for all threads Fixes https://github.com/rust-lang/miri/issues/3424
This commit is contained in:
commit
fee494b2b2
15 changed files with 175 additions and 25 deletions
|
|
@ -438,7 +438,7 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
|
|||
.machine
|
||||
.threads
|
||||
.all_stacks()
|
||||
.flatten()
|
||||
.flat_map(|(_id, stack)| stack)
|
||||
.map(|frame| {
|
||||
frame.extra.borrow_tracker.as_ref().expect("we should have borrow tracking data")
|
||||
})
|
||||
|
|
|
|||
|
|
@ -445,11 +445,10 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
|
|||
) -> &mut Vec<Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>> {
|
||||
&mut self.threads[self.active_thread].stack
|
||||
}
|
||||
|
||||
pub fn all_stacks(
|
||||
&self,
|
||||
) -> impl Iterator<Item = &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>]> {
|
||||
self.threads.iter().map(|t| &t.stack[..])
|
||||
) -> impl Iterator<Item = (ThreadId, &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>])> {
|
||||
self.threads.iter_enumerated().map(|(id, t)| (id, &t.stack[..]))
|
||||
}
|
||||
|
||||
/// Create a new thread and returns its id.
|
||||
|
|
|
|||
|
|
@ -361,9 +361,12 @@ pub fn report_error<'tcx, 'mir>(
|
|||
};
|
||||
|
||||
let stacktrace = ecx.generate_stacktrace();
|
||||
let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
|
||||
let (stacktrace, mut any_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
|
||||
|
||||
// We want to dump the allocation if this is `InvalidUninitBytes`. Since `format_error` consumes `e`, we compute the outut early.
|
||||
let mut show_all_threads = false;
|
||||
|
||||
// We want to dump the allocation if this is `InvalidUninitBytes`.
|
||||
// Since `format_interp_error` consumes `e`, we compute the outut early.
|
||||
let mut extra = String::new();
|
||||
match e.kind() {
|
||||
UndefinedBehavior(InvalidUninitBytes(Some((alloc_id, access)))) => {
|
||||
|
|
@ -375,6 +378,15 @@ pub fn report_error<'tcx, 'mir>(
|
|||
.unwrap();
|
||||
writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap();
|
||||
}
|
||||
MachineStop(info) => {
|
||||
let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
|
||||
match info {
|
||||
TerminationInfo::Deadlock => {
|
||||
show_all_threads = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
|
@ -387,18 +399,39 @@ pub fn report_error<'tcx, 'mir>(
|
|||
vec![],
|
||||
helps,
|
||||
&stacktrace,
|
||||
Some(ecx.get_active_thread()),
|
||||
&ecx.machine,
|
||||
);
|
||||
|
||||
eprint!("{extra}"); // newlines are already in the string
|
||||
|
||||
if show_all_threads {
|
||||
for (thread, stack) in ecx.machine.threads.all_stacks() {
|
||||
if thread != ecx.get_active_thread() {
|
||||
let stacktrace = Frame::generate_stacktrace_from_stack(stack);
|
||||
let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
|
||||
any_pruned |= was_pruned;
|
||||
report_msg(
|
||||
DiagLevel::Error,
|
||||
format!("deadlock: the evaluated program deadlocked"),
|
||||
vec![format!("the evaluated program deadlocked")],
|
||||
vec![],
|
||||
vec![],
|
||||
&stacktrace,
|
||||
Some(thread),
|
||||
&ecx.machine,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Include a note like `std` does when we omit frames from a backtrace
|
||||
if was_pruned {
|
||||
if any_pruned {
|
||||
ecx.tcx.dcx().note(
|
||||
"some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
|
||||
);
|
||||
}
|
||||
|
||||
eprint!("{extra}"); // newlines are already in the string
|
||||
|
||||
// Debug-dump all locals.
|
||||
for (i, frame) in ecx.active_thread_stack().iter().enumerate() {
|
||||
trace!("-------------------");
|
||||
|
|
@ -435,6 +468,7 @@ pub fn report_leaks<'mir, 'tcx>(
|
|||
vec![],
|
||||
vec![],
|
||||
&backtrace,
|
||||
None, // we don't know the thread this is from
|
||||
&ecx.machine,
|
||||
);
|
||||
}
|
||||
|
|
@ -457,6 +491,7 @@ pub fn report_msg<'tcx>(
|
|||
notes: Vec<(Option<SpanData>, String)>,
|
||||
helps: Vec<(Option<SpanData>, String)>,
|
||||
stacktrace: &[FrameInfo<'tcx>],
|
||||
thread: Option<ThreadId>,
|
||||
machine: &MiriMachine<'_, 'tcx>,
|
||||
) {
|
||||
let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);
|
||||
|
|
@ -506,12 +541,13 @@ pub fn report_msg<'tcx>(
|
|||
if extra_span {
|
||||
write!(backtrace_title, " (of the first span)").unwrap();
|
||||
}
|
||||
let thread_name =
|
||||
machine.threads.get_thread_display_name(machine.threads.get_active_thread_id());
|
||||
if thread_name != "main" {
|
||||
// Only print thread name if it is not `main`.
|
||||
write!(backtrace_title, " on thread `{thread_name}`").unwrap();
|
||||
};
|
||||
if let Some(thread) = thread {
|
||||
let thread_name = machine.threads.get_thread_display_name(thread);
|
||||
if thread_name != "main" {
|
||||
// Only print thread name if it is not `main`.
|
||||
write!(backtrace_title, " on thread `{thread_name}`").unwrap();
|
||||
};
|
||||
}
|
||||
write!(backtrace_title, ":").unwrap();
|
||||
err.note(backtrace_title);
|
||||
for (idx, frame_info) in stacktrace.iter().enumerate() {
|
||||
|
|
@ -628,7 +664,16 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
|
|||
_ => vec![],
|
||||
};
|
||||
|
||||
report_msg(diag_level, title, vec![msg], notes, helps, &stacktrace, self);
|
||||
report_msg(
|
||||
diag_level,
|
||||
title,
|
||||
vec![msg],
|
||||
notes,
|
||||
helps,
|
||||
&stacktrace,
|
||||
Some(self.threads.get_active_thread_id()),
|
||||
self,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -654,6 +699,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
vec![],
|
||||
vec![],
|
||||
&stacktrace,
|
||||
Some(this.get_active_thread()),
|
||||
&this.machine,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//@only-target-windows: Uses win32 api functions
|
||||
// We are making scheduler assumptions here.
|
||||
//@compile-flags: -Zmiri-preemption-rate=0
|
||||
//@error-in-other-file: deadlock
|
||||
|
||||
// On windows, joining main is not UB, but it will block a thread forever.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,28 @@ LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJ
|
|||
= note: inside closure at RUSTLIB/core/src/macros/mod.rs:LL:CC
|
||||
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
|
|
||||
LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
|
||||
| ^ the evaluated program deadlocked
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> $DIR/windows_join_main.rs:LL:CC
|
||||
|
|
||||
LL | / thread::spawn(|| {
|
||||
LL | | unsafe {
|
||||
LL | | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJECT_0);
|
||||
LL | | }
|
||||
LL | | })
|
||||
LL | | .join()
|
||||
| |___________^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//@only-target-windows: Uses win32 api functions
|
||||
// We are making scheduler assumptions here.
|
||||
//@compile-flags: -Zmiri-preemption-rate=0
|
||||
//@error-in-other-file: deadlock
|
||||
|
||||
// On windows, a thread joining itself is not UB, but it will deadlock.
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,29 @@ LL | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0
|
|||
= note: BACKTRACE on thread `unnamed-ID`:
|
||||
= note: inside closure at $DIR/windows_join_self.rs:LL:CC
|
||||
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
|
|
||||
LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
|
||||
| ^ the evaluated program deadlocked
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> $DIR/windows_join_self.rs:LL:CC
|
||||
|
|
||||
LL | / thread::spawn(|| {
|
||||
LL | | unsafe {
|
||||
LL | | let native = GetCurrentThread();
|
||||
LL | | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0);
|
||||
LL | | }
|
||||
LL | | })
|
||||
LL | | .join()
|
||||
| |___________^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
//@ignore-target-windows: No libc on Windows
|
||||
//@error-in-other-file: deadlock
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::sync::Arc;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,26 @@ LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _
|
|||
= note: BACKTRACE on thread `unnamed-ID`:
|
||||
= note: inside closure at $DIR/libc_pthread_mutex_deadlock.rs:LL:CC
|
||||
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
|
|
||||
LL | let ret = libc::pthread_join(self.id, ptr::null_mut());
|
||||
| ^ the evaluated program deadlocked
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> $DIR/libc_pthread_mutex_deadlock.rs:LL:CC
|
||||
|
|
||||
LL | / thread::spawn(move || {
|
||||
LL | | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _), 0);
|
||||
LL | | })
|
||||
LL | | .join()
|
||||
| |_______________^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
//@ignore-target-windows: No libc on Windows
|
||||
//@error-in-other-file: deadlock
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::sync::Arc;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,26 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu
|
|||
= note: BACKTRACE on thread `unnamed-ID`:
|
||||
= note: inside closure at $DIR/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
|
||||
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
|
|
||||
LL | let ret = libc::pthread_join(self.id, ptr::null_mut());
|
||||
| ^ the evaluated program deadlocked
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> $DIR/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
|
||||
|
|
||||
LL | / thread::spawn(move || {
|
||||
LL | | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0);
|
||||
LL | | })
|
||||
LL | | .join()
|
||||
| |_______________^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
//@ignore-target-windows: No libc on Windows
|
||||
//@error-in-other-file: deadlock
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::sync::Arc;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,26 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu
|
|||
= note: BACKTRACE on thread `unnamed-ID`:
|
||||
= note: inside closure at $DIR/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
|
||||
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
|
|
||||
LL | let ret = libc::pthread_join(self.id, ptr::null_mut());
|
||||
| ^ the evaluated program deadlocked
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> $DIR/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
|
||||
|
|
||||
LL | / thread::spawn(move || {
|
||||
LL | | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0);
|
||||
LL | | })
|
||||
LL | | .join()
|
||||
| |_______________^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ note: inside `main`
|
|||
LL | drop(slice1.cmp(slice2));
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
Uninitialized memory occurred at ALLOC[0x4..0x10], in this allocation:
|
||||
ALLOC (Rust heap, size: 32, align: 8) {
|
||||
0x00 │ 41 42 43 44 __ __ __ __ __ __ __ __ __ __ __ __ │ ABCD░░░░░░░░░░░░
|
||||
0x10 │ 00 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ .░░░░░░░░░░░░░░░
|
||||
}
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ note: inside `main`
|
|||
LL | drop(slice1.cmp(slice2));
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
Uninitialized memory occurred at ALLOC[0x4..0x8], in this allocation:
|
||||
ALLOC (Rust heap, size: 16, align: 8) {
|
||||
╾42[ALLOC]<TAG> (1 ptr byte)╼ 12 13 ╾43[ALLOC]<TAG> (1 ptr byte)╼ __ __ __ __ __ __ __ __ __ __ __ __ │ ━..━░░░░░░░░░░░░
|
||||
|
|
@ -28,5 +26,7 @@ ALLOC (global (static or const), size: 1, align: 1) {
|
|||
00 │ .
|
||||
}
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue