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