Skip to content

Commit 923906e

Browse files
committed
deadlock: show backtrace for all threads
1 parent 361e014 commit 923906e

9 files changed

+145
-30
lines changed

src/borrow_tracker/stacked_borrows/diagnostics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
438438
.machine
439439
.threads
440440
.all_stacks()
441-
.flatten()
441+
.flat_map(|(_id, stack)| stack)
442442
.map(|frame| {
443443
frame.extra.borrow_tracker.as_ref().expect("we should have borrow tracking data")
444444
})

src/concurrency/thread.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -430,11 +430,10 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
430430
) -> &mut Vec<Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>> {
431431
&mut self.threads[self.active_thread].stack
432432
}
433-
434433
pub fn all_stacks(
435434
&self,
436-
) -> impl Iterator<Item = &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>]> {
437-
self.threads.iter().map(|t| &t.stack[..])
435+
) -> impl Iterator<Item = (ThreadId, &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>])> {
436+
self.threads.iter_enumerated().map(|(id, t)| (id, &t.stack[..]))
438437
}
439438

440439
/// Create a new thread and returns its id.

src/diagnostics.rs

Lines changed: 79 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -361,9 +361,12 @@ pub fn report_error<'tcx, 'mir>(
361361
};
362362

363363
let stacktrace = ecx.generate_stacktrace();
364-
let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
364+
let (stacktrace, mut any_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
365365

366-
// We want to dump the allocation if this is `InvalidUninitBytes`. Since `format_error` consumes `e`, we compute the outut early.
366+
let mut show_all_threads = false;
367+
368+
// We want to dump the allocation if this is `InvalidUninitBytes`.
369+
// Since `format_interp_error` consumes `e`, we compute the outut early.
367370
let mut extra = String::new();
368371
match e.kind() {
369372
UndefinedBehavior(InvalidUninitBytes(Some((alloc_id, access)))) => {
@@ -375,6 +378,15 @@ pub fn report_error<'tcx, 'mir>(
375378
.unwrap();
376379
writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap();
377380
}
381+
MachineStop(info) => {
382+
let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
383+
match info {
384+
TerminationInfo::Deadlock => {
385+
show_all_threads = true;
386+
}
387+
_ => {}
388+
}
389+
}
378390
_ => {}
379391
}
380392

@@ -387,18 +399,39 @@ pub fn report_error<'tcx, 'mir>(
387399
vec![],
388400
helps,
389401
&stacktrace,
402+
Some(ecx.get_active_thread()),
390403
&ecx.machine,
391404
);
392405

406+
eprint!("{extra}"); // newlines are already in the string
407+
408+
if show_all_threads {
409+
for (thread, stack) in ecx.machine.threads.all_stacks() {
410+
if thread != ecx.get_active_thread() {
411+
let stacktrace = Frame::generate_stacktrace_from_stack(stack);
412+
let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
413+
any_pruned |= was_pruned;
414+
report_msg(
415+
DiagLevel::Error,
416+
format!("deadlock: the evaluated program deadlocked"),
417+
vec![format!("the evaluated program deadlocked")],
418+
vec![],
419+
vec![],
420+
&stacktrace,
421+
Some(thread),
422+
&ecx.machine,
423+
)
424+
}
425+
}
426+
}
427+
393428
// Include a note like `std` does when we omit frames from a backtrace
394-
if was_pruned {
429+
if any_pruned {
395430
ecx.tcx.dcx().note(
396431
"some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
397432
);
398433
}
399434

400-
eprint!("{extra}"); // newlines are already in the string
401-
402435
// Debug-dump all locals.
403436
for (i, frame) in ecx.active_thread_stack().iter().enumerate() {
404437
trace!("-------------------");
@@ -435,6 +468,7 @@ pub fn report_leaks<'mir, 'tcx>(
435468
vec![],
436469
vec![],
437470
&backtrace,
471+
None, // we don't know the thread this is from
438472
&ecx.machine,
439473
);
440474
}
@@ -445,6 +479,24 @@ pub fn report_leaks<'mir, 'tcx>(
445479
}
446480
}
447481

482+
fn emit_stacktrace<'tcx>(
483+
stacktrace: &[FrameInfo<'tcx>],
484+
machine: &MiriMachine<'_, 'tcx>,
485+
err: &mut Diag<'_, ()>,
486+
) {
487+
for (idx, frame_info) in stacktrace.iter().enumerate() {
488+
let is_local = machine.is_local(frame_info);
489+
// No span for non-local frames and the first frame (which is the error site).
490+
if is_local && idx > 0 {
491+
err.subdiagnostic(err.dcx, frame_info.as_note(machine.tcx));
492+
} else {
493+
let sm = machine.tcx.sess.source_map();
494+
let span = sm.span_to_embeddable_string(frame_info.span);
495+
err.note(format!("{frame_info} at {span}"));
496+
}
497+
}
498+
}
499+
448500
/// Report an error or note (depending on the `error` argument) with the given stacktrace.
449501
/// Also emits a full stacktrace of the interpreter stack.
450502
/// We want to present a multi-line span message for some errors. Diagnostics do not support this
@@ -457,6 +509,7 @@ pub fn report_msg<'tcx>(
457509
notes: Vec<(Option<SpanData>, String)>,
458510
helps: Vec<(Option<SpanData>, String)>,
459511
stacktrace: &[FrameInfo<'tcx>],
512+
thread: Option<ThreadId>,
460513
machine: &MiriMachine<'_, 'tcx>,
461514
) {
462515
let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);
@@ -502,29 +555,22 @@ pub fn report_msg<'tcx>(
502555
}
503556

504557
// Add backtrace
558+
// Compute backtrace title.
505559
let mut backtrace_title = String::from("BACKTRACE");
506560
if extra_span {
507561
write!(backtrace_title, " (of the first span)").unwrap();
508562
}
509-
let thread_name =
510-
machine.threads.get_thread_display_name(machine.threads.get_active_thread_id());
511-
if thread_name != "main" {
512-
// Only print thread name if it is not `main`.
513-
write!(backtrace_title, " on thread `{thread_name}`").unwrap();
514-
};
563+
if let Some(thread) = thread {
564+
let thread_name = machine.threads.get_thread_display_name(thread);
565+
if thread_name != "main" {
566+
// Only print thread name if it is not `main`.
567+
write!(backtrace_title, " on thread `{thread_name}`").unwrap();
568+
};
569+
}
515570
write!(backtrace_title, ":").unwrap();
516571
err.note(backtrace_title);
517-
for (idx, frame_info) in stacktrace.iter().enumerate() {
518-
let is_local = machine.is_local(frame_info);
519-
// No span for non-local frames and the first frame (which is the error site).
520-
if is_local && idx > 0 {
521-
err.subdiagnostic(err.dcx, frame_info.as_note(machine.tcx));
522-
} else {
523-
let sm = sess.source_map();
524-
let span = sm.span_to_embeddable_string(frame_info.span);
525-
err.note(format!("{frame_info} at {span}"));
526-
}
527-
}
572+
// Add backtrace itself.
573+
emit_stacktrace(stacktrace, machine, &mut err);
528574

529575
err.emit();
530576
}
@@ -628,7 +674,16 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
628674
_ => vec![],
629675
};
630676

631-
report_msg(diag_level, title, vec![msg], notes, helps, &stacktrace, self);
677+
report_msg(
678+
diag_level,
679+
title,
680+
vec![msg],
681+
notes,
682+
helps,
683+
&stacktrace,
684+
Some(self.threads.get_active_thread_id()),
685+
self,
686+
);
632687
}
633688
}
634689

@@ -654,6 +709,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
654709
vec![],
655710
vec![],
656711
&stacktrace,
712+
Some(this.get_active_thread()),
657713
&this.machine,
658714
);
659715
}

tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//@ignore-target-windows: No libc on Windows
2+
//@error-in-other-file: deadlock
23

34
use std::cell::UnsafeCell;
45
use std::sync::Arc;

tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,26 @@ LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _
77
= note: BACKTRACE on thread `unnamed-ID`:
88
= note: inside closure at $DIR/libc_pthread_mutex_deadlock.rs:LL:CC
99

10+
error: deadlock: the evaluated program deadlocked
11+
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
12+
|
13+
LL | let ret = libc::pthread_join(self.id, ptr::null_mut());
14+
| ^ the evaluated program deadlocked
15+
|
16+
= note: BACKTRACE:
17+
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
18+
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
19+
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
20+
note: inside `main`
21+
--> $DIR/libc_pthread_mutex_deadlock.rs:LL:CC
22+
|
23+
LL | / thread::spawn(move || {
24+
LL | | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _), 0);
25+
LL | | })
26+
LL | | .join()
27+
| |_______________^
28+
1029
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
1130

12-
error: aborting due to 1 previous error
31+
error: aborting due to 2 previous errors
1332

tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//@ignore-target-windows: No libc on Windows
2+
//@error-in-other-file: deadlock
23

34
use std::cell::UnsafeCell;
45
use std::sync::Arc;

tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,26 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu
77
= note: BACKTRACE on thread `unnamed-ID`:
88
= note: inside closure at $DIR/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
99

10+
error: deadlock: the evaluated program deadlocked
11+
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
12+
|
13+
LL | let ret = libc::pthread_join(self.id, ptr::null_mut());
14+
| ^ the evaluated program deadlocked
15+
|
16+
= note: BACKTRACE:
17+
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
18+
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
19+
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
20+
note: inside `main`
21+
--> $DIR/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
22+
|
23+
LL | / thread::spawn(move || {
24+
LL | | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0);
25+
LL | | })
26+
LL | | .join()
27+
| |_______________^
28+
1029
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
1130

12-
error: aborting due to 1 previous error
31+
error: aborting due to 2 previous errors
1332

tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//@ignore-target-windows: No libc on Windows
2+
//@error-in-other-file: deadlock
23

34
use std::cell::UnsafeCell;
45
use std::sync::Arc;

tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,26 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu
77
= note: BACKTRACE on thread `unnamed-ID`:
88
= note: inside closure at $DIR/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
99

10+
error: deadlock: the evaluated program deadlocked
11+
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
12+
|
13+
LL | let ret = libc::pthread_join(self.id, ptr::null_mut());
14+
| ^ the evaluated program deadlocked
15+
|
16+
= note: BACKTRACE:
17+
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
18+
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
19+
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
20+
note: inside `main`
21+
--> $DIR/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
22+
|
23+
LL | / thread::spawn(move || {
24+
LL | | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0);
25+
LL | | })
26+
LL | | .join()
27+
| |_______________^
28+
1029
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
1130

12-
error: aborting due to 1 previous error
31+
error: aborting due to 2 previous errors
1332

0 commit comments

Comments
 (0)