]> git.lizzy.rs Git - rust.git/blob - src/librustc/util/common.rs
3e52a6aa50850c12901e0f9b8c5cac6e418a5374
[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 syntax::symbol::{Symbol, sym};
10 use rustc_macros::HashStable;
11 use crate::session::Session;
12
13 #[cfg(test)]
14 mod tests;
15
16 // The name of the associated type for `Fn` return types.
17 pub const FN_OUTPUT_NAME: Symbol = sym::Output;
18
19 // Useful type to use with `Result<>` indicate that an error has already
20 // been reported to the user, so no need to continue checking.
21 #[derive(Clone, Copy, Debug, RustcEncodable, RustcDecodable, HashStable)]
22 pub struct ErrorReported;
23
24 thread_local!(static TIME_DEPTH: Cell<usize> = Cell::new(0));
25
26 #[allow(nonstandard_style)]
27 #[derive(Clone, Debug, PartialEq, Eq)]
28 pub struct QueryMsg {
29     pub query: &'static str,
30     pub msg: Option<String>,
31 }
32
33 /// Read the current depth of `time()` calls. This is used to
34 /// encourage indentation across threads.
35 pub fn time_depth() -> usize {
36     TIME_DEPTH.with(|slot| slot.get())
37 }
38
39 /// Sets the current depth of `time()` calls. The idea is to call
40 /// `set_time_depth()` with the result from `time_depth()` in the
41 /// parent thread.
42 pub fn set_time_depth(depth: usize) {
43     TIME_DEPTH.with(|slot| slot.set(depth));
44 }
45
46 pub fn time<T, F>(sess: &Session, what: &str, f: F) -> T where
47     F: FnOnce() -> T,
48 {
49     time_ext(sess.time_passes(), what, f)
50 }
51
52 pub fn time_ext<T, F>(do_it: bool, what: &str, f: F) -> T where
53     F: FnOnce() -> T,
54 {
55     if !do_it { return f(); }
56
57     let old = TIME_DEPTH.with(|slot| {
58         let r = slot.get();
59         slot.set(r + 1);
60         r
61     });
62
63     let start = Instant::now();
64     let rv = f();
65     let dur = start.elapsed();
66
67     print_time_passes_entry(true, what, dur);
68
69     TIME_DEPTH.with(|slot| slot.set(old));
70
71     rv
72 }
73
74 pub fn print_time_passes_entry(do_it: bool, what: &str, dur: Duration) {
75     if !do_it {
76         return
77     }
78
79     let indentation = TIME_DEPTH.with(|slot| slot.get());
80
81     let mem_string = match get_resident() {
82         Some(n) => {
83             let mb = n as f64 / 1_000_000.0;
84             format!("; rss: {}MB", mb.round() as usize)
85         }
86         None => String::new(),
87     };
88     println!("{}time: {}{}\t{}",
89              "  ".repeat(indentation),
90              duration_to_secs_str(dur),
91              mem_string,
92              what);
93 }
94
95 // Hack up our own formatting for the duration to make it easier for scripts
96 // to parse (always use the same number of decimal places and the same unit).
97 pub fn duration_to_secs_str(dur: Duration) -> String {
98     const NANOS_PER_SEC: f64 = 1_000_000_000.0;
99     let secs = dur.as_secs() as f64 +
100                dur.subsec_nanos() as f64 / NANOS_PER_SEC;
101
102     format!("{:.3}", secs)
103 }
104
105 pub fn to_readable_str(mut val: usize) -> String {
106     let mut groups = vec![];
107     loop {
108         let group = val % 1000;
109
110         val /= 1000;
111
112         if val == 0 {
113             groups.push(group.to_string());
114             break;
115         } else {
116             groups.push(format!("{:03}", group));
117         }
118     }
119
120     groups.reverse();
121
122     groups.join("_")
123 }
124
125 pub fn record_time<T, F>(accu: &Lock<Duration>, f: F) -> T where
126     F: FnOnce() -> T,
127 {
128     let start = Instant::now();
129     let rv = f();
130     let duration = start.elapsed();
131     let mut accu = accu.lock();
132     *accu = *accu + duration;
133     rv
134 }
135
136 // Memory reporting
137 #[cfg(unix)]
138 fn get_resident() -> Option<usize> {
139     use std::fs;
140
141     let field = 1;
142     let contents = fs::read("/proc/self/statm").ok()?;
143     let contents = String::from_utf8(contents).ok()?;
144     let s = contents.split_whitespace().nth(field)?;
145     let npages = s.parse::<usize>().ok()?;
146     Some(npages * 4096)
147 }
148
149 #[cfg(windows)]
150 fn get_resident() -> Option<usize> {
151     type BOOL = i32;
152     type DWORD = u32;
153     type HANDLE = *mut u8;
154     use libc::size_t;
155     use std::mem;
156     #[repr(C)]
157     #[allow(non_snake_case)]
158     struct PROCESS_MEMORY_COUNTERS {
159         cb: DWORD,
160         PageFaultCount: DWORD,
161         PeakWorkingSetSize: size_t,
162         WorkingSetSize: size_t,
163         QuotaPeakPagedPoolUsage: size_t,
164         QuotaPagedPoolUsage: size_t,
165         QuotaPeakNonPagedPoolUsage: size_t,
166         QuotaNonPagedPoolUsage: size_t,
167         PagefileUsage: size_t,
168         PeakPagefileUsage: size_t,
169     }
170     type PPROCESS_MEMORY_COUNTERS = *mut PROCESS_MEMORY_COUNTERS;
171     #[link(name = "psapi")]
172     extern "system" {
173         fn GetCurrentProcess() -> HANDLE;
174         fn GetProcessMemoryInfo(Process: HANDLE,
175                                 ppsmemCounters: PPROCESS_MEMORY_COUNTERS,
176                                 cb: DWORD) -> BOOL;
177     }
178     let mut pmc: PROCESS_MEMORY_COUNTERS = unsafe { mem::zeroed() };
179     pmc.cb = mem::size_of_val(&pmc) as DWORD;
180     match unsafe { GetProcessMemoryInfo(GetCurrentProcess(), &mut pmc, pmc.cb) } {
181         0 => None,
182         _ => Some(pmc.WorkingSetSize as usize),
183     }
184 }
185
186 pub fn indent<R, F>(op: F) -> R where
187     R: Debug,
188     F: FnOnce() -> R,
189 {
190     // Use in conjunction with the log post-processor like `src/etc/indenter`
191     // to make debug output more readable.
192     debug!(">>");
193     let r = op();
194     debug!("<< (Result = {:?})", r);
195     r
196 }
197
198 pub struct Indenter {
199     _cannot_construct_outside_of_this_module: (),
200 }
201
202 impl Drop for Indenter {
203     fn drop(&mut self) { debug!("<<"); }
204 }
205
206 pub fn indenter() -> Indenter {
207     debug!(">>");
208     Indenter { _cannot_construct_outside_of_this_module: () }
209 }