Skip to content

Commit dd9df18

Browse files
committed
Auto merge of rust-lang#2867 - Vanille-N:tb-diags, r=RalfJung
Simplify event selection in TB diagnostics As discussed previously, getting the range from `RangeMap` can make the filtering of events much simpler without any user-visible diff. See minor exception in [<9d8fc00>](rust-lang/miri@9d8fc00) and decide how to resolve it - add a boolean flag not to record events produced by deallocations ? - add a `help: deallocation counts as an implicit write` ? (Note: could be generalized to also include `help: reborrow counts as an implicit read`) - not bother and keep as-is ? - something else ?
2 parents 457a7a8 + 0677cd0 commit dd9df18

File tree

6 files changed

+109
-94
lines changed

6 files changed

+109
-94
lines changed

src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ impl<'tcx> Stack {
459459
impl Stacks {
460460
pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet<BorTag>) {
461461
if self.modified_since_last_gc {
462-
for stack in self.stacks.iter_mut_all() {
462+
for (_stack_range, stack) in self.stacks.iter_mut_all() {
463463
if stack.len() > 64 {
464464
stack.retain(live_tags);
465465
}
@@ -511,8 +511,8 @@ impl<'tcx> Stacks {
511511
) -> InterpResult<'tcx>,
512512
) -> InterpResult<'tcx> {
513513
self.modified_since_last_gc = true;
514-
for (offset, stack) in self.stacks.iter_mut(range.start, range.size) {
515-
let mut dcx = dcx_builder.build(&mut self.history, offset);
514+
for (stack_range, stack) in self.stacks.iter_mut(range.start, range.size) {
515+
let mut dcx = dcx_builder.build(&mut self.history, Size::from_bytes(stack_range.start));
516516
f(stack, &mut dcx, &mut self.exposed_tags)?;
517517
dcx_builder = dcx.unbuild();
518518
}

src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs

Lines changed: 48 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::ops::Range;
33

44
use rustc_data_structures::fx::FxHashMap;
55
use rustc_span::{Span, SpanData};
6-
use rustc_target::abi::Size;
76

87
use crate::borrow_tracker::tree_borrows::{
98
perms::{PermTransition, Permission},
@@ -14,18 +13,30 @@ use crate::borrow_tracker::{AccessKind, ProtectorKind};
1413
use crate::*;
1514

1615
/// Complete data for an event:
17-
/// - `kind` is what happened to the permissions
18-
/// - `access_kind` and `access_range` describe the access that caused the event
19-
/// - `offset` allows filtering only the relevant events for a given memory location
20-
/// (see how we perform the filtering in `History::extract_relevant`.
21-
/// - `span` is the line of code in question
2216
#[derive(Clone, Debug)]
2317
pub struct Event {
18+
/// Transformation of permissions that occured because of this event
2419
pub transition: PermTransition,
20+
/// Kind of the access that triggered this event
2521
pub access_kind: AccessKind,
22+
/// Relative position of the tag to the one used for the access
2623
pub is_foreign: bool,
24+
/// User-visible range of the access
2725
pub access_range: AllocRange,
28-
pub offset: Size,
26+
/// The transition recorded by this event only occured on a subrange of
27+
/// `access_range`: a single access on `access_range` triggers several events,
28+
/// each with their own mutually disjoint `transition_range`. No-op transitions
29+
/// should not be recorded as events, so the union of all `transition_range` is not
30+
/// necessarily the entire `access_range`.
31+
///
32+
/// No data from any `transition_range` should ever be user-visible, because
33+
/// both the start and end of `transition_range` are entirely dependent on the
34+
/// internal representation of `RangeMap` which is supposed to be opaque.
35+
/// What will be shown in the error message is the first byte `error_offset` of
36+
/// the `TbError`, which should satisfy
37+
/// `event.transition_range.contains(error.error_offset)`.
38+
pub transition_range: Range<u64>,
39+
/// Line of code that triggered this event
2940
pub span: Span,
3041
}
3142

@@ -35,9 +46,9 @@ pub struct Event {
3546
/// Available filtering methods include `History::forget` and `History::extract_relevant`.
3647
#[derive(Clone, Debug)]
3748
pub struct History {
38-
pub tag: BorTag,
39-
pub created: (Span, Permission),
40-
pub events: Vec<Event>,
49+
tag: BorTag,
50+
created: (Span, Permission),
51+
events: Vec<Event>,
4152
}
4253

4354
/// History formatted for use by `src/diagnostics.rs`.
@@ -60,12 +71,7 @@ impl HistoryData {
6071
// Format events from `new_history` into those recorded by `self`.
6172
//
6273
// NOTE: also converts `Span` to `SpanData`.
63-
pub fn extend(
64-
&mut self,
65-
new_history: History,
66-
tag_name: &'static str,
67-
show_initial_state: bool,
68-
) {
74+
fn extend(&mut self, new_history: History, tag_name: &'static str, show_initial_state: bool) {
6975
let History { tag, created, events } = new_history;
7076
let this = format!("the {tag_name} tag {tag:?}");
7177
let msg_initial_state = format!(", in the initial state {}", created.1);
@@ -75,9 +81,16 @@ impl HistoryData {
7581
);
7682

7783
self.events.push((Some(created.0.data()), msg_creation));
78-
for &Event { transition, access_kind, is_foreign, access_range, span, offset: _ } in &events
84+
for &Event {
85+
transition,
86+
access_kind,
87+
is_foreign,
88+
access_range,
89+
span,
90+
transition_range: _,
91+
} in &events
7992
{
80-
// NOTE: `offset` is explicitly absent from the error message, it has no significance
93+
// NOTE: `transition_range` is explicitly absent from the error message, it has no significance
8194
// to the user. The meaningful one is `access_range`.
8295
self.events.push((Some(span.data()), format!("{this} then transitioned {transition} due to a {rel} {access_kind} at offsets {access_range:?}", rel = if is_foreign { "foreign" } else { "child" })));
8396
self.events.push((None, format!("this corresponds to {}", transition.summary())));
@@ -197,53 +210,28 @@ impl History {
197210
History { events: Vec::new(), created: self.created, tag: self.tag }
198211
}
199212

200-
/// Reconstruct the history relevant to `error_offset` knowing that
201-
/// its permission followed `complete_transition`.
202-
///
203-
/// Here's how we do this:
204-
/// - we know `full := complete_transition` the transition of the permission from
205-
/// its initialization to the state just before the error was caused,
206-
/// we want to find a chain of events that produces `full`
207-
/// - we decompose `full` into `pre o post` where
208-
/// `pre` is the best applicable transition from recorded events
209-
/// - we select the event that caused `pre` and iterate
210-
/// to find the chain of events that produces `full := post`
211-
///
212-
/// To find the "best applicable transition" for full:
213-
/// - eliminate events that cannot be applied because their offset is too big
214-
/// - eliminate events that cannot be applied because their starting point is wrong
215-
/// - select the one that happened closest to the range of interest
216-
fn extract_relevant(&self, complete_transition: PermTransition, error_offset: Size) -> Self {
217-
let mut selected_events: Vec<Event> = Vec::new();
218-
let mut full = complete_transition;
219-
while !full.is_noop() {
220-
let (pre, post) = self
213+
/// Reconstruct the history relevant to `error_offset` by filtering
214+
/// only events whose range contains the offset we are interested in.
215+
fn extract_relevant(&self, error_offset: u64) -> Self {
216+
History {
217+
events: self
221218
.events
222219
.iter()
223-
.filter(|e| e.offset <= error_offset)
224-
.filter_map(|pre_canditate| {
225-
full.apply_start(pre_canditate.transition)
226-
.map(|post_canditate| (pre_canditate, post_canditate))
227-
})
228-
.max_by_key(|(pre_canditate, _post_candidate)| pre_canditate.offset)
229-
.unwrap();
230-
// If this occurs we will loop infinitely !
231-
// Make sure to only put non-noop transitions in `History`.
232-
assert!(!pre.transition.is_noop());
233-
full = post;
234-
selected_events.push(pre.clone());
220+
.filter(|e| e.transition_range.contains(&error_offset))
221+
.cloned()
222+
.collect::<Vec<_>>(),
223+
created: self.created,
224+
tag: self.tag,
235225
}
236-
237-
History { events: selected_events, created: self.created, tag: self.tag }
238226
}
239227
}
240228

241229
/// Failures that can occur during the execution of Tree Borrows procedures.
242230
pub(super) struct TbError<'node> {
243231
/// What failure occurred.
244232
pub error_kind: TransitionError,
245-
/// The byte at which the conflict occured.
246-
pub error_offset: Size,
233+
/// The offset (into the allocation) at which the conflict occurred.
234+
pub error_offset: u64,
247235
/// The tag on which the error was triggered.
248236
/// On protector violations, this is the tag that was protected.
249237
/// On accesses rejected due to insufficient permissions, this is the
@@ -261,12 +249,11 @@ impl TbError<'_> {
261249
/// Produce a UB error.
262250
pub fn build<'tcx>(self) -> InterpError<'tcx> {
263251
use TransitionError::*;
264-
let started_as = self.conflicting_info.history.created.1;
265252
let kind = self.access_kind;
266253
let accessed = self.accessed_info;
267254
let conflicting = self.conflicting_info;
268255
let accessed_is_conflicting = accessed.tag == conflicting.tag;
269-
let (pre_error, title, details, conflicting_tag_name) = match self.error_kind {
256+
let (title, details, conflicting_tag_name) = match self.error_kind {
270257
ChildAccessForbidden(perm) => {
271258
let conflicting_tag_name =
272259
if accessed_is_conflicting { "accessed" } else { "conflicting" };
@@ -280,7 +267,7 @@ impl TbError<'_> {
280267
details.push(format!(
281268
"the {conflicting_tag_name} tag {conflicting} has state {perm} which forbids child {kind}es"
282269
));
283-
(perm, title, details, conflicting_tag_name)
270+
(title, details, conflicting_tag_name)
284271
}
285272
ProtectedTransition(transition) => {
286273
let conflicting_tag_name = "protected";
@@ -297,7 +284,7 @@ impl TbError<'_> {
297284
loss = transition.summary(),
298285
),
299286
];
300-
(transition.started(), title, details, conflicting_tag_name)
287+
(title, details, conflicting_tag_name)
301288
}
302289
ProtectedDealloc => {
303290
let conflicting_tag_name = "strongly protected";
@@ -308,16 +295,15 @@ impl TbError<'_> {
308295
),
309296
format!("the {conflicting_tag_name} tag {conflicting} disallows deallocations"),
310297
];
311-
(started_as, title, details, conflicting_tag_name)
298+
(title, details, conflicting_tag_name)
312299
}
313300
};
314-
let pre_transition = PermTransition::from(started_as, pre_error).unwrap();
315301
let mut history = HistoryData::default();
316302
if !accessed_is_conflicting {
317303
history.extend(self.accessed_info.history.forget(), "accessed", false);
318304
}
319305
history.extend(
320-
self.conflicting_info.history.extract_relevant(pre_transition, self.error_offset),
306+
self.conflicting_info.history.extract_relevant(self.error_offset),
321307
conflicting_tag_name,
322308
true,
323309
);

src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ impl<'tcx> Tree {
311311
parent_tag: BorTag,
312312
new_tag: BorTag,
313313
default_initial_perm: Permission,
314-
range: AllocRange,
314+
reborrow_range: AllocRange,
315315
span: Span,
316316
) -> InterpResult<'tcx> {
317317
assert!(!self.tag_mapping.contains_key(&new_tag));
@@ -332,7 +332,8 @@ impl<'tcx> Tree {
332332
self.nodes.get_mut(parent_idx).unwrap().children.push(idx);
333333
// Initialize perms
334334
let perm = LocationState::new(default_initial_perm).with_access();
335-
for (_range, perms) in self.rperms.iter_mut(range.start, range.size) {
335+
for (_perms_range, perms) in self.rperms.iter_mut(reborrow_range.start, reborrow_range.size)
336+
{
336337
perms.insert(idx, perm);
337338
}
338339
Ok(())
@@ -344,12 +345,12 @@ impl<'tcx> Tree {
344345
pub fn dealloc(
345346
&mut self,
346347
tag: BorTag,
347-
range: AllocRange,
348+
access_range: AllocRange,
348349
global: &GlobalState,
349350
span: Span, // diagnostics
350351
) -> InterpResult<'tcx> {
351-
self.perform_access(AccessKind::Write, tag, range, global, span)?;
352-
for (offset, perms) in self.rperms.iter_mut(range.start, range.size) {
352+
self.perform_access(AccessKind::Write, tag, access_range, global, span)?;
353+
for (perms_range, perms) in self.rperms.iter_mut(access_range.start, access_range.size) {
353354
TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, perms }
354355
.traverse_parents_this_children_others(
355356
tag,
@@ -368,7 +369,7 @@ impl<'tcx> Tree {
368369
TbError {
369370
conflicting_info,
370371
access_kind: AccessKind::Write,
371-
error_offset: offset,
372+
error_offset: perms_range.start,
372373
error_kind,
373374
accessed_info,
374375
}
@@ -388,11 +389,11 @@ impl<'tcx> Tree {
388389
&mut self,
389390
access_kind: AccessKind,
390391
tag: BorTag,
391-
range: AllocRange,
392+
access_range: AllocRange,
392393
global: &GlobalState,
393394
span: Span, // diagnostics
394395
) -> InterpResult<'tcx> {
395-
for (offset, perms) in self.rperms.iter_mut(range.start, range.size) {
396+
for (perms_range, perms) in self.rperms.iter_mut(access_range.start, access_range.size) {
396397
TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, perms }
397398
.traverse_parents_this_children_others(
398399
tag,
@@ -456,9 +457,9 @@ impl<'tcx> Tree {
456457
node.debug_info.history.push(diagnostics::Event {
457458
transition,
458459
access_kind,
459-
access_range: range,
460460
is_foreign: rel_pos.is_foreign(),
461-
offset,
461+
access_range,
462+
transition_range: perms_range.clone(),
462463
span,
463464
});
464465
old_state.permission =
@@ -472,7 +473,7 @@ impl<'tcx> Tree {
472473
TbError {
473474
conflicting_info,
474475
access_kind,
475-
error_offset: offset,
476+
error_offset: perms_range.start,
476477
error_kind,
477478
accessed_info,
478479
}
@@ -530,7 +531,7 @@ impl Tree {
530531
// the tag from the mapping.
531532
let tag = node.tag;
532533
self.nodes.remove(idx);
533-
for perms in self.rperms.iter_mut_all() {
534+
for (_perms_range, perms) in self.rperms.iter_mut_all() {
534535
perms.remove(idx);
535536
}
536537
self.tag_mapping.remove(&tag);

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

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -868,15 +868,17 @@ impl VClockAlloc {
868868
pub fn read<'tcx>(
869869
&self,
870870
alloc_id: AllocId,
871-
range: AllocRange,
871+
access_range: AllocRange,
872872
machine: &MiriMachine<'_, '_>,
873873
) -> InterpResult<'tcx> {
874874
let current_span = machine.current_span();
875875
let global = machine.data_race.as_ref().unwrap();
876876
if global.race_detecting() {
877877
let (index, mut thread_clocks) = global.current_thread_state_mut(&machine.threads);
878878
let mut alloc_ranges = self.alloc_ranges.borrow_mut();
879-
for (offset, mem_clocks) in alloc_ranges.iter_mut(range.start, range.size) {
879+
for (mem_clocks_range, mem_clocks) in
880+
alloc_ranges.iter_mut(access_range.start, access_range.size)
881+
{
880882
if let Err(DataRace) =
881883
mem_clocks.read_race_detect(&mut thread_clocks, index, current_span)
882884
{
@@ -888,7 +890,7 @@ impl VClockAlloc {
888890
mem_clocks,
889891
"Read",
890892
false,
891-
Pointer::new(alloc_id, offset),
893+
Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
892894
);
893895
}
894896
}
@@ -902,16 +904,16 @@ impl VClockAlloc {
902904
fn unique_access<'tcx>(
903905
&mut self,
904906
alloc_id: AllocId,
905-
range: AllocRange,
907+
access_range: AllocRange,
906908
write_type: WriteType,
907909
machine: &mut MiriMachine<'_, '_>,
908910
) -> InterpResult<'tcx> {
909911
let current_span = machine.current_span();
910912
let global = machine.data_race.as_mut().unwrap();
911913
if global.race_detecting() {
912914
let (index, mut thread_clocks) = global.current_thread_state_mut(&machine.threads);
913-
for (offset, mem_clocks) in
914-
self.alloc_ranges.get_mut().iter_mut(range.start, range.size)
915+
for (mem_clocks_range, mem_clocks) in
916+
self.alloc_ranges.get_mut().iter_mut(access_range.start, access_range.size)
915917
{
916918
if let Err(DataRace) = mem_clocks.write_race_detect(
917919
&mut thread_clocks,
@@ -927,7 +929,7 @@ impl VClockAlloc {
927929
mem_clocks,
928930
write_type.get_descriptor(),
929931
false,
930-
Pointer::new(alloc_id, offset),
932+
Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
931933
);
932934
}
933935
}
@@ -1150,7 +1152,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
11501152
&this.machine.threads,
11511153
current_span,
11521154
|index, mut thread_clocks| {
1153-
for (offset, mem_clocks) in
1155+
for (mem_clocks_range, mem_clocks) in
11541156
alloc_meta.alloc_ranges.borrow_mut().iter_mut(base_offset, size)
11551157
{
11561158
if let Err(DataRace) = op(mem_clocks, &mut thread_clocks, index, atomic)
@@ -1162,7 +1164,10 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
11621164
mem_clocks,
11631165
description,
11641166
true,
1165-
Pointer::new(alloc_id, offset),
1167+
Pointer::new(
1168+
alloc_id,
1169+
Size::from_bytes(mem_clocks_range.start),
1170+
),
11661171
)
11671172
.map(|_| true);
11681173
}

0 commit comments

Comments
 (0)