]> git.lizzy.rs Git - rust.git/blob - src/bin/miri.rs
Fixed nits raised in review.
[rust.git] / src / bin / miri.rs
1 #![feature(rustc_private)]
2
3 extern crate env_logger;
4 extern crate getopts;
5 #[macro_use]
6 extern crate log;
7 extern crate log_settings;
8 extern crate miri;
9 extern crate rustc;
10 extern crate rustc_metadata;
11 extern crate rustc_driver;
12 extern crate rustc_errors;
13 extern crate rustc_codegen_utils;
14 extern crate syntax;
15
16 use std::path::PathBuf;
17 use std::str::FromStr;
18 use std::env;
19
20 use miri::MiriConfig;
21 use rustc::session::Session;
22 use rustc_metadata::cstore::CStore;
23 use rustc_driver::{Compilation, CompilerCalls, RustcDefaultCalls};
24 use rustc_driver::driver::{CompileState, CompileController};
25 use rustc::session::config::{self, Input, ErrorOutputType};
26 use rustc_codegen_utils::codegen_backend::CodegenBackend;
27 use rustc::hir::def_id::LOCAL_CRATE;
28 use syntax::ast;
29
30 struct MiriCompilerCalls {
31     default: Box<RustcDefaultCalls>,
32     miri_config: MiriConfig,
33 }
34
35 impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
36     fn early_callback(
37         &mut self,
38         matches: &getopts::Matches,
39         sopts: &config::Options,
40         cfg: &ast::CrateConfig,
41         descriptions: &rustc_errors::registry::Registry,
42         output: ErrorOutputType,
43     ) -> Compilation {
44         self.default.early_callback(
45             matches,
46             sopts,
47             cfg,
48             descriptions,
49             output,
50         )
51     }
52     fn no_input(
53         &mut self,
54         matches: &getopts::Matches,
55         sopts: &config::Options,
56         cfg: &ast::CrateConfig,
57         odir: &Option<PathBuf>,
58         ofile: &Option<PathBuf>,
59         descriptions: &rustc_errors::registry::Registry,
60     ) -> Option<(Input, Option<PathBuf>)> {
61         self.default.no_input(
62             matches,
63             sopts,
64             cfg,
65             odir,
66             ofile,
67             descriptions,
68         )
69     }
70     fn late_callback(
71         &mut self,
72         codegen_backend: &CodegenBackend,
73         matches: &getopts::Matches,
74         sess: &Session,
75         cstore: &CStore,
76         input: &Input,
77         odir: &Option<PathBuf>,
78         ofile: &Option<PathBuf>,
79     ) -> Compilation {
80         // Called *before* `build_controller`. Add filename to `miri` arguments.
81         self.miri_config.args.insert(0, input.filestem().to_string());
82         self.default.late_callback(codegen_backend, matches, sess, cstore, input, odir, ofile)
83     }
84     fn build_controller(
85         self: Box<Self>,
86         sess: &Session,
87         matches: &getopts::Matches,
88     ) -> CompileController<'a> {
89         let this = *self;
90         let mut control = this.default.build_controller(sess, matches);
91         control.after_hir_lowering.callback = Box::new(after_hir_lowering);
92         let miri_config = this.miri_config;
93         control.after_analysis.callback =
94             Box::new(move |state| after_analysis(state, miri_config.clone()));
95         control.after_analysis.stop = Compilation::Stop;
96         control
97     }
98 }
99
100 fn after_hir_lowering(state: &mut CompileState) {
101     let attr = (
102         String::from("miri"),
103         syntax::feature_gate::AttributeType::Whitelisted,
104     );
105     state.session.plugin_attributes.borrow_mut().push(attr);
106 }
107
108 fn after_analysis<'a, 'tcx>(
109     state: &mut CompileState<'a, 'tcx>,
110     miri_config: MiriConfig,
111 ) {
112     init_late_loggers();
113     state.session.abort_if_errors();
114
115     let tcx = state.tcx.unwrap();
116
117
118     let (entry_def_id, _) = tcx.entry_fn(LOCAL_CRATE).expect("no main function found!");
119
120     miri::eval_main(tcx, entry_def_id, miri_config);
121
122     state.session.abort_if_errors();
123 }
124
125 fn init_early_loggers() {
126     // Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to
127     // initialize them both, and we always initialize `miri`'s first.
128     let env = env_logger::Env::new().filter("MIRI_LOG").write_style("MIRI_LOG_STYLE");
129     env_logger::init_from_env(env);
130     // We only initialize `rustc` if the env var is set (so the user asked for it).
131     // If it is not set, we avoid initializing now so that we can initialize
132     // later with our custom settings, and *not* log anything for what happens before
133     // `miri` gets started.
134     if env::var("RUST_LOG").is_ok() {
135         rustc_driver::init_rustc_env_logger();
136     }
137 }
138
139 fn init_late_loggers() {
140     // We initialize loggers right before we start evaluation. We overwrite the `RUST_LOG`
141     // env var if it is not set, control it based on `MIRI_LOG`.
142     if let Ok(var) = env::var("MIRI_LOG") {
143         if env::var("RUST_LOG").is_err() {
144             // We try to be a bit clever here: if `MIRI_LOG` is just a single level
145             // used for everything, we only apply it to the parts of rustc that are
146             // CTFE-related. Otherwise, we use it verbatim for `RUST_LOG`.
147             // This way, if you set `MIRI_LOG=trace`, you get only the right parts of
148             // rustc traced, but you can also do `MIRI_LOG=miri=trace,rustc_mir::interpret=debug`.
149             if log::Level::from_str(&var).is_ok() {
150                 env::set_var("RUST_LOG",
151                     &format!("rustc::mir::interpret={0},rustc_mir::interpret={0}", var));
152             } else {
153                 env::set_var("RUST_LOG", &var);
154             }
155             rustc_driver::init_rustc_env_logger();
156         }
157     }
158
159     // If `MIRI_BACKTRACE` is set and `RUST_CTFE_BACKTRACE` is not, set `RUST_CTFE_BACKTRACE`.
160     // Do this late, so we really only apply this to miri's errors.
161     if let Ok(var) = env::var("MIRI_BACKTRACE") {
162         if env::var("RUST_CTFE_BACKTRACE") == Err(env::VarError::NotPresent) {
163             env::set_var("RUST_CTFE_BACKTRACE", &var);
164         }
165     }
166 }
167
168 fn find_sysroot() -> String {
169     if let Ok(sysroot) = std::env::var("MIRI_SYSROOT") {
170         return sysroot;
171     }
172
173     // Taken from PR <https://github.com/Manishearth/rust-clippy/pull/911>.
174     let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
175     let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
176     match (home, toolchain) {
177         (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
178         _ => {
179             option_env!("RUST_SYSROOT")
180                 .expect(
181                     "could not find sysroot. Either set `MIRI_SYSROOT` at run-time, or at \
182                      build-time specify `RUST_SYSROOT` env var or use rustup or multirust",
183                 )
184                 .to_owned()
185         }
186     }
187 }
188
189 fn main() {
190     init_early_loggers();
191
192     // Parse our arguments and split them across `rustc` and `miri`.
193     let mut validate = true;
194     let mut rustc_args = vec![];
195     let mut miri_args = vec![];
196     let mut after_dashdash = false;
197     for arg in std::env::args() {
198         if rustc_args.is_empty() {
199             // Very first arg: for `rustc`.
200             rustc_args.push(arg);
201         }
202         else if after_dashdash {
203             // Everything that comes after are `miri` args.
204             miri_args.push(arg);
205         } else {
206             match arg.as_str() {
207                 "-Zmiri-disable-validation" => {
208                     validate = false;
209                 },
210                 "--" => {
211                     after_dashdash = true;
212                 }
213                 _ => {
214                     rustc_args.push(arg);
215                 }
216             }
217         }
218     }
219
220     // Determine sysroot and let rustc know about it.
221     let sysroot_flag = String::from("--sysroot");
222     if !rustc_args.contains(&sysroot_flag) {
223         rustc_args.push(sysroot_flag);
224         rustc_args.push(find_sysroot());
225     }
226     // Finally, add the default flags all the way in the beginning, but after the binary name.
227     rustc_args.splice(1..1, miri::miri_default_args().iter().map(ToString::to_string));
228
229     debug!("rustc arguments: {:?}", rustc_args);
230     debug!("miri arguments: {:?}", miri_args);
231     let miri_config = MiriConfig { validate, args: miri_args };
232     let result = rustc_driver::run(move || {
233         rustc_driver::run_compiler(&rustc_args, Box::new(MiriCompilerCalls {
234             default: Box::new(RustcDefaultCalls),
235             miri_config,
236         }), None, None)
237     });
238     std::process::exit(result as i32);
239 }