]> git.lizzy.rs Git - rust.git/blob - src/librustc/util/common.rs
Rollup merge of #60590 - petertodd:2018-test-union-nonzero, r=nikomatsakis,Centril
[rust.git] / src / librustc / util / common.rs
1 #![allow(non_camel_case_types)]
2
3 use rustc_data_structures::{fx::FxHashMap, sync::Lock};
4
5 use std::cell::{RefCell, Cell};
6 use std::fmt::Debug;
7 use std::hash::Hash;
8 use std::panic;
9 use std::env;
10 use std::time::{Duration, Instant};
11
12 use std::sync::mpsc::{Sender};
13 use syntax_pos::{SpanData};
14 use syntax::symbol::{Symbol, sym};
15 use rustc_macros::HashStable;
16 use crate::ty::TyCtxt;
17 use crate::dep_graph::{DepNode};
18 use lazy_static;
19 use crate::session::Session;
20
21 // The name of the associated type for `Fn` return types
22 pub const FN_OUTPUT_NAME: Symbol = sym::Output;
23
24 // Useful type to use with `Result<>` indicate that an error has already
25 // been reported to the user, so no need to continue checking.
26 #[derive(Clone, Copy, Debug, RustcEncodable, RustcDecodable, HashStable)]
27 pub struct ErrorReported;
28
29 thread_local!(static TIME_DEPTH: Cell<usize> = Cell::new(0));
30
31 lazy_static! {
32     static ref DEFAULT_HOOK: Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static> = {
33         let hook = panic::take_hook();
34         panic::set_hook(Box::new(panic_hook));
35         hook
36     };
37 }
38
39 fn panic_hook(info: &panic::PanicInfo<'_>) {
40     (*DEFAULT_HOOK)(info);
41
42     let backtrace = env::var_os("RUST_BACKTRACE").map(|x| &x != "0").unwrap_or(false);
43
44     if backtrace {
45         TyCtxt::try_print_query_stack();
46     }
47
48         #[cfg(windows)]
49         unsafe {
50             if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
51                 extern "system" {
52                     fn DebugBreak();
53                 }
54                 // Trigger a debugger if we crashed during bootstrap
55                 DebugBreak();
56             }
57         }
58 }
59
60 pub fn install_panic_hook() {
61     lazy_static::initialize(&DEFAULT_HOOK);
62 }
63
64 /// Parameters to the `Dump` variant of type `ProfileQueriesMsg`.
65 #[derive(Clone,Debug)]
66 pub struct ProfQDumpParams {
67     /// A base path for the files we will dump.
68     pub path:String,
69     /// To ensure that the compiler waits for us to finish our dumps.
70     pub ack:Sender<()>,
71     /// Toggle dumping a log file with every `ProfileQueriesMsg`.
72     pub dump_profq_msg_log:bool,
73 }
74
75 #[allow(nonstandard_style)]
76 #[derive(Clone, Debug, PartialEq, Eq)]
77 pub struct QueryMsg {
78     pub query: &'static str,
79     pub msg: Option<String>,
80 }
81
82 /// A sequence of these messages induce a trace of query-based incremental compilation.
83 /// FIXME(matthewhammer): Determine whether we should include cycle detection here or not.
84 #[derive(Clone,Debug)]
85 pub enum ProfileQueriesMsg {
86     /// begin a timed pass
87     TimeBegin(String),
88     /// end a timed pass
89     TimeEnd,
90     /// begin a task (see dep_graph::graph::with_task)
91     TaskBegin(DepNode),
92     /// end a task
93     TaskEnd,
94     /// begin a new query
95     /// can't use `Span` because queries are sent to other thread
96     QueryBegin(SpanData, QueryMsg),
97     /// query is satisfied by using an already-known value for the given key
98     CacheHit,
99     /// query requires running a provider; providers may nest, permitting queries to nest.
100     ProviderBegin,
101     /// query is satisfied by a provider terminating with a value
102     ProviderEnd,
103     /// dump a record of the queries to the given path
104     Dump(ProfQDumpParams),
105     /// halt the profiling/monitoring background thread
106     Halt
107 }
108
109 /// If enabled, send a message to the profile-queries thread
110 pub fn profq_msg(sess: &Session, msg: ProfileQueriesMsg) {
111     if let Some(s) = sess.profile_channel.borrow().as_ref() {
112         s.send(msg).unwrap()
113     } else {
114         // Do nothing
115     }
116 }
117
118 /// Set channel for profile queries channel
119 pub fn profq_set_chan(sess: &Session, s: Sender<ProfileQueriesMsg>) -> bool {
120     let mut channel = sess.profile_channel.borrow_mut();
121     if channel.is_none() {
122         *channel = Some(s);
123         true
124     } else {
125         false
126     }
127 }
128
129 /// Read the current depth of `time()` calls. This is used to
130 /// encourage indentation across threads.
131 pub fn time_depth() -> usize {
132     TIME_DEPTH.with(|slot| slot.get())
133 }
134
135 /// Sets the current depth of `time()` calls. The idea is to call
136 /// `set_time_depth()` with the result from `time_depth()` in the
137 /// parent thread.
138 pub fn set_time_depth(depth: usize) {
139     TIME_DEPTH.with(|slot| slot.set(depth));
140 }
141
142 pub fn time<T, F>(sess: &Session, what: &str, f: F) -> T where
143     F: FnOnce() -> T,
144 {
145     time_ext(sess.time_passes(), Some(sess), what, f)
146 }
147
148 pub fn time_ext<T, F>(do_it: bool, sess: Option<&Session>, what: &str, f: F) -> T where
149     F: FnOnce() -> T,
150 {
151     if !do_it { return f(); }
152
153     let old = TIME_DEPTH.with(|slot| {
154         let r = slot.get();
155         slot.set(r + 1);
156         r
157     });
158
159     if let Some(sess) = sess {
160         if cfg!(debug_assertions) {
161             profq_msg(sess, ProfileQueriesMsg::TimeBegin(what.to_string()))
162         }
163     }
164     let start = Instant::now();
165     let rv = f();
166     let dur = start.elapsed();
167     if let Some(sess) = sess {
168         if cfg!(debug_assertions) {
169             profq_msg(sess, ProfileQueriesMsg::TimeEnd)
170         }
171     }
172
173     print_time_passes_entry_internal(what, dur);
174
175     TIME_DEPTH.with(|slot| slot.set(old));
176
177     rv
178 }
179
180 pub fn print_time_passes_entry(do_it: bool, what: &str, dur: Duration) {
181     if !do_it {
182         return
183     }
184
185     let old = TIME_DEPTH.with(|slot| {
186         let r = slot.get();
187         slot.set(r + 1);
188         r
189     });
190
191     print_time_passes_entry_internal(what, dur);
192
193     TIME_DEPTH.with(|slot| slot.set(old));
194 }
195
196 fn print_time_passes_entry_internal(what: &str, dur: Duration) {
197     let indentation = TIME_DEPTH.with(|slot| slot.get());
198
199     let mem_string = match get_resident() {
200         Some(n) => {
201             let mb = n as f64 / 1_000_000.0;
202             format!("; rss: {}MB", mb.round() as usize)
203         }
204         None => String::new(),
205     };
206     println!("{}time: {}{}\t{}",
207              "  ".repeat(indentation),
208              duration_to_secs_str(dur),
209              mem_string,
210              what);
211 }
212
213 // Hack up our own formatting for the duration to make it easier for scripts
214 // to parse (always use the same number of decimal places and the same unit).
215 pub fn duration_to_secs_str(dur: Duration) -> String {
216     const NANOS_PER_SEC: f64 = 1_000_000_000.0;
217     let secs = dur.as_secs() as f64 +
218                dur.subsec_nanos() as f64 / NANOS_PER_SEC;
219
220     format!("{:.3}", secs)
221 }
222
223 pub fn to_readable_str(mut val: usize) -> String {
224     let mut groups = vec![];
225     loop {
226         let group = val % 1000;
227
228         val /= 1000;
229
230         if val == 0 {
231             groups.push(group.to_string());
232             break;
233         } else {
234             groups.push(format!("{:03}", group));
235         }
236     }
237
238     groups.reverse();
239
240     groups.join("_")
241 }
242
243 pub fn record_time<T, F>(accu: &Lock<Duration>, f: F) -> T where
244     F: FnOnce() -> T,
245 {
246     let start = Instant::now();
247     let rv = f();
248     let duration = start.elapsed();
249     let mut accu = accu.lock();
250     *accu = *accu + duration;
251     rv
252 }
253
254 // Memory reporting
255 #[cfg(unix)]
256 fn get_resident() -> Option<usize> {
257     use std::fs;
258
259     let field = 1;
260     let contents = fs::read("/proc/self/statm").ok()?;
261     let contents = String::from_utf8(contents).ok()?;
262     let s = contents.split_whitespace().nth(field)?;
263     let npages = s.parse::<usize>().ok()?;
264     Some(npages * 4096)
265 }
266
267 #[cfg(windows)]
268 fn get_resident() -> Option<usize> {
269     type BOOL = i32;
270     type DWORD = u32;
271     type HANDLE = *mut u8;
272     use libc::size_t;
273     use std::mem;
274     #[repr(C)]
275     #[allow(non_snake_case)]
276     struct PROCESS_MEMORY_COUNTERS {
277         cb: DWORD,
278         PageFaultCount: DWORD,
279         PeakWorkingSetSize: size_t,
280         WorkingSetSize: size_t,
281         QuotaPeakPagedPoolUsage: size_t,
282         QuotaPagedPoolUsage: size_t,
283         QuotaPeakNonPagedPoolUsage: size_t,
284         QuotaNonPagedPoolUsage: size_t,
285         PagefileUsage: size_t,
286         PeakPagefileUsage: size_t,
287     }
288     type PPROCESS_MEMORY_COUNTERS = *mut PROCESS_MEMORY_COUNTERS;
289     #[link(name = "psapi")]
290     extern "system" {
291         fn GetCurrentProcess() -> HANDLE;
292         fn GetProcessMemoryInfo(Process: HANDLE,
293                                 ppsmemCounters: PPROCESS_MEMORY_COUNTERS,
294                                 cb: DWORD) -> BOOL;
295     }
296     let mut pmc: PROCESS_MEMORY_COUNTERS = unsafe { mem::zeroed() };
297     pmc.cb = mem::size_of_val(&pmc) as DWORD;
298     match unsafe { GetProcessMemoryInfo(GetCurrentProcess(), &mut pmc, pmc.cb) } {
299         0 => None,
300         _ => Some(pmc.WorkingSetSize as usize),
301     }
302 }
303
304 pub fn indent<R, F>(op: F) -> R where
305     R: Debug,
306     F: FnOnce() -> R,
307 {
308     // Use in conjunction with the log post-processor like `src/etc/indenter`
309     // to make debug output more readable.
310     debug!(">>");
311     let r = op();
312     debug!("<< (Result = {:?})", r);
313     r
314 }
315
316 pub struct Indenter {
317     _cannot_construct_outside_of_this_module: (),
318 }
319
320 impl Drop for Indenter {
321     fn drop(&mut self) { debug!("<<"); }
322 }
323
324 pub fn indenter() -> Indenter {
325     debug!(">>");
326     Indenter { _cannot_construct_outside_of_this_module: () }
327 }
328
329 pub trait MemoizationMap {
330     type Key: Clone;
331     type Value: Clone;
332
333     /// If `key` is present in the map, return the value,
334     /// otherwise invoke `op` and store the value in the map.
335     ///
336     /// N.B., if the receiver is a `DepTrackingMap`, special care is
337     /// needed in the `op` to ensure that the correct edges are
338     /// added into the dep graph. See the `DepTrackingMap` impl for
339     /// more details!
340     fn memoize<OP>(&self, key: Self::Key, op: OP) -> Self::Value
341         where OP: FnOnce() -> Self::Value;
342 }
343
344 impl<K, V> MemoizationMap for RefCell<FxHashMap<K,V>>
345     where K: Hash+Eq+Clone, V: Clone
346 {
347     type Key = K;
348     type Value = V;
349
350     fn memoize<OP>(&self, key: K, op: OP) -> V
351         where OP: FnOnce() -> V
352     {
353         let result = self.borrow().get(&key).cloned();
354         match result {
355             Some(result) => result,
356             None => {
357                 let result = op();
358                 self.borrow_mut().insert(key, result.clone());
359                 result
360             }
361         }
362     }
363 }
364
365 #[test]
366 fn test_to_readable_str() {
367     assert_eq!("0", to_readable_str(0));
368     assert_eq!("1", to_readable_str(1));
369     assert_eq!("99", to_readable_str(99));
370     assert_eq!("999", to_readable_str(999));
371     assert_eq!("1_000", to_readable_str(1_000));
372     assert_eq!("1_001", to_readable_str(1_001));
373     assert_eq!("999_999", to_readable_str(999_999));
374     assert_eq!("1_000_000", to_readable_str(1_000_000));
375     assert_eq!("1_234_567", to_readable_str(1_234_567));
376 }