]> git.lizzy.rs Git - rust.git/blob - src/bin/miri.rs
use the logging framework instead of println!
[rust.git] / src / bin / miri.rs
1 #![feature(rustc_private)]
2
3 extern crate getopts;
4 extern crate miri;
5 extern crate rustc;
6 extern crate rustc_driver;
7 extern crate env_logger;
8 extern crate log_settings;
9 extern crate syntax;
10 #[macro_use] extern crate log;
11
12 use miri::{
13     EvalContext,
14     CachedMir,
15     step,
16     EvalError,
17     Frame,
18 };
19 use rustc::session::Session;
20 use rustc_driver::{driver, CompilerCalls};
21 use rustc::ty::{TyCtxt, subst};
22 use rustc::hir::def_id::DefId;
23
24 struct MiriCompilerCalls;
25
26 impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
27     fn build_controller(
28         &mut self,
29         _: &Session,
30         _: &getopts::Matches
31     ) -> driver::CompileController<'a> {
32         let mut control = driver::CompileController::basic();
33
34         control.after_analysis.callback = Box::new(|state| {
35             state.session.abort_if_errors();
36
37             let tcx = state.tcx.unwrap();
38             let mir_map = state.mir_map.unwrap();
39
40             let (node_id, span) = state.session.entry_fn.borrow().expect("no main or start function found");
41             debug!("found `main` function at: {:?}", span);
42
43             let mir = mir_map.map.get(&node_id).expect("no mir for main function");
44             let def_id = tcx.map.local_def_id(node_id);
45             let mut ecx = EvalContext::new(tcx, mir_map);
46             let substs = tcx.mk_substs(subst::Substs::empty());
47             let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs).expect("main function should not be diverging");
48
49             ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr));
50
51             if mir.arg_decls.len() == 2 {
52                 // start function
53                 let ptr_size = ecx.memory().pointer_size;
54                 let nargs = ecx.memory_mut().allocate(ptr_size);
55                 ecx.memory_mut().write_usize(nargs, 0).unwrap();
56                 let args = ecx.memory_mut().allocate(ptr_size);
57                 ecx.memory_mut().write_usize(args, 0).unwrap();
58                 ecx.frame_mut().locals[0] = nargs;
59                 ecx.frame_mut().locals[1] = args;
60             }
61
62             loop {
63                 match step(&mut ecx) {
64                     Ok(true) => {}
65                     Ok(false) => break,
66                     // FIXME: diverging functions can end up here in some future miri
67                     Err(e) => {
68                         report(tcx, &ecx, e);
69                         break;
70                     }
71                 }
72             }
73         });
74
75         control
76     }
77 }
78
79 fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) {
80     let frame = ecx.stack().last().expect("stackframe was empty");
81     let block = &frame.mir.basic_blocks()[frame.next_block];
82     let span = if frame.stmt < block.statements.len() {
83         block.statements[frame.stmt].source_info.span
84     } else {
85         block.terminator().source_info.span
86     };
87     let mut err = tcx.sess.struct_span_err(span, &e.to_string());
88     for &Frame { def_id, substs, span, .. } in ecx.stack().iter().rev() {
89         // FIXME(solson): Find a way to do this without this Display impl hack.
90         use rustc::util::ppaux;
91         use std::fmt;
92         struct Instance<'tcx>(DefId, &'tcx subst::Substs<'tcx>);
93         impl<'tcx> fmt::Display for Instance<'tcx> {
94             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95                 ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[],
96                     |tcx| Some(tcx.lookup_item_type(self.0).generics))
97             }
98         }
99         err.span_note(span, &format!("inside call to {}", Instance(def_id, substs)));
100     }
101     err.emit();
102 }
103
104 fn main() {
105     init_logger();
106     let args: Vec<String> = std::env::args().collect();
107     rustc_driver::run_compiler(&args, &mut MiriCompilerCalls);
108 }
109
110 fn init_logger() {
111     const NSPACES: usize = 40;
112     let format = |record: &log::LogRecord| {
113         // prepend spaces to indent the final string
114         let indentation = log_settings::settings().indentation;
115         format!("{lvl}:{module}{depth:2}{indent:<indentation$} {text}",
116             lvl = record.level(),
117             module = record.location().module_path(),
118             depth = indentation / NSPACES,
119             indentation = indentation % NSPACES,
120             indent = "",
121             text = record.args())
122     };
123
124     let mut builder = env_logger::LogBuilder::new();
125     builder.format(format).filter(None, log::LogLevelFilter::Info);
126
127     if std::env::var("MIRI_LOG").is_ok() {
128         builder.parse(&std::env::var("MIRI_LOG").unwrap());
129     }
130
131     builder.init().unwrap();
132 }