]> git.lizzy.rs Git - rust.git/blob - src/bin/miri.rs
728266a755d64c998040ca9b9ffece70d24b0bc5
[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::{eval_main, run_mir_passes};
13 use rustc::session::Session;
14 use rustc::mir::mir_map::MirMap;
15 use rustc_driver::{driver, CompilerCalls, Compilation};
16 use syntax::ast::MetaItemKind;
17
18 struct MiriCompilerCalls;
19
20 impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
21     fn build_controller(
22         &mut self,
23         _: &Session,
24         _: &getopts::Matches
25     ) -> driver::CompileController<'a> {
26         let mut control = driver::CompileController::basic();
27         control.after_hir_lowering.callback = Box::new(|state| {
28             state.session.plugin_attributes.borrow_mut().push(("miri".to_owned(), syntax::feature_gate::AttributeType::Whitelisted));
29         });
30         control.after_analysis.stop = Compilation::Stop;
31         control.after_analysis.callback = Box::new(|state| {
32             state.session.abort_if_errors();
33
34             let tcx = state.tcx.unwrap();
35             let mir_map = state.mir_map.unwrap();
36             let (entry_node_id, _) = state.session.entry_fn.borrow()
37                 .expect("no main or start function found");
38             let entry_def_id = tcx.map.local_def_id(entry_node_id);
39
40             let krate = state.hir_crate.as_ref().unwrap();
41             let mut memory_size = 100*1024*1024; // 100MB
42             let mut step_limit = 1000_000;
43             let mut stack_limit = 100;
44             fn extract_str(lit: &syntax::ast::Lit) -> syntax::parse::token::InternedString {
45                 match lit.node {
46                     syntax::ast::LitKind::Str(ref s, _) => s.clone(),
47                     _ => panic!("attribute values need to be strings"),
48                 }
49             }
50             for attr in krate.attrs.iter() {
51                 match attr.node.value.node {
52                     MetaItemKind::List(ref name, _) if name != "miri" => {}
53                     MetaItemKind::List(_, ref items) => for item in items {
54                         match item.node {
55                             MetaItemKind::NameValue(ref name, ref value) => {
56                                 match &**name {
57                                     "memory_size" => memory_size = extract_str(value).parse().expect("not a number"),
58                                     "step_limit" => step_limit = extract_str(value).parse().expect("not a number"),
59                                     "stack_limit" => stack_limit = extract_str(value).parse().expect("not a number"),
60                                     _ => state.session.span_err(item.span, "unknown miri attribute"),
61                                 }
62                             }
63                             _ => state.session.span_err(item.span, "miri attributes need to be of key = value kind"),
64                         }
65                     },
66                     _ => {},
67                 }
68             }
69
70             let mut mir_map_copy = MirMap::new(tcx.dep_graph.clone());
71             for def_id in mir_map.map.keys() {
72                 mir_map_copy.map.insert(def_id, mir_map.map.get(&def_id).unwrap().clone());
73             }
74             run_mir_passes(tcx, &mut mir_map_copy);
75             eval_main(tcx, &mir_map_copy, entry_def_id, memory_size, step_limit, stack_limit);
76
77             state.session.abort_if_errors();
78         });
79
80         control
81     }
82 }
83
84 fn init_logger() {
85     const MAX_INDENT: usize = 40;
86
87     let format = |record: &log::LogRecord| {
88         if record.level() == log::LogLevel::Trace {
89             // prepend spaces to indent the final string
90             let indentation = log_settings::settings().indentation;
91             format!("{lvl}:{module}{depth:2}{indent:<indentation$} {text}",
92                 lvl = record.level(),
93                 module = record.location().module_path(),
94                 depth = indentation / MAX_INDENT,
95                 indentation = indentation % MAX_INDENT,
96                 indent = "",
97                 text = record.args())
98         } else {
99             format!("{lvl}:{module}: {text}",
100                 lvl = record.level(),
101                 module = record.location().module_path(),
102                 text = record.args())
103         }
104     };
105
106     let mut builder = env_logger::LogBuilder::new();
107     builder.format(format).filter(None, log::LogLevelFilter::Info);
108
109     if std::env::var("MIRI_LOG").is_ok() {
110         builder.parse(&std::env::var("MIRI_LOG").unwrap());
111     }
112
113     builder.init().unwrap();
114 }
115
116 fn find_sysroot() -> String {
117     // Taken from https://github.com/Manishearth/rust-clippy/pull/911.
118     let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
119     let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
120     match (home, toolchain) {
121         (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
122         _ => option_env!("RUST_SYSROOT")
123             .expect("need to specify RUST_SYSROOT env var or use rustup or multirust")
124             .to_owned(),
125     }
126 }
127
128 fn main() {
129     init_logger();
130     let mut args: Vec<String> = std::env::args().collect();
131
132     let sysroot_flag = String::from("--sysroot");
133     if !args.contains(&sysroot_flag) {
134         args.push(sysroot_flag);
135         args.push(find_sysroot());
136     }
137
138     rustc_driver::run_compiler(&args, &mut MiriCompilerCalls);
139 }