Skip to content

Commit 245357f

Browse files
committed
Auto merge of #2646 - saethlin:data-race-spans, r=RalfJung
Data race spans Fixes rust-lang/miri#2205 This adds output to data race errors very similar to the spans we emit for Stacked Borrows errors. For example, from our test suite: ``` help: The Atomic Load on thread `<unnamed>` is here --> tests/fail/data_race/atomic_read_na_write_race1.rs:23:13 | 23 | ... (&*c.0).load(Ordering::SeqCst) //~ ERROR: Data race detected between Atomic Load on thread `<unnamed>` and Write o... | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: The Write on thread `<unnamed>` is here --> tests/fail/data_race/atomic_read_na_write_race1.rs:19:13 | 19 | *(c.0 as *mut usize) = 32; | ^^^^^^^^^^^^^^^^^^^^^^^^^``` ``` Because of rust-lang/miri#2647 this comes without a perf regression, according to our benchmarks.
2 parents 5b64c91 + 81fe37a commit 245357f

File tree

126 files changed

+555
-273
lines changed

Some content is hidden

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

126 files changed

+555
-273
lines changed

src/tools/miri/src/concurrency/data_race.rs

Lines changed: 104 additions & 71 deletions
Large diffs are not rendered by default.

src/tools/miri/src/concurrency/init_once.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
160160
fn init_once_complete(&mut self, id: InitOnceId) -> InterpResult<'tcx> {
161161
let this = self.eval_context_mut();
162162
let current_thread = this.get_active_thread();
163+
let current_span = this.machine.current_span();
163164
let init_once = &mut this.machine.threads.sync.init_onces[id];
164165

