-
Notifications
You must be signed in to change notification settings - Fork 332
/
diagnostics.rs
188 lines (172 loc) · 6.8 KB
/
diagnostics.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
use std::cell::RefCell;
use std::fmt;
use log::trace;
use rustc_span::DUMMY_SP;
use crate::*;
/// Details of premature program termination.
pub enum TerminationInfo {
Exit(i64),
Abort(Option<String>),
UnsupportedInIsolation(String),
ExperimentalUb { msg: String, url: String }
}
impl fmt::Debug for TerminationInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use TerminationInfo::*;
match self {
Exit(code) =>
write!(f, "the evaluated program completed with exit code {}", code),
Abort(None) =>
write!(f, "the evaluated program aborted execution"),
Abort(Some(msg)) =>
write!(f, "the evaluated program aborted execution: {}", msg),
UnsupportedInIsolation(msg) =>
write!(f, "{}", msg),
ExperimentalUb { msg, .. } =>
write!(f, "{}", msg),
}
}
}
impl MachineStopType for TerminationInfo {}
/// Miri specific diagnostics
pub enum NonHaltingDiagnostic {
PoppedTrackedPointerTag(Item),
CreatedAlloc(AllocId),
}
/// Emit a custom diagnostic without going through the miri-engine machinery
pub fn report_error<'tcx, 'mir>(
ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
mut e: InterpErrorInfo<'tcx>,
) -> Option<i64> {
use InterpError::*;
let (title, helps) = match e.kind {
MachineStop(ref info) => {
let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
use TerminationInfo::*;
let title = match info {
Exit(code) => return Some(*code),
Abort(_) =>
"abnormal termination",
UnsupportedInIsolation(_) =>
"unsupported operation",
ExperimentalUb { .. } =>
"Undefined Behavior",
};
let helps = match info {
UnsupportedInIsolation(_) =>
vec![format!("pass the flag `-Zmiri-disable-isolation` to disable isolation")],
ExperimentalUb { url, .. } =>
vec![
format!("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental"),
format!("see {} for further information", url),
],
_ => vec![],
};
(title, helps)
}
_ => {
let title = match e.kind {
Unsupported(_) =>
"unsupported operation",
UndefinedBehavior(_) =>
"Undefined Behavior",
ResourceExhaustion(_) =>
"resource exhaustion",
_ =>
bug!("This error should be impossible in Miri: {}", e),
};
let helps = match e.kind {
Unsupported(UnsupportedOpInfo::NoMirFor(..)) =>
vec![format!("make sure to use a Miri sysroot, which you can prepare with `cargo miri setup`")],
Unsupported(_) =>
vec![format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support")],
UndefinedBehavior(_) =>
vec![
format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"),
],
_ => vec![],
};
(title, helps)
}
};
e.print_backtrace();
let msg = e.to_string();
report_msg(ecx, &format!("{}: {}", title, msg), msg, &helps, true)
}
/// Report an error or note (depending on the `error` argument) at the current frame's current statement.
/// Also emits a full stacktrace of the interpreter stack.
fn report_msg<'tcx, 'mir>(
ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
title: &str,
span_msg: String,
helps: &[String],
error: bool,
) -> Option<i64> {
let span = if let Some(frame) = ecx.stack().last() {
frame.current_source_info().unwrap().span
} else {
DUMMY_SP
};
let mut err = if error {
ecx.tcx.sess.struct_span_err(span, title)
} else {
ecx.tcx.sess.diagnostic().span_note_diag(span, title)
};
err.span_label(span, span_msg);
for help in helps {
err.help(help);
}
// Add backtrace
let frames = ecx.generate_stacktrace(None);
// We iterate with indices because we need to look at the next frame (the caller).
for idx in 0..frames.len() {
let frame_info = &frames[idx];
let call_site_is_local = frames
.get(idx + 1)
.map_or(false, |caller_info| caller_info.instance.def_id().is_local());
if call_site_is_local {
err.span_note(frame_info.call_site, &frame_info.to_string());
} else {
err.note(&frame_info.to_string());
}
}
err.emit();
for (i, frame) in ecx.stack().iter().enumerate() {
trace!("-------------------");
trace!("Frame {}", i);
trace!(" return: {:?}", frame.return_place.map(|p| *p));
for (i, local) in frame.locals.iter().enumerate() {
trace!(" local {}: {:?}", i, local.value);
}
}
// Let the reported error determine the return code.
return None;
}
thread_local! {
static DIAGNOSTICS: RefCell<Vec<NonHaltingDiagnostic>> = RefCell::new(Vec::new());
}
/// Schedule a diagnostic for emitting. This function works even if you have no `InterpCx` available.
/// The diagnostic will be emitted after the current interpreter step is finished.
pub fn register_diagnostic(e: NonHaltingDiagnostic) {
DIAGNOSTICS.with(|diagnostics| diagnostics.borrow_mut().push(e));
}
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
/// Emit all diagnostics that were registed with `register_diagnostics`
fn process_diagnostics(&self) {
let this = self.eval_context_ref();
DIAGNOSTICS.with(|diagnostics| {
for e in diagnostics.borrow_mut().drain(..) {
use NonHaltingDiagnostic::*;
let msg = match e {
PoppedTrackedPointerTag(item) =>
format!("popped tracked tag for item {:?}", item),
CreatedAlloc(AllocId(id)) =>
format!("created allocation with id {}", id),
};
report_msg(this, "tracking was triggered", msg, &[], false);
}
});
}
}