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