X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fdiagnostics.rs;h=45c0996355bffd43238dd1b4a7436aa395708d1e;hb=2670839e1af540a496a0d889fce9ad42529ecc11;hp=114f1d9be3623bdb77d95d582b897398d02811ca;hpb=e267fb4edec86c84b6c0e415a647ba9f8b9f0c9e;p=rust.git diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 114f1d9be36..45c0996355b 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -1,31 +1,31 @@ use std::cell::RefCell; use std::fmt; +use std::num::NonZeroU64; use log::trace; -use rustc_span::DUMMY_SP; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::{source_map::DUMMY_SP, Span}; use crate::*; /// Details of premature program termination. pub enum TerminationInfo { Exit(i64), - Abort(Option), + Abort(String), UnsupportedInIsolation(String), ExperimentalUb { msg: String, url: String }, Deadlock, } -impl fmt::Debug for TerminationInfo { +impl fmt::Display 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), + Abort(msg) => + write!(f, "{}", msg), UnsupportedInIsolation(msg) => write!(f, "{}", msg), ExperimentalUb { msg, .. } => @@ -40,7 +40,9 @@ impl MachineStopType for TerminationInfo {} /// Miri specific diagnostics pub enum NonHaltingDiagnostic { - PoppedTrackedPointerTag(Item), + CreatedPointerTag(NonZeroU64), + PoppedPointerTag(Item), + CreatedCallId(CallId), CreatedAlloc(AllocId), FreedAlloc(AllocId), } @@ -48,11 +50,11 @@ pub enum NonHaltingDiagnostic { /// Emit a custom diagnostic without going through the miri-engine machinery pub fn report_error<'tcx, 'mir>( ecx: &InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, - mut e: InterpErrorInfo<'tcx>, + e: InterpErrorInfo<'tcx>, ) -> Option { use InterpError::*; - let (title, helps) = match &e.kind { + let (title, helps) = match &e.kind() { MachineStop(info) => { let info = info.downcast_ref::().expect("invalid MachineStop payload"); use TerminationInfo::*; @@ -79,28 +81,31 @@ pub fn report_error<'tcx, 'mir>( (title, helps) } _ => { - let title = match e.kind { + let title = match e.kind() { Unsupported(_) => "unsupported operation", UndefinedBehavior(_) => "Undefined Behavior", ResourceExhaustion(_) => "resource exhaustion", + InvalidProgram(InvalidProgramInfo::ReferencedConstant) => + "post-monomorphization error", _ => bug!("This error should be impossible in Miri: {}", e), }; - let helps = match e.kind { + 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(UnsupportedOpInfo::ReadBytesAsPointer) => - panic!("`ReadBytesAsPointer` cannot be raised by Miri"), + Unsupported(UnsupportedOpInfo::ReadBytesAsPointer | UnsupportedOpInfo::ThreadLocalStatic(_) | UnsupportedOpInfo::ReadExternStatic(_)) => + panic!("Error should never be raised by Miri: {:?}", e.kind()), 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(UndefinedBehaviorInfo::AlignmentCheckFailed { .. }) => + UndefinedBehavior(UndefinedBehaviorInfo::AlignmentCheckFailed { .. }) + if ecx.memory.extra.check_alignment == AlignmentCheck::Symbolic + => vec![ format!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior"), - format!("but alignment errors can also be false positives, see https://github.com/rust-lang/miri/issues/1074"), - format!("you can disable the alignment check with `-Zmiri-disable-alignment-check`, but that could hide true bugs") + format!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives"), ], UndefinedBehavior(_) => vec![ @@ -115,41 +120,59 @@ pub fn report_error<'tcx, 'mir>( e.print_backtrace(); let msg = e.to_string(); - report_msg(ecx, &format!("{}: {}", title, msg), msg, helps, true); + report_msg(*ecx.tcx, /*error*/true, &format!("{}: {}", title, msg), msg, helps, &ecx.generate_stacktrace()); + + // Debug-dump all locals. + for (i, frame) in ecx.active_thread_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); + } + } // Extra output to help debug specific issues. - if let UndefinedBehavior(UndefinedBehaviorInfo::InvalidUndefBytes(Some(ptr))) = e.kind { - eprintln!( - "Uninitialized read occurred at offset 0x{:x} into this allocation:", - ptr.offset.bytes(), - ); - ecx.memory.dump_alloc(ptr.alloc_id); - eprintln!(); + match e.kind() { + UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some(access))) => { + eprintln!( + "Uninitialized read occurred at offsets 0x{:x}..0x{:x} into this allocation:", + access.uninit_ptr.offset.bytes(), + access.uninit_ptr.offset.bytes() + access.uninit_size.bytes(), + ); + eprintln!("{:?}", ecx.memory.dump_alloc(access.uninit_ptr.alloc_id)); + } + _ => {} } None } -/// Report an error or note (depending on the `error` argument) at the current frame's current statement. +/// Report an error or note (depending on the `error` argument) with the given stacktrace. /// Also emits a full stacktrace of the interpreter stack. -fn report_msg<'tcx, 'mir>( - ecx: &InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, +fn report_msg<'tcx>( + tcx: TyCtxt<'tcx>, + error: bool, title: &str, span_msg: String, mut helps: Vec, - error: bool, + stacktrace: &[FrameInfo<'tcx>], ) { - let span = if let Some(frame) = ecx.machine.stack.last() { - frame.current_source_info().unwrap().span - } else { - DUMMY_SP - }; + let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span); let mut err = if error { - ecx.tcx.sess.struct_span_err(span, title) + tcx.sess.struct_span_err(span, title) } else { - ecx.tcx.sess.diagnostic().span_note_diag(span, title) + tcx.sess.diagnostic().span_note_diag(span, title) }; - err.span_label(span, span_msg); + // Show main message. + if span != DUMMY_SP { + err.span_label(span, span_msg); + } else { + // Make sure we show the message even when it is a dummy span. + err.note(&span_msg); + err.note("(no span available)"); + } + // Show help messages. if !helps.is_empty() { // Add visual separator before backtrace. helps.last_mut().unwrap().push_str("\n"); @@ -158,8 +181,7 @@ fn report_msg<'tcx, 'mir>( } } // Add backtrace - let frames = ecx.generate_stacktrace(); - for (idx, frame_info) in frames.iter().enumerate() { + for (idx, frame_info) in stacktrace.iter().enumerate() { let is_local = frame_info.instance.def_id().is_local(); // No span for non-local frames and the first frame (which is the error site). if is_local && idx > 0 { @@ -170,15 +192,6 @@ fn report_msg<'tcx, 'mir>( } err.emit(); - - for (i, frame) in ecx.machine.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); - } - } } thread_local! { @@ -191,23 +204,80 @@ pub fn register_diagnostic(e: NonHaltingDiagnostic) { DIAGNOSTICS.with(|diagnostics| diagnostics.borrow_mut().push(e)); } +/// Remember enough about the topmost frame so that we can restore the stack +/// after a step was taken. +pub struct TopFrameInfo<'tcx> { + stack_size: usize, + instance: Option>, + span: Span, +} + impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { + fn preprocess_diagnostics(&self) -> TopFrameInfo<'tcx> { + // Ensure we have no lingering diagnostics. + DIAGNOSTICS.with(|diagnostics| assert!(diagnostics.borrow().is_empty())); + + let this = self.eval_context_ref(); + if this.active_thread_stack().is_empty() { + // Diagnostics can happen even with the empty stack (e.g. deallocation of thread-local statics). + return TopFrameInfo { stack_size: 0, instance: None, span: DUMMY_SP }; + } + let frame = this.frame(); + + TopFrameInfo { + stack_size: this.active_thread_stack().len(), + instance: Some(frame.instance), + span: frame.current_span(), + } + } + /// Emit all diagnostics that were registed with `register_diagnostics` - fn process_diagnostics(&self) { + fn process_diagnostics(&self, info: TopFrameInfo<'tcx>) { let this = self.eval_context_ref(); DIAGNOSTICS.with(|diagnostics| { - for e in diagnostics.borrow_mut().drain(..) { + let mut diagnostics = diagnostics.borrow_mut(); + if diagnostics.is_empty() { + return; + } + // We need to fix up the stack trace, because the machine has already + // stepped to the next statement. + let mut stacktrace = this.generate_stacktrace(); + // Remove newly pushed frames. + while stacktrace.len() > info.stack_size { + stacktrace.remove(0); + } + // Add popped frame back. + if stacktrace.len() < info.stack_size { + assert!(stacktrace.len() == info.stack_size-1, "we should never pop more than one frame at once"); + let frame_info = FrameInfo { + instance: info.instance.unwrap(), + span: info.span, + lint_root: None, + }; + stacktrace.insert(0, frame_info); + } else if let Some(instance) = info.instance { + // Adjust topmost frame. + stacktrace[0].span = info.span; + assert_eq!(stacktrace[0].instance, instance, "we should not pop and push a frame in one step"); + } + + // Show diagnostics. + for e in diagnostics.drain(..) { use NonHaltingDiagnostic::*; let msg = match e { - PoppedTrackedPointerTag(item) => + CreatedPointerTag(tag) => + format!("created tag {:?}", tag), + PoppedPointerTag(item) => format!("popped tracked tag for item {:?}", item), + CreatedCallId(id) => + format!("function call with id {}", id), CreatedAlloc(AllocId(id)) => format!("created allocation with id {}", id), FreedAlloc(AllocId(id)) => format!("freed allocation with id {}", id), }; - report_msg(this, "tracking was triggered", msg, vec![], false); + report_msg(*this.tcx, /*error*/false, "tracking was triggered", msg, vec![], &stacktrace); } }); }