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