]> git.lizzy.rs Git - rust.git/blob - src/bin/miri-rustc-tests.rs
9ef64c38638b7033fd22e20c1d1c7342f3baf3e5
[rust.git] / src / bin / miri-rustc-tests.rs
1 #![feature(rustc_private)]
2 extern crate miri;
3 extern crate getopts;
4 extern crate rustc;
5 extern crate rustc_metadata;
6 extern crate rustc_driver;
7 extern crate rustc_errors;
8 extern crate rustc_codegen_utils;
9 extern crate rustc_interface;
10 extern crate syntax;
11
12 use std::path::Path;
13 use std::io::Write;
14 use std::sync::{Mutex, Arc};
15 use std::io;
16
17
18 use rustc_interface::interface;
19 use rustc::hir::{self, itemlikevisit};
20 use rustc::ty::TyCtxt;
21 use rustc::hir::def_id::LOCAL_CRATE;
22 use rustc_driver::Compilation;
23
24 use miri::MiriConfig;
25
26 struct MiriCompilerCalls {
27     /// whether we are building for the host
28     host_target: bool,
29 }
30
31 impl rustc_driver::Callbacks for MiriCompilerCalls {
32     fn after_parsing(&mut self, compiler: &interface::Compiler) -> Compilation {
33         let attr = (
34             syntax::symbol::Symbol::intern("miri"),
35             syntax::feature_gate::AttributeType::Whitelisted,
36         );
37         compiler.session().plugin_attributes.borrow_mut().push(attr);
38
39         Compilation::Continue
40     }
41
42     fn after_analysis(&mut self, compiler: &interface::Compiler) -> Compilation {
43         compiler.session().abort_if_errors();
44         compiler.global_ctxt().unwrap().peek_mut().enter(|tcx| {
45             if std::env::args().any(|arg| arg == "--test") {
46                 struct Visitor<'tcx>(TyCtxt<'tcx>);
47                 impl<'tcx, 'hir> itemlikevisit::ItemLikeVisitor<'hir> for Visitor<'tcx> {
48                     fn visit_item(&mut self, i: &'hir hir::Item) {
49                         if let hir::ItemKind::Fn(.., body_id) = i.node {
50                             if i.attrs.iter().any(|attr| attr.check_name(syntax::symbol::sym::test)) {
51                                 let config = MiriConfig {
52                                     validate: true,
53                                     communicate: false,
54                                     args: vec![],
55                                     seed: None,
56                                 };
57                                 let did = self.0.hir().body_owner_def_id(body_id);
58                                 println!("running test: {}", self.0.def_path_debug_str(did));
59                                 miri::eval_main(self.0, did, config);
60                                 self.0.sess.abort_if_errors();
61                             }
62                         }
63                     }
64                     fn visit_trait_item(&mut self, _trait_item: &'hir hir::TraitItem) {}
65                     fn visit_impl_item(&mut self, _impl_item: &'hir hir::ImplItem) {}
66                 }
67                 tcx.hir().krate().visit_all_item_likes(&mut Visitor(tcx));
68             } else if let Some((entry_def_id, _)) = tcx.entry_fn(LOCAL_CRATE) {
69                 let config = MiriConfig {
70                     validate: true,
71                     communicate: false,
72                     args: vec![],
73                     seed: None
74                 };
75                 miri::eval_main(tcx, entry_def_id, config);
76
77                 compiler.session().abort_if_errors();
78             } else {
79                 println!("no main function found, assuming auxiliary build");
80             }
81         });
82
83         // Continue execution on host target
84         if self.host_target { Compilation::Continue } else { Compilation::Stop }
85     }
86 }
87
88 fn main() {
89     let path = option_env!("MIRI_RUSTC_TEST")
90         .map(String::from)
91         .unwrap_or_else(|| {
92             std::env::var("MIRI_RUSTC_TEST")
93                 .expect("need to set MIRI_RUSTC_TEST to path of rustc tests")
94         });
95
96     let mut mir_not_found = Vec::new();
97     let mut crate_not_found = Vec::new();
98     let mut success = 0;
99     let mut failed = Vec::new();
100     let mut c_abi_fns = Vec::new();
101     let mut abi = Vec::new();
102     let mut unsupported = Vec::new();
103     let mut unimplemented_intrinsic = Vec::new();
104     let mut limits = Vec::new();
105     let mut files: Vec<_> = std::fs::read_dir(path).unwrap().collect();
106     while let Some(file) = files.pop() {
107         let file = file.unwrap();
108         let path = file.path();
109         if file.metadata().unwrap().is_dir() {
110             if !path.to_str().unwrap().ends_with("auxiliary") {
111                 // add subdirs recursively
112                 files.extend(std::fs::read_dir(path).unwrap());
113             }
114             continue;
115         }
116         if !file.metadata().unwrap().is_file() || !path.to_str().unwrap().ends_with(".rs") {
117             continue;
118         }
119         let stderr = std::io::stderr();
120         write!(stderr.lock(), "test [miri-pass] {} ... ", path.display()).unwrap();
121         let mut host_target = false;
122         let mut args: Vec<String> = std::env::args().filter(|arg| {
123             if arg == "--miri_host_target" {
124                 host_target = true;
125                 false // remove the flag, rustc doesn't know it
126             } else {
127                 true
128             }
129         }).collect();
130         args.splice(1..1, miri::miri_default_args().iter().map(ToString::to_string));
131         // file to process
132         args.push(path.display().to_string());
133
134         let sysroot_flag = String::from("--sysroot");
135         if !args.contains(&sysroot_flag) {
136             args.push(sysroot_flag);
137             args.push(Path::new(&std::env::var("HOME").unwrap()).join(".xargo").join("HOST").display().to_string());
138         }
139
140         // A threadsafe buffer for writing.
141         #[derive(Default, Clone)]
142         struct BufWriter(Arc<Mutex<Vec<u8>>>);
143
144         impl Write for BufWriter {
145             fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
146                 self.0.lock().unwrap().write(buf)
147             }
148             fn flush(&mut self) -> io::Result<()> {
149                 self.0.lock().unwrap().flush()
150             }
151         }
152         let buf = BufWriter::default();
153         let output = buf.clone();
154         let result = std::panic::catch_unwind(|| {
155             let _ = rustc_driver::run_compiler(&args, &mut MiriCompilerCalls { host_target }, None, Some(Box::new(buf)));
156         });
157
158         match result {
159             Ok(()) => {
160                 success += 1;
161                 writeln!(stderr.lock(), "ok").unwrap()
162             },
163             Err(_) => {
164                 let output = output.0.lock().unwrap();
165                 let output_err = std::str::from_utf8(&output).unwrap();
166                 if let Some(text) = output_err.splitn(2, "no mir for `").nth(1) {
167                     let end = text.find('`').unwrap();
168                     mir_not_found.push(text[..end].to_string());
169                     writeln!(stderr.lock(), "NO MIR FOR `{}`", &text[..end]).unwrap();
170                 } else if let Some(text) = output_err.splitn(2, "can't find crate for `").nth(1) {
171                     let end = text.find('`').unwrap();
172                     crate_not_found.push(text[..end].to_string());
173                     writeln!(stderr.lock(), "CAN'T FIND CRATE FOR `{}`", &text[..end]).unwrap();
174                 } else {
175                     for text in output_err.split("error: ").skip(1) {
176                         let end = text.find('\n').unwrap_or(text.len());
177                         let c_abi = "can't call C ABI function: ";
178                         let unimplemented_intrinsic_s = "unimplemented intrinsic: ";
179                         let unsupported_s = "miri does not support ";
180                         let abi_s = "can't handle function with ";
181                         let limit_s = "reached the configured maximum ";
182                         if text.starts_with(c_abi) {
183                             c_abi_fns.push(text[c_abi.len()..end].to_string());
184                         } else if text.starts_with(unimplemented_intrinsic_s) {
185                             unimplemented_intrinsic.push(text[unimplemented_intrinsic_s.len()..end].to_string());
186                         } else if text.starts_with(unsupported_s) {
187                             unsupported.push(text[unsupported_s.len()..end].to_string());
188                         } else if text.starts_with(abi_s) {
189                             abi.push(text[abi_s.len()..end].to_string());
190                         } else if text.starts_with(limit_s) {
191                             limits.push(text[limit_s.len()..end].to_string());
192                         } else if text.find("aborting").is_none() {
193                             failed.push(text[..end].to_string());
194                         }
195                     }
196                     writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap();
197                 }
198             }
199         }
200     }
201     let stderr = std::io::stderr();
202     let mut stderr = stderr.lock();
203     writeln!(stderr, "{} success, {} no mir, {} crate not found, {} failed, \
204                         {} C fn, {} ABI, {} unsupported, {} intrinsic",
205                         success, mir_not_found.len(), crate_not_found.len(), failed.len(),
206                         c_abi_fns.len(), abi.len(), unsupported.len(), unimplemented_intrinsic.len()).unwrap();
207     writeln!(stderr, "# The \"other reasons\" errors").unwrap();
208     writeln!(stderr, "(sorted, deduplicated)").unwrap();
209     print_vec(&mut stderr, failed);
210
211     writeln!(stderr, "# can't call C ABI function").unwrap();
212     print_vec(&mut stderr, c_abi_fns);
213
214     writeln!(stderr, "# unsupported ABI").unwrap();
215     print_vec(&mut stderr, abi);
216
217     writeln!(stderr, "# unsupported").unwrap();
218     print_vec(&mut stderr, unsupported);
219
220     writeln!(stderr, "# unimplemented intrinsics").unwrap();
221     print_vec(&mut stderr, unimplemented_intrinsic);
222
223     writeln!(stderr, "# mir not found").unwrap();
224     print_vec(&mut stderr, mir_not_found);
225
226     writeln!(stderr, "# crate not found").unwrap();
227     print_vec(&mut stderr, crate_not_found);
228 }
229
230 fn print_vec<W: std::io::Write>(stderr: &mut W, v: Vec<String>) {
231     writeln!(stderr, "```").unwrap();
232     for (n, s) in vec_to_hist(v).into_iter().rev() {
233         writeln!(stderr, "{:4} {}", n, s).unwrap();
234     }
235     writeln!(stderr, "```").unwrap();
236 }
237
238 fn vec_to_hist<T: PartialEq + Ord>(mut v: Vec<T>) -> Vec<(usize, T)> {
239     v.sort();
240     let mut v = v.into_iter();
241     let mut result = Vec::new();
242     let mut current = v.next();
243     'outer: while let Some(current_val) = current {
244         let mut n = 1;
245         for next in &mut v {
246             if next == current_val {
247                 n += 1;
248             } else {
249                 result.push((n, current_val));
250                 current = Some(next);
251                 continue 'outer;
252             }
253         }
254         result.push((n, current_val));
255         break;
256     }
257     result.sort();
258     result
259 }