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