]> git.lizzy.rs Git - rust.git/blob - src/librustc/util/common.rs
Auto merge of #64455 - GuillaumeGomez:long-error-explanation-E0531, r=Centril
[rust.git] / src / librustc / util / common.rs
1 #![allow(non_camel_case_types)]
2
3 use rustc_data_structures::sync::Lock;
4
5 use std::cell::Cell;
6 use std::fmt::Debug;
7 use std::time::{Duration, Instant};
8
9 use std::sync::mpsc::{Sender};
10 use syntax_pos::{SpanData};
11 use syntax::symbol::{Symbol, sym};
12 use rustc_macros::HashStable;
13 use crate::dep_graph::{DepNode};
14 use crate::session::Session;
15
16 #[cfg(test)]
17 mod tests;
18
19 // The name of the associated type for `Fn` return types.
20 pub const FN_OUTPUT_NAME: Symbol = sym::Output;
21
22 // Useful type to use with `Result<>` indicate that an error has already
23 // been reported to the user, so no need to continue checking.
24 #[derive(Clone, Copy, Debug, RustcEncodable, RustcDecodable, HashStable)]
25 pub struct ErrorReported;
26
27 thread_local!(static TIME_DEPTH: Cell<usize> = Cell::new(0));
28
29 /// Parameters to the `Dump` variant of type `ProfileQueriesMsg`.
30 #[derive(Clone,Debug)]
31 pub struct ProfQDumpParams {
32     /// A base path for the files we will dump.
33     pub path:String,
34     /// To ensure that the compiler waits for us to finish our dumps.
35     pub ack:Sender<()>,
36     /// Toggle dumping a log file with every `ProfileQueriesMsg`.
37     pub dump_profq_msg_log:bool,
38 }
39
40 #[allow(nonstandard_style)]
41 #[derive(Clone, Debug, PartialEq, Eq)]
42 pub struct QueryMsg {
43     pub query: &'static str,
44     pub msg: Option<String>,
45 }
46
47 /// A sequence of these messages induce a trace of query-based incremental compilation.
48 // FIXME(matthewhammer): Determine whether we should include cycle detection here or not.
49 #[derive(Clone,Debug)]
50 pub enum ProfileQueriesMsg {
51     /// Begin a timed pass.
52     TimeBegin(String),
53     /// End a timed pass.
54     TimeEnd,
55     /// Begin a task (see `dep_graph::graph::with_task`).
56     TaskBegin(DepNode),
57     /// End a task.
58     TaskEnd,
59     /// Begin a new query.
60     /// Cannot use `Span` because queries are sent to other thread.
61     QueryBegin(SpanData, QueryMsg),
62     /// Query is satisfied by using an already-known value for the given key.
63     CacheHit,
64     /// Query requires running a provider; providers may nest, permitting queries to nest.
65     ProviderBegin,
66     /// Query is satisfied by a provider terminating with a value.
67     ProviderEnd,
68     /// Dump a record of the queries to the given path.
69     Dump(ProfQDumpParams),
70     /// Halt the profiling/monitoring background thread.
71     Halt
72 }
73
74 /// If enabled, send a message to the profile-queries thread.
75 pub fn profq_msg(sess: &Session, msg: ProfileQueriesMsg) {
76     if let Some(s) = sess.profile_channel.borrow().as_ref() {
77         s.send(msg).unwrap()
78     } else {
79         // Do nothing.
80     }
81 }
82
83 /// Set channel for profile queries channel.
84 pub fn profq_set_chan(sess: &Session, s: Sender<ProfileQueriesMsg>) -> bool {
85     let mut channel = sess.profile_channel.borrow_mut();
86     if channel.is_none() {
87         *channel = Some(s);
88         true
89     } else {
90         false
91     }
92 }
93
94 /// Read the current depth of `time()` calls. This is used to
95 /// encourage indentation across threads.
96 pub fn time_depth() -> usize {
97     TIME_DEPTH.with(|slot| slot.get())
98 }
99
100 /// Sets the current depth of `time()` calls. The idea is to call
101 /// `set_time_depth()` with the result from `time_depth()` in the
102 /// parent thread.
103 pub fn set_time_depth(depth: usize) {
104     TIME_DEPTH.with(|slot| slot.set(depth));
105 }
106
107 pub fn time<T, F>(sess: &Session, what: &str, f: F) -> T where
108     F: FnOnce() -> T,
109 {
110     time_ext(sess.time_passes(), Some(sess), what, f)
111 }
112
113 pub fn time_ext<T, F>(do_it: bool, sess: Option<&Session>, what: &str, f: F) -> T where
114     F: FnOnce() -> T,
115 {
116     if !do_it { return f(); }
117
118     let old = TIME_DEPTH.with(|slot| {
119         let r = slot.get();
120         slot.set(r + 1);
121         r
122     });
123
124     if let Some(sess) = sess {
125         if cfg!(debug_assertions) {
126             profq_msg(sess, ProfileQueriesMsg::TimeBegin(what.to_string()))
127         }
128     }
129     let start = Instant::now();
130     let rv = f();
131     let dur = start.elapsed();
132     if let Some(sess) = sess {
133         if cfg!(debug_assertions) {
134             profq_msg(sess, ProfileQueriesMsg::TimeEnd)
135         }
136     }
137
138     print_time_passes_entry(true, what, dur);
139
140     TIME_DEPTH.with(|slot| slot.set(old));
141
142     rv
143 }
144
145 pub fn print_time_passes_entry(do_it: bool, what: &str, dur: Duration) {
146     if !do_it {
147         return
148     }
149
150     let indentation = TIME_DEPTH.with(|slot| slot.get());
151
152     let mem_string = match get_resident() {
153         Some(n) => {
154             let mb = n as f64 / 1_000_000.0;
155             format!("; rss: {}MB", mb.round() as usize)
156         }
157         None => String::new(),
158     };
159     println!("{}time: {}{}\t{}",
160              "  ".repeat(indentation),
161              duration_to_secs_str(dur),
162              mem_string,
163              what);
164 }
165
166 // Hack up our own formatting for the duration to make it easier for scripts
167 // to parse (always use the same number of decimal places and the same unit).
168 pub fn duration_to_secs_str(dur: Duration) -> String {
169     const NANOS_PER_SEC: f64 = 1_000_000_000.0;
170     let secs = dur.as_secs() as f64 +
171                dur.subsec_nanos() as f64 / NANOS_PER_SEC;
172
173     format!("{:.3}", secs)
174 }
175
176 pub fn to_readable_str(mut val: usize) -> String {
177     let mut groups = vec![];
178     loop {
179         let group = val % 1000;
180
181         val /= 1000;
182
183         if val == 0 {
184             groups.push(group.to_string());
185             break;
186         } else {
187             groups.push(format!("{:03}", group));
188         }
189     }
190
191     groups.reverse();
192
193     groups.join("_")
194 }
195
196 pub fn record_time<T, F>(accu: &Lock<Duration>, f: F) -> T where
197     F: FnOnce() -> T,
198 {
199     let start = Instant::now();
200     let rv = f();
201     let duration = start.elapsed();
202     let mut accu = accu.lock();
203     *accu = *accu + duration;
204     rv
205 }
206
207 // Memory reporting
208 #[cfg(unix)]
209 fn get_resident() -> Option<usize> {
210     use std::fs;
211
212     let field = 1;
213     let contents = fs::read("/proc/self/statm").ok()?;
214     let contents = String::from_utf8(contents).ok()?;
215     let s = contents.split_whitespace().nth(field)?;
216     let npages = s.parse::<usize>().ok()?;
217     Some(npages * 4096)
218 }
219
220 #[cfg(windows)]
221 fn get_resident() -> Option<usize> {
222     type BOOL = i32;
223     type DWORD = u32;
224     type HANDLE = *mut u8;
225     use libc::size_t;
226     use std::mem;
227     #[repr(C)]
228     #[allow(non_snake_case)]
229     struct PROCESS_MEMORY_COUNTERS {
230         cb: DWORD,
231         PageFaultCount: DWORD,
232         PeakWorkingSetSize: size_t,
233         WorkingSetSize: size_t,
234         QuotaPeakPagedPoolUsage: size_t,
235         QuotaPagedPoolUsage: size_t,
236         QuotaPeakNonPagedPoolUsage: size_t,
237         QuotaNonPagedPoolUsage: size_t,
238         PagefileUsage: size_t,
239         PeakPagefileUsage: size_t,
240     }
241     type PPROCESS_MEMORY_COUNTERS = *mut PROCESS_MEMORY_COUNTERS;
242     #[link(name = "psapi")]
243     extern "system" {
244         fn GetCurrentProcess() -> HANDLE;
245         fn GetProcessMemoryInfo(Process: HANDLE,
246                                 ppsmemCounters: PPROCESS_MEMORY_COUNTERS,
247                                 cb: DWORD) -> BOOL;
248     }
249     let mut pmc: PROCESS_MEMORY_COUNTERS = unsafe { mem::zeroed() };
250     pmc.cb = mem::size_of_val(&pmc) as DWORD;
251     match unsafe { GetProcessMemoryInfo(GetCurrentProcess(), &mut pmc, pmc.cb) } {
252         0 => None,
253         _ => Some(pmc.WorkingSetSize as usize),
254     }
255 }
256
257 pub fn indent<R, F>(op: F) -> R where
258     R: Debug,
259     F: FnOnce() -> R,
260 {
261     // Use in conjunction with the log post-processor like `src/etc/indenter`
262     // to make debug output more readable.
263     debug!(">>");
264     let r = op();
265     debug!("<< (Result = {:?})", r);
266     r
267 }
268
269 pub struct Indenter {
270     _cannot_construct_outside_of_this_module: (),
271 }
272
273 impl Drop for Indenter {
274     fn drop(&mut self) { debug!("<<"); }
275 }
276
277 pub fn indenter() -> Indenter {
278     debug!(">>");
279     Indenter { _cannot_construct_outside_of_this_module: () }
280 }