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