Skip to content

Commit 9272474

Browse files
committed
Auto merge of #3338 - RalfJung:more-tracking-and-threads, r=RalfJung
print thread name in miri error backtraces; add option to track read/write accesses This came up while debugging rust-lang/rust#121626. It didn't end up being useful there but still seems like good tools to have around.
2 parents f79ea81 + 61b58bd commit 9272474

File tree

132 files changed

+257
-184
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

132 files changed

+257
-184
lines changed

src/bin/miri.rs

+2
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,8 @@ fn main() {
534534
),
535535
};
536536
miri_config.tracked_alloc_ids.extend(ids);
537+
} else if arg == "-Zmiri-track-alloc-accesses" {
538+
miri_config.track_alloc_accesses = true;
537539
} else if let Some(param) = arg.strip_prefix("-Zmiri-compare-exchange-weak-failure-rate=") {
538540
let rate = match param.parse::<f64>() {
539541
Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate,

src/borrow_tracker/mod.rs

-7
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,6 @@ impl VisitProvenance for GlobalStateInner {
122122
/// We need interior mutable access to the global state.
123123
pub type GlobalState = RefCell<GlobalStateInner>;
124124

125-
/// Indicates which kind of access is being performed.
126-
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
127-
pub enum AccessKind {
128-
Read,
129-
Write,
130-
}
131-
132125
impl fmt::Display for AccessKind {
133126
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134127
match self {

src/borrow_tracker/stacked_borrows/diagnostics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_data_structures::fx::FxHashSet;
55
use rustc_span::{Span, SpanData};
66
use rustc_target::abi::Size;
77

8-
use crate::borrow_tracker::{AccessKind, GlobalStateInner, ProtectorKind};
8+
use crate::borrow_tracker::{GlobalStateInner, ProtectorKind};
99
use crate::*;
1010

1111
/// Error reporting

src/borrow_tracker/stacked_borrows/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_target::abi::{Abi, Size};
1616

1717
use crate::borrow_tracker::{
1818
stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder},
19-
AccessKind, GlobalStateInner, ProtectorKind,
19+
GlobalStateInner, ProtectorKind,
2020
};
2121
use crate::*;
2222

src/borrow_tracker/tree_borrows/diagnostics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::borrow_tracker::tree_borrows::{
99
tree::LocationState,
1010
unimap::UniIndex,
1111
};
12-
use crate::borrow_tracker::{AccessKind, ProtectorKind};
12+
use crate::borrow_tracker::ProtectorKind;
1313
use crate::*;
1414

1515
/// Cause of an access: either a real access or one

src/borrow_tracker/tree_borrows/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_target::abi::{Abi, Size};
22

3-
use crate::borrow_tracker::{AccessKind, GlobalState, GlobalStateInner, ProtectorKind};
3+
use crate::borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind};
44
use rustc_middle::{
55
mir::{Mutability, RetagKind},
66
ty::{

src/borrow_tracker/tree_borrows/perms.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fmt;
33

44
use crate::borrow_tracker::tree_borrows::diagnostics::TransitionError;
55
use crate::borrow_tracker::tree_borrows::tree::AccessRelatedness;
6-
use crate::borrow_tracker::AccessKind;
6+
use crate::AccessKind;
77

88
/// The activation states of a pointer.
99
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]

src/borrow_tracker/tree_borrows/tree.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::borrow_tracker::tree_borrows::{
2424
unimap::{UniEntry, UniIndex, UniKeyMap, UniValMap},
2525
Permission,
2626
};
27-
use crate::borrow_tracker::{AccessKind, GlobalState, ProtectorKind};
27+
use crate::borrow_tracker::{GlobalState, ProtectorKind};
2828
use crate::*;
2929

3030
mod tests;

src/concurrency/data_race.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1673,8 +1673,8 @@ impl GlobalState {
16731673
vector: VectorIdx,
16741674
) -> String {
16751675
let thread = self.vector_info.borrow()[vector];
1676-
let thread_name = thread_mgr.get_thread_name(thread);
1677-
format!("thread `{}`", String::from_utf8_lossy(thread_name))
1676+
let thread_name = thread_mgr.get_thread_display_name(thread);
1677+
format!("thread `{thread_name}`")
16781678
}
16791679

16801680
/// Acquire a lock, express that the previous call of

src/concurrency/thread.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,18 @@ pub type StackEmptyCallback<'mir, 'tcx> =
160160
Box<dyn FnMut(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx, Poll<()>>>;
161161

162162
impl<'mir, 'tcx> Thread<'mir, 'tcx> {
163-
/// Get the name of the current thread, or `<unnamed>` if it was not set.
164-
fn thread_name(&self) -> &[u8] {
165-
if let Some(ref thread_name) = self.thread_name { thread_name } else { b"<unnamed>" }
163+
/// Get the name of the current thread if it was set.
164+
fn thread_name(&self) -> Option<&[u8]> {
165+
self.thread_name.as_deref()
166+
}
167+
168+
/// Get the name of the current thread for display purposes; will include thread ID if not set.
169+
fn thread_display_name(&self, id: ThreadId) -> String {
170+
if let Some(ref thread_name) = self.thread_name {
171+
String::from_utf8_lossy(thread_name).into_owned()
172+
} else {
173+
format!("unnamed-{}", id.index())
174+
}
166175
}
167176

168177
/// Return the top user-relevant frame, if there is one.
@@ -205,7 +214,7 @@ impl<'mir, 'tcx> std::fmt::Debug for Thread<'mir, 'tcx> {
205214
write!(
206215
f,
207216
"{}({:?}, {:?})",
208-
String::from_utf8_lossy(self.thread_name()),
217+
String::from_utf8_lossy(self.thread_name().unwrap_or(b"<unnamed>")),
209218
self.state,
210219
self.join_status
211220
)
@@ -572,10 +581,14 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
572581
}
573582

574583
/// Get the name of the given thread.
575-
pub fn get_thread_name(&self, thread: ThreadId) -> &[u8] {
584+
pub fn get_thread_name(&self, thread: ThreadId) -> Option<&[u8]> {
576585
self.threads[thread].thread_name()
577586
}
578587

588+
pub fn get_thread_display_name(&self, thread: ThreadId) -> String {
589+
self.threads[thread].thread_display_name(thread)
590+
}
591+
579592
/// Put the thread into the blocked state.
580593
fn block_thread(&mut self, thread: ThreadId) {
581594
let state = &mut self.threads[thread].state;
@@ -980,7 +993,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
980993
}
981994

982995
#[inline]
983-
fn get_thread_name<'c>(&'c self, thread: ThreadId) -> &'c [u8]
996+
fn get_thread_name<'c>(&'c self, thread: ThreadId) -> Option<&[u8]>
984997
where
985998
'mir: 'c,
986999
{

src/diagnostics.rs

+16-6
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ pub enum NonHaltingDiagnostic {
116116
CreatedCallId(CallId),
117117
CreatedAlloc(AllocId, Size, Align, MemoryKind<MiriMemoryKind>),
118118
FreedAlloc(AllocId),
119+
AccessedAlloc(AllocId, AccessKind),
119120
RejectedIsolatedOp(String),
120121
ProgressReport {
121122
block_count: u64, // how many basic blocks have been run so far
@@ -477,7 +478,6 @@ pub fn report_msg<'tcx>(
477478

478479
// Show note and help messages.
479480
let mut extra_span = false;
480-
let notes_len = notes.len();
481481
for (span_data, note) in notes {
482482
if let Some(span_data) = span_data {
483483
err.span_note(span_data.span(), note);
@@ -486,7 +486,6 @@ pub fn report_msg<'tcx>(
486486
err.note(note);
487487
}
488488
}
489-
let helps_len = helps.len();
490489
for (span_data, help) in helps {
491490
if let Some(span_data) = span_data {
492491
err.span_help(span_data.span(), help);
@@ -495,12 +494,20 @@ pub fn report_msg<'tcx>(
495494
err.help(help);
496495
}
497496
}
498-
if notes_len + helps_len > 0 {
499-
// Add visual separator before backtrace.
500-
err.note(if extra_span { "BACKTRACE (of the first span):" } else { "BACKTRACE:" });
501-
}
502497

503498
// Add backtrace
499+
let mut backtrace_title = String::from("BACKTRACE");
500+
if extra_span {
501+
write!(backtrace_title, " (of the first span)").unwrap();
502+
}
503+
let thread_name =
504+
machine.threads.get_thread_display_name(machine.threads.get_active_thread_id());
505+
if thread_name != "main" {
506+
// Only print thread name if it is not `main`.
507+
write!(backtrace_title, " on thread `{thread_name}`").unwrap();
508+
};
509+
write!(backtrace_title, ":").unwrap();
510+
err.note(backtrace_title);
504511
for (idx, frame_info) in stacktrace.iter().enumerate() {
505512
let is_local = machine.is_local(frame_info);
506513
// No span for non-local frames and the first frame (which is the error site).
@@ -532,6 +539,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
532539
| PoppedPointerTag(..)
533540
| CreatedCallId(..)
534541
| CreatedAlloc(..)
542+
| AccessedAlloc(..)
535543
| FreedAlloc(..)
536544
| ProgressReport { .. }
537545
| WeakMemoryOutdatedLoad => ("tracking was triggered".to_string(), DiagLevel::Note),
@@ -553,6 +561,8 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
553561
size = size.bytes(),
554562
align = align.bytes(),
555563
),
564+
AccessedAlloc(AllocId(id), access_kind) =>
565+
format!("{access_kind} access to allocation with id {id}"),
556566
FreedAlloc(AllocId(id)) => format!("freed allocation with id {id}"),
557567
RejectedIsolatedOp(ref op) =>
558568
format!("{op} was made to return an error due to isolation"),

src/eval.rs

+3
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ pub struct MiriConfig {
112112
pub tracked_call_ids: FxHashSet<CallId>,
113113
/// The allocation ids to report about.
114114
pub tracked_alloc_ids: FxHashSet<AllocId>,
115+
/// For the tracked alloc ids, also report read/write accesses.
116+
pub track_alloc_accesses: bool,
115117
/// Determine if data race detection should be enabled
116118
pub data_race_detector: bool,
117119
/// Determine if weak memory emulation should be enabled. Requires data race detection to be enabled
@@ -169,6 +171,7 @@ impl Default for MiriConfig {
169171
tracked_pointer_tags: FxHashSet::default(),
170172
tracked_call_ids: FxHashSet::default(),
171173
tracked_alloc_ids: FxHashSet::default(),
174+
track_alloc_accesses: false,
172175
data_race_detector: true,
173176
weak_memory_emulation: true,
174177
track_outdated_loads: false,

src/helpers.rs

+7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ use rand::RngCore;
2222

2323
use crate::*;
2424

25+
/// Indicates which kind of access is being performed.
26+
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
27+
pub enum AccessKind {
28+
Read,
29+
Write,
30+
}
31+
2532
// This mapping should match `decode_error_kind` in
2633
// <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/unix/mod.rs>.
2734
const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ pub use crate::diagnostics::{
120120
pub use crate::eval::{
121121
create_ecx, eval_entry, AlignmentCheck, BacktraceStyle, IsolatedOp, MiriConfig, RejectOpWith,
122122
};
123-
pub use crate::helpers::EvalContextExt as _;
123+
pub use crate::helpers::{AccessKind, EvalContextExt as _};
124124
pub use crate::intptrcast::{EvalContextExt as _, ProvenanceMode};
125125
pub use crate::machine::{
126126
AllocExtra, FrameExtra, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind,

src/machine.rs

+12
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,8 @@ pub struct MiriMachine<'mir, 'tcx> {
512512
/// The allocation IDs to report when they are being allocated
513513
/// (helps for debugging memory leaks and use after free bugs).
514514
tracked_alloc_ids: FxHashSet<AllocId>,
515+
/// For the tracked alloc ids, also report read/write accesses.
516+
track_alloc_accesses: bool,
515517

516518
/// Controls whether alignment of memory accesses is being checked.
517519
pub(crate) check_alignment: AlignmentCheck,
@@ -654,6 +656,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
654656
extern_statics: FxHashMap::default(),
655657
rng: RefCell::new(rng),
656658
tracked_alloc_ids: config.tracked_alloc_ids.clone(),
659+
track_alloc_accesses: config.track_alloc_accesses,
657660
check_alignment: config.check_alignment,
658661
cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
659662
mute_stdout_stderr: config.mute_stdout_stderr,
@@ -793,6 +796,7 @@ impl VisitProvenance for MiriMachine<'_, '_> {
793796
local_crates: _,
794797
rng: _,
795798
tracked_alloc_ids: _,
799+
track_alloc_accesses: _,
796800
check_alignment: _,
797801
cmpxchg_weak_failure_rate: _,
798802
mute_stdout_stderr: _,
@@ -1235,6 +1239,10 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
12351239
(alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
12361240
range: AllocRange,
12371241
) -> InterpResult<'tcx> {
1242+
if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1243+
machine
1244+
.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Read));
1245+
}
12381246
if let Some(data_race) = &alloc_extra.data_race {
12391247
data_race.read(alloc_id, range, machine)?;
12401248
}
@@ -1255,6 +1263,10 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
12551263
(alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
12561264
range: AllocRange,
12571265
) -> InterpResult<'tcx> {
1266+
if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1267+
machine
1268+
.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Write));
1269+
}
12581270
if let Some(data_race) = &mut alloc_extra.data_race {
12591271
data_race.write(alloc_id, range, machine)?;
12601272
}

src/shims/unix/thread.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
104104
let name_out = name_out.to_pointer(this)?;
105105
let len = len.to_target_usize(this)?;
106106

107-
let name = this.get_thread_name(thread).to_owned();
107+
// FIXME: we should use the program name if the thread name is not set
108+
let name = this.get_thread_name(thread).unwrap_or(b"<unnamed>").to_owned();
108109
let (success, _written) = this.write_c_str(&name, name_out, len)?;
109110

110111
Ok(if success { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") })

tests/compiletest.rs

+2
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ regexes! {
172172
r"\.rs:[0-9]+:[0-9]+(: [0-9]+:[0-9]+)?" => ".rs:LL:CC",
173173
// erase alloc ids
174174
"alloc[0-9]+" => "ALLOC",
175+
// erase thread ids
176+
r"unnamed-[0-9]+" => "unnamed-ID",
175177
// erase borrow tags
176178
"<[0-9]+>" => "<TAG>",
177179
"<[0-9]+=" => "<TAG=",

tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | panic!()
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9-
= note: BACKTRACE:
9+
= note: BACKTRACE on thread `unnamed-ID`:
1010
= note: inside `thread_start` at RUSTLIB/core/src/panic.rs:LL:CC
1111
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
1212

tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | panic!()
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9-
= note: BACKTRACE:
9+
= note: BACKTRACE on thread `unnamed-ID`:
1010
= note: inside `thread_start` at RUSTLIB/core/src/panic.rs:LL:CC
1111
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
1212

tests/fail-dep/concurrency/libc_pthread_join_main.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | assert_eq!(libc::pthread_join(thread_id, ptr::null_mut()), 0);
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9-
= note: BACKTRACE:
9+
= note: BACKTRACE on thread `unnamed-ID`:
1010
= note: inside closure at $DIR/libc_pthread_join_main.rs:LL:CC
1111

1212
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

tests/fail-dep/concurrency/libc_pthread_join_multiple.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | ... assert_eq!(libc::pthread_join(native_copy, ptr::null_mut()), 0);
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9-
= note: BACKTRACE:
9+
= note: BACKTRACE on thread `unnamed-ID`:
1010
= note: inside closure at $DIR/libc_pthread_join_multiple.rs:LL:CC
1111

1212
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

tests/fail-dep/concurrency/libc_pthread_join_self.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9-
= note: BACKTRACE:
9+
= note: BACKTRACE on thread `unnamed-ID`:
1010
= note: inside closure at $DIR/libc_pthread_join_self.rs:LL:CC
1111

1212
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

tests/fail-dep/concurrency/unwind_top_of_stack.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ LL | | }
1414
|
1515
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
1616
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
17-
= note: BACKTRACE:
17+
= note: BACKTRACE on thread `unnamed-ID`:
1818
= note: inside `thread_start` at $DIR/unwind_top_of_stack.rs:LL:CC
1919

2020
error: aborting due to 1 previous error

tests/fail-dep/concurrency/windows_join_main.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ error: deadlock: the evaluated program deadlocked
44
LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJECT_0);
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program deadlocked
66
|
7+
= note: BACKTRACE on thread `unnamed-ID`:
78
= note: inside closure at RUSTLIB/core/src/macros/mod.rs:LL:CC
89
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
910

tests/fail-dep/concurrency/windows_join_self.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ error: deadlock: the evaluated program deadlocked
44
LL | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0);
55
| ^ the evaluated program deadlocked
66
|
7+
= note: BACKTRACE on thread `unnamed-ID`:
78
= note: inside closure at $DIR/windows_join_self.rs:LL:CC
89

910
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

0 commit comments

Comments
 (0)