Skip to content

Commit 19ecd13

Browse files
committed
Prune backtraces similar to RUST_BACKTRACE=1 logic
Previously, Miri would always print a backtrace including all frames when encountering an error. This adds -Zmiri-backtrace which defaults to 1, internally called BacktraceStyle::Short. By default, backtraces are pruned to start at __rust_begin_short_backtrace, similar to std. Then we also remove non-local frames from the bottom of the trace. This cleans up the last one or two shims outside main or a test. Users can opt out of pruning by setting -Zmiri-backtrace=full, and will be automatically opted out if there are no local frames because that means the reported error is likely in the Rust runtime, which this pruning is crafted to remove.
1 parent 2b0078e commit 19ecd13

File tree

5 files changed

+76
-2
lines changed

5 files changed

+76
-2
lines changed

src/bin/miri.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ use rustc_middle::{
2929
};
3030
use rustc_session::{config::ErrorOutputType, search_paths::PathKind, CtfeBacktrace};
3131

32+
use miri::BacktraceStyle;
33+
3234
struct MiriCompilerCalls {
3335
miri_config: miri::MiriConfig,
3436
}
@@ -462,6 +464,14 @@ fn main() {
462464
let measureme_out = arg.strip_prefix("-Zmiri-measureme=").unwrap();
463465
miri_config.measureme_out = Some(measureme_out.to_string());
464466
}
467+
arg if arg.starts_with("-Zmiri-backtrace=") => {
468+
miri_config.backtrace_style = match arg.strip_prefix("-Zmiri-backtrace=") {
469+
Some("0") => BacktraceStyle::Off,
470+
Some("1") => BacktraceStyle::Short,
471+
Some("full") => BacktraceStyle::Full,
472+
_ => panic!("-Zmiri-backtrace may only be 0, 1, or full"),
473+
};
474+
}
465475
_ => {
466476
// Forward to rustc.
467477
rustc_args.push(arg);

src/diagnostics.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,46 @@ pub fn report_error<'tcx, 'mir>(
157157
}
158158
};
159159

160+
let mut stacktrace = ecx.generate_stacktrace();
161+
let has_local_frame = stacktrace.iter().any(|frame| frame.instance.def_id().is_local());
162+
match ecx.machine.backtrace_style {
163+
BacktraceStyle::Off => {
164+
// Retain one frame so that we can print a span for the error itself
165+
stacktrace.truncate(1);
166+
}
167+
BacktraceStyle::Short => {
168+
// Only prune frames if there is at least one local frame. This check ensures that if
169+
// we get a backtrace that never makes it to the user code because it has detected a
170+
// bug in the Rust runtime, we don't prune away every frame.
171+
if has_local_frame {
172+
// This is part of the logic that `std` uses to select the relevant part of a
173+
// backtrace. But here, we only look for __rust_begin_short_backtrace, not
174+
// __rust_end_short_backtrace because the end symbol comes from a call to the default
175+
// panic handler.
176+
stacktrace = stacktrace
177+
.into_iter()
178+
.take_while(|frame| {
179+
let def_id = frame.instance.def_id();
180+
let path = ecx.tcx.tcx.def_path_str(def_id);
181+
!path.contains("__rust_begin_short_backtrace")
182+
})
183+
.collect::<Vec<_>>();
184+
185+
// After we prune frames from the bottom, there are a few left that are part of the
186+
// Rust runtime. So we remove frames until we get to a local symbol, which should be
187+
// main or a test.
188+
// This len check ensures that we don't somehow remove every frame, as doing so breaks
189+
// the primary error message.
190+
while stacktrace.len() > 1
191+
&& stacktrace.last().map_or(false, |e| !e.instance.def_id().is_local())
192+
{
193+
stacktrace.pop();
194+
}
195+
}
196+
}
197+
BacktraceStyle::Full => {}
198+
}
199+
160200
e.print_backtrace();
161201
let msg = e.to_string();
162202
report_msg(
@@ -165,9 +205,17 @@ pub fn report_error<'tcx, 'mir>(
165205
&if let Some(title) = title { format!("{}: {}", title, msg) } else { msg.clone() },
166206
msg,
167207
helps,
168-
&ecx.generate_stacktrace(),
208+
&stacktrace,
169209
);
170210

211+
// Include a note like `std` does for short backtraces, but since we are opt-out not opt-in, we
212+
// do not include a note when backtraces are off.
213+
if ecx.machine.backtrace_style == BacktraceStyle::Short && has_local_frame {
214+
ecx.tcx.sess.diagnostic().note_without_error(
215+
"some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
216+
);
217+
}
218+
171219
// Debug-dump all locals.
172220
for (i, frame) in ecx.active_thread_stack().iter().enumerate() {
173221
trace!("-------------------");

src/eval.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ pub enum IsolatedOp {
5757
Allow,
5858
}
5959

60+
#[derive(Copy, Clone, PartialEq, Eq)]
61+
pub enum BacktraceStyle {
62+
/// Prints a terser backtrace which ideally only contains relevant information.
63+
Short,
64+
/// Prints a backtrace with all possible information.
65+
Full,
66+
/// Prints only the frame that the error occurs in.
67+
Off,
68+
}
69+
6070
/// Configuration needed to spawn a Miri instance.
6171
#[derive(Clone)]
6272
pub struct MiriConfig {
@@ -98,6 +108,7 @@ pub struct MiriConfig {
98108
pub measureme_out: Option<String>,
99109
/// Panic when unsupported functionality is encountered
100110
pub panic_on_unsupported: bool,
111+
pub backtrace_style: BacktraceStyle,
101112
}
102113

103114
impl Default for MiriConfig {
@@ -121,6 +132,7 @@ impl Default for MiriConfig {
121132
cmpxchg_weak_failure_rate: 0.8,
122133
measureme_out: None,
123134
panic_on_unsupported: false,
135+
backtrace_style: BacktraceStyle::Short,
124136
}
125137
}
126138
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ pub use crate::diagnostics::{
6060
NonHaltingDiagnostic, TerminationInfo,
6161
};
6262
pub use crate::eval::{
63-
create_ecx, eval_entry, AlignmentCheck, IsolatedOp, MiriConfig, RejectOpWith,
63+
create_ecx, eval_entry, AlignmentCheck, BacktraceStyle, IsolatedOp, MiriConfig, RejectOpWith,
6464
};
6565
pub use crate::helpers::EvalContextExt as HelpersEvalContextExt;
6666
pub use crate::machine::{

src/machine.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,9 @@ pub struct Evaluator<'mir, 'tcx> {
343343
/// functionality is encountered. If `false`, an error is propagated in the Miri application context
344344
/// instead (default behavior)
345345
pub(crate) panic_on_unsupported: bool,
346+
347+
/// Equivalent setting as RUST_BACKTRACE on encountering an error.
348+
pub(crate) backtrace_style: BacktraceStyle,
346349
}
347350

348351
impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
@@ -374,6 +377,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
374377
string_cache: Default::default(),
375378
exported_symbols_cache: FxHashMap::default(),
376379
panic_on_unsupported: config.panic_on_unsupported,
380+
backtrace_style: config.backtrace_style,
377381
}
378382
}
379383

0 commit comments

Comments
 (0)