diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
index aa99a14b18e6..5a941ae9d030 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
@@ -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")
})
diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs
index 06e23188332a..e28e5f836977 100644
--- a/src/tools/miri/src/concurrency/thread.rs
+++ b/src/tools/miri/src/concurrency/thread.rs
@@ -445,11 +445,10 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
) -> &mut Vec>> {
&mut self.threads[self.active_thread].stack
}
-
pub fn all_stacks(
&self,
- ) -> impl Iterator- >]> {
- self.threads.iter().map(|t| &t.stack[..])
+ ) -> impl Iterator
- >])> {
+ self.threads.iter_enumerated().map(|(id, t)| (id, &t.stack[..]))
}
/// Create a new thread and returns its id.
diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs
index 30349c003a94..dfbcaaac5c6d 100644
--- a/src/tools/miri/src/diagnostics.rs
+++ b/src/tools/miri/src/diagnostics.rs
@@ -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::().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, String)>,
helps: Vec<(Option, String)>,
stacktrace: &[FrameInfo<'tcx>],
+ thread: Option,
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,
);
}
diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs
index 910e06222ee7..532bda201364 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs
@@ -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.
diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr
index d9137ee74376..12f35fdeb02a 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr
@@ -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
diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs
index a7c8faf5a98b..a64265ca0ca5 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs
@@ -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.
diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr
index 74699a0317ff..8d26c35de8ab 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr
@@ -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
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs
index 6c3cb738e299..60d56d41fd98 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs
+++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs
@@ -1,4 +1,5 @@
//@ignore-target-windows: No libc on Windows
+//@error-in-other-file: deadlock
use std::cell::UnsafeCell;
use std::sync::Arc;
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr
index 76b1d26bd332..987d0fc4c2d0 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr
+++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr
@@ -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
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs
index 201844615e18..0f02c3231a68 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs
+++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs
@@ -1,4 +1,5 @@
//@ignore-target-windows: No libc on Windows
+//@error-in-other-file: deadlock
use std::cell::UnsafeCell;
use std::sync::Arc;
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr
index 5501dab81aca..bc9b15f293ef 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr
+++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr
@@ -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
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs
index b1d7e0492e5a..10be5b337523 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs
+++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs
@@ -1,4 +1,5 @@
//@ignore-target-windows: No libc on Windows
+//@error-in-other-file: deadlock
use std::cell::UnsafeCell;
use std::sync::Arc;
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr
index 815d85af502c..66c142bbc5c8 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr
+++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr
@@ -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
diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr
index cca17a07ec20..960cae901248 100644
--- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr
+++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr
@@ -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
diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr
index 4dc2d27ead43..5439418f2677 100644
--- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr
+++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr
@@ -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] (1 ptr byte)╼ 12 13 ╾43[ALLOC] (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