165166
assert_eq!(
@@ -172,7 +173,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
172173

173174
// Each complete happens-before the end of the wait
174175
if let Some(data_race) = &this.machine.data_race {
175-
data_race.validate_lock_release(&mut init_once.data_race, current_thread);
176+
data_race.validate_lock_release(&mut init_once.data_race, current_thread, current_span);
176177
}
177178

178179
// Wake up everyone.
@@ -188,6 +189,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
188189
fn init_once_fail(&mut self, id: InitOnceId) -> InterpResult<'tcx> {
189190
let this = self.eval_context_mut();
190191
let current_thread = this.get_active_thread();
192+
let current_span = this.machine.current_span();
191193
let init_once = &mut this.machine.threads.sync.init_onces[id];
192194
assert_eq!(
193195
init_once.status,
@@ -197,7 +199,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
197199

198200
// Each complete happens-before the end of the wait
199201
if let Some(data_race) = &this.machine.data_race {
200-
data_race.validate_lock_release(&mut init_once.data_race, current_thread);
202+
data_race.validate_lock_release(&mut init_once.data_race, current_thread, current_span);
201203
}
202204

203205
// Wake up one waiting thread, so they can go ahead and try to init this.

src/tools/miri/src/concurrency/sync.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
359359
/// return `None`.
360360
fn mutex_unlock(&mut self, id: MutexId, expected_owner: ThreadId) -> Option<usize> {
361361
let this = self.eval_context_mut();
362+
let current_span = this.machine.current_span();
362363
let mutex = &mut this.machine.threads.sync.mutexes[id];
363364
if let Some(current_owner) = mutex.owner {
364365
// Mutex is locked.
@@ -375,7 +376,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
375376
// The mutex is completely unlocked. Try transfering ownership
376377
// to another thread.
377378
if let Some(data_race) = &this.machine.data_race {
378-
data_race.validate_lock_release(&mut mutex.data_race, current_owner);
379+
data_race.validate_lock_release(
380+
&mut mutex.data_race,
381+
current_owner,
382+
current_span,
383+
);
379384
}
380385
this.mutex_dequeue_and_lock(id);
381386
}
@@ -454,6 +459,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
454459
/// Returns `true` if succeeded, `false` if this `reader` did not hold the lock.
455460
fn rwlock_reader_unlock(&mut self, id: RwLockId, reader: ThreadId) -> bool {
456461
let this = self.eval_context_mut();
462+
let current_span = this.machine.current_span();
457463
let rwlock = &mut this.machine.threads.sync.rwlocks[id];
458464
match rwlock.readers.entry(reader) {
459465
Entry::Occupied(mut entry) => {
@@ -470,7 +476,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
470476
Entry::Vacant(_) => return false, // we did not even own this lock
471477
}
472478
if let Some(data_race) = &this.machine.data_race {
473-
data_race.validate_lock_release_shared(&mut rwlock.data_race_reader, reader);
479+
data_race.validate_lock_release_shared(
480+
&mut rwlock.data_race_reader,
481+
reader,
482+
current_span,
483+
);
474484
}
475485

476486
// The thread was a reader. If the lock is not held any more, give it to a writer.
@@ -511,6 +521,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
511521
#[inline]
512522
fn rwlock_writer_unlock(&mut self, id: RwLockId, expected_writer: ThreadId) -> bool {
513523
let this = self.eval_context_mut();
524+
let current_span = this.machine.current_span();
514525
let rwlock = &mut this.machine.threads.sync.rwlocks[id];
515526
if let Some(current_writer) = rwlock.writer {
516527
if current_writer != expected_writer {
@@ -523,8 +534,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
523534
// since this writer happens-before both the union of readers once they are finished
524535
// and the next writer
525536
if let Some(data_race) = &this.machine.data_race {
526-
data_race.validate_lock_release(&mut rwlock.data_race, current_writer);
527-
data_race.validate_lock_release(&mut rwlock.data_race_reader, current_writer);
537+
data_race.validate_lock_release(
538+
&mut rwlock.data_race,
539+
current_writer,
540+
current_span,
541+
);
542+
data_race.validate_lock_release(
543+
&mut rwlock.data_race_reader,
544+
current_writer,
545+
current_span,
546+
);
528547
}
529548
// The thread was a writer.
530549
//
@@ -595,12 +614,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
595614
fn condvar_signal(&mut self, id: CondvarId) -> Option<(ThreadId, CondvarLock)> {
596615
let this = self.eval_context_mut();
597616
let current_thread = this.get_active_thread();
617+
let current_span = this.machine.current_span();
598618
let condvar = &mut this.machine.threads.sync.condvars[id];
599619
let data_race = &this.machine.data_race;
600620

601621
// Each condvar signal happens-before the end of the condvar wake
602622
if let Some(data_race) = data_race {
603-
data_race.validate_lock_release(&mut condvar.data_race, current_thread);
623+
data_race.validate_lock_release(&mut condvar.data_race, current_thread, current_span);
604624
}
605625
condvar.waiters.pop_front().map(|waiter| {
606626
if let Some(data_race) = data_race {
@@ -628,12 +648,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
628648
fn futex_wake(&mut self, addr: u64, bitset: u32) -> Option<ThreadId> {
629649
let this = self.eval_context_mut();
630650
let current_thread = this.get_active_thread();
651+
let current_span = this.machine.current_span();
631652
let futex = &mut this.machine.threads.sync.futexes.get_mut(&addr)?;
632653
let data_race = &this.machine.data_race;
633654

634655
// Each futex-wake happens-before the end of the futex wait
635656
if let Some(data_race) = data_race {
636-
data_race.validate_lock_release(&mut futex.data_race, current_thread);
657+
data_race.validate_lock_release(&mut futex.data_race, current_thread, current_span);
637658
}
638659

639660
// Wake up the first thread in the queue that matches any of the bits in the bitset.

src/tools/miri/src/concurrency/thread.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use rustc_hir::def_id::DefId;
1313
use rustc_index::vec::{Idx, IndexVec};
1414
use rustc_middle::mir::Mutability;
1515
use rustc_middle::ty::layout::TyAndLayout;
16+
use rustc_span::Span;
1617
use rustc_target::spec::abi::Abi;
1718

1819
use crate::concurrency::data_race;
@@ -617,6 +618,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
617618
fn thread_terminated(
618619
&mut self,
619620
mut data_race: Option<&mut data_race::GlobalState>,
621+
current_span: Span,
620622
) -> Vec<Pointer<Provenance>> {
621623
let mut free_tls_statics = Vec::new();
622624
{
@@ -634,7 +636,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
634636
}
635637
// Set the thread into a terminated state in the data-race detector.
636638
if let Some(ref mut data_race) = data_race {
637-
data_race.thread_terminated(self);
639+
data_race.thread_terminated(self, current_span);
638640
}
639641
// Check if we need to unblock any threads.
640642
let mut joined_threads = vec![]; // store which threads joined, we'll need it
@@ -813,8 +815,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
813815
let mut state = tls::TlsDtorsState::default();
814816
Box::new(move |m| state.on_stack_empty(m))
815817
});
818+
let current_span = this.machine.current_span();
816819
if let Some(data_race) = &mut this.machine.data_race {
817-
data_race.thread_created(&this.machine.threads, new_thread_id);
820+
data_race.thread_created(&this.machine.threads, new_thread_id, current_span);
818821
}
819822

820823
// Write the current thread-id, switch to the next thread later
@@ -1041,7 +1044,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
10411044
assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated");
10421045
thread.state = ThreadState::Terminated;
10431046

1044-
for ptr in this.machine.threads.thread_terminated(this.machine.data_race.as_mut()) {
1047+
let current_span = this.machine.current_span();
1048+
for ptr in
1049+
this.machine.threads.thread_terminated(this.machine.data_race.as_mut(), current_span)
1050+
{
10451051
this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?;
10461052
}
10471053
Ok(())

src/tools/miri/src/concurrency/vector_clock.rs

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
use rustc_index::vec::Idx;
2+
use rustc_span::{Span, SpanData, DUMMY_SP};
23
use smallvec::SmallVec;
3-
use std::{cmp::Ordering, fmt::Debug, ops::Index};
4+
use std::{
5+
cmp::Ordering,
6+
fmt::Debug,
7+
ops::{Index, IndexMut},
8+
};
49

510
/// A vector clock index, this is associated with a thread id
611
/// but in some cases one vector index may be shared with
@@ -40,9 +45,42 @@ impl From<u32> for VectorIdx {
4045
/// clock vectors larger than this will be stored on the heap
4146
const SMALL_VECTOR: usize = 4;
4247

43-
/// The type of the time-stamps recorded in the data-race detector
44-
/// set to a type of unsigned integer
45-
pub type VTimestamp = u32;
48+
/// The time-stamps recorded in the data-race detector consist of both
49+
/// a 32-bit unsigned integer which is the actual timestamp, and a `Span`
50+
/// so that diagnostics can report what code was responsible for an operation.
51+
#[derive(Clone, Copy, Debug)]
52+
pub struct VTimestamp {
53+
time: u32,
54+
pub span: Span,
55+
}
56+
57+
impl VTimestamp {
58+
pub const ZERO: VTimestamp = VTimestamp { time: 0, span: DUMMY_SP };
59+
60+
pub fn span_data(&self) -> SpanData {
61+
self.span.data()
62+
}
63+
}
64+
65+
impl PartialEq for VTimestamp {
66+
fn eq(&self, other: &Self) -> bool {
67+
self.time == other.time
68+
}
69+
}
70+
71+
impl Eq for VTimestamp {}
72+
73+
impl PartialOrd for VTimestamp {
74+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
75+
Some(self.cmp(other))
76+
}
77+
}
78+
79+
impl Ord for VTimestamp {
80+
fn cmp(&self, other: &Self) -> Ordering {
81+
self.time.cmp(&other.time)
82+
}
83+
}
4684

4785
/// A vector clock for detecting data-races, this is conceptually
4886
/// a map from a vector index (and thus a thread id) to a timestamp.
@@ -62,7 +100,7 @@ impl VClock {
62100
/// for a value at the given index
63101
pub fn new_with_index(index: VectorIdx, timestamp: VTimestamp) -> VClock {
64102
let len = index.index() + 1;
65-
let mut vec = smallvec::smallvec![0; len];
103+
let mut vec = smallvec::smallvec![VTimestamp::ZERO; len];
66104
vec[index.index()] = timestamp;
67105
VClock(vec)
68106
}
@@ -79,7 +117,7 @@ impl VClock {
79117
#[inline]
80118
fn get_mut_with_min_len(&mut self, min_len: usize) -> &mut [VTimestamp] {
81119
if self.0.len() < min_len {
82-
self.0.resize(min_len, 0);
120+
self.0.resize(min_len, VTimestamp::ZERO);
83121
}
84122
assert!(self.0.len() >= min_len);
85123
self.0.as_mut_slice()
@@ -88,11 +126,14 @@ impl VClock {
88126
/// Increment the vector clock at a known index
89127
/// this will panic if the vector index overflows
90128
#[inline]
91-
pub fn increment_index(&mut self, idx: VectorIdx) {
129+
pub fn increment_index(&mut self, idx: VectorIdx, current_span: Span) {
92130
let idx = idx.index();
93131
let mut_slice = self.get_mut_with_min_len(idx + 1);
94132
let idx_ref = &mut mut_slice[idx];
95-
*idx_ref = idx_ref.checked_add(1).expect("Vector clock overflow")
133+
idx_ref.time = idx_ref.time.checked_add(1).expect("Vector clock overflow");
134+
if !current_span.is_dummy() {
135+
idx_ref.span = current_span;
136+
}
96137
}
97138

98139
// Join the two vector-clocks together, this
@@ -102,14 +143,23 @@ impl VClock {
102143
let rhs_slice = other.as_slice();
103144
let lhs_slice = self.get_mut_with_min_len(rhs_slice.len());
104145
for (l, &r) in lhs_slice.iter_mut().zip(rhs_slice.iter()) {
146+
let l_span = l.span;
147+
let r_span = r.span;
105148
*l = r.max(*l);
149+
l.span = l.span.substitute_dummy(r_span).substitute_dummy(l_span);
106150
}
107151
}
108152

109153
/// Set the element at the current index of the vector
110154
pub fn set_at_index(&mut self, other: &Self, idx: VectorIdx) {
111155
let mut_slice = self.get_mut_with_min_len(idx.index() + 1);
156+
157+
let prev_span = mut_slice[idx.index()].span;
158+
112159
mut_slice[idx.index()] = other[idx];
160+
161+
let span = &mut mut_slice[idx.index()].span;
162+
*span = span.substitute_dummy(prev_span);
113163
}
114164

115165
/// Set the vector to the all-zero vector
@@ -313,7 +363,14 @@ impl Index<VectorIdx> for VClock {
313363

314364
#[inline]
315365
fn index(&self, index: VectorIdx) -> &VTimestamp {
316-
self.as_slice().get(index.to_u32() as usize).unwrap_or(&0)
366+
self.as_slice().get(index.to_u32() as usize).unwrap_or(&VTimestamp::ZERO)
367+
}
368+
}
369+
370+
impl IndexMut<VectorIdx> for VClock {
371+
#[inline]
372+
fn index_mut(&mut self, index: VectorIdx) -> &mut VTimestamp {
373+
self.0.as_mut_slice().get_mut(index.to_u32() as usize).unwrap()
317374
}
318375
}
319376

@@ -324,20 +381,21 @@ impl Index<VectorIdx> for VClock {
324381
mod tests {
325382

326383
use super::{VClock, VTimestamp, VectorIdx};
384+
use rustc_span::DUMMY_SP;
327385
use std::cmp::Ordering;
328386

329387
#[test]
330388
fn test_equal() {
331389
let mut c1 = VClock::default();
332390
let mut c2 = VClock::default();
333391
assert_eq!(c1, c2);
334-
c1.increment_index(VectorIdx(5));
392+
c1.increment_index(VectorIdx(5), DUMMY_SP);
335393
assert_ne!(c1, c2);
336-
c2.increment_index(VectorIdx(53));
394+
c2.increment_index(VectorIdx(53), DUMMY_SP);
337395
assert_ne!(c1, c2);
338-
c1.increment_index(VectorIdx(53));
396+
c1.increment_index(VectorIdx(53), DUMMY_SP);
339397
assert_ne!(c1, c2);
340-
c2.increment_index(VectorIdx(5));
398+
c2.increment_index(VectorIdx(5), DUMMY_SP);
341399
assert_eq!(c1, c2);
342400
}
343401

@@ -386,14 +444,14 @@ mod tests {
386444
);
387445
}
388446

389-
fn from_slice(mut slice: &[VTimestamp]) -> VClock {
447+
fn from_slice(mut slice: &[u32]) -> VClock {
390448
while let Some(0) = slice.last() {
391449
slice = &slice[..slice.len() - 1]
392450
}
393-
VClock(smallvec::SmallVec::from_slice(slice))
451+
VClock(slice.iter().copied().map(|time| VTimestamp { time, span: DUMMY_SP }).collect())
394452
}
395453

396-
fn assert_order(l: &[VTimestamp], r: &[VTimestamp], o: Option<Ordering>) {
454+
fn assert_order(l: &[u32], r: &[u32], o: Option<Ordering>) {
397455
let l = from_slice(l);
398456
let r = from_slice(r);
399457

src/tools/miri/src/concurrency/weak_memory.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
258258
// The thread index and timestamp of the initialisation write
259259
// are never meaningfully used, so it's fine to leave them as 0
260260
store_index: VectorIdx::from(0),
261-
timestamp: 0,
261+
timestamp: VTimestamp::ZERO,
262262
val: init,
263263
is_seqcst: false,
264264
load_info: RefCell::new(LoadInfo::default()),

0 commit comments

Comments
 (0)