]> git.lizzy.rs Git - rust.git/blob - src/librustc/util/common.rs
Rollup merge of #42496 - Razaekel:feature/integer_max-min, r=BurntSushi
[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 std::cell::{RefCell, Cell};
14 use std::collections::HashMap;
15 use std::ffi::CString;
16 use std::fmt::Debug;
17 use std::hash::{Hash, BuildHasher};
18 use std::iter::repeat;
19 use std::path::Path;
20 use std::time::{Duration, Instant};
21
22 use ty::TyCtxt;
23
24 // The name of the associated type for `Fn` return types
25 pub const FN_OUTPUT_NAME: &'static str = "Output";
26
27 // Useful type to use with `Result<>` indicate that an error has already
28 // been reported to the user, so no need to continue checking.
29 #[derive(Clone, Copy, Debug)]
30 pub struct ErrorReported;
31
32 thread_local!(static TIME_DEPTH: Cell<usize> = Cell::new(0));
33
34 /// Read the current depth of `time()` calls. This is used to
35 /// encourage indentation across threads.
36 pub fn time_depth() -> usize {
37     TIME_DEPTH.with(|slot| slot.get())
38 }
39
40 /// Set the current depth of `time()` calls. The idea is to call
41 /// `set_time_depth()` with the result from `time_depth()` in the
42 /// parent thread.
43 pub fn set_time_depth(depth: usize) {
44     TIME_DEPTH.with(|slot| slot.set(depth));
45 }
46
47 pub fn time<T, F>(do_it: bool, what: &str, f: F) -> T where
48     F: FnOnce() -> T,
49 {
50     if !do_it { return f(); }
51
52     let old = TIME_DEPTH.with(|slot| {
53         let r = slot.get();
54         slot.set(r + 1);
55         r
56     });
57
58     let start = Instant::now();
59     let rv = f();
60     let dur = start.elapsed();
61
62     let mem_string = match get_resident() {
63         Some(n) => {
64             let mb = n as f64 / 1_000_000.0;
65             format!("; rss: {}MB", mb.round() as usize)
66         }
67         None => "".to_owned(),
68     };
69     println!("{}time: {}{}\t{}",
70              repeat("  ").take(old).collect::<String>(),
71              duration_to_secs_str(dur),
72              mem_string,
73              what);
74
75     TIME_DEPTH.with(|slot| slot.set(old));
76
77     rv
78 }
79
80 // Hack up our own formatting for the duration to make it easier for scripts
81 // to parse (always use the same number of decimal places and the same unit).
82 pub fn duration_to_secs_str(dur: Duration) -> String {
83     const NANOS_PER_SEC: f64 = 1_000_000_000.0;
84     let secs = dur.as_secs() as f64 +
85                dur.subsec_nanos() as f64 / NANOS_PER_SEC;
86
87     format!("{:.3}", secs)
88 }
89
90 pub fn to_readable_str(mut val: usize) -> String {
91     let mut groups = vec![];
92     loop {
93         let group = val % 1000;
94
95         val /= 1000;
96
97         if val == 0 {
98             groups.push(format!("{}", group));
99             break;
100         } else {
101             groups.push(format!("{:03}", group));
102         }
103     }
104
105     groups.reverse();
106
107     groups.join("_")
108 }
109
110 pub fn record_time<T, F>(accu: &Cell<Duration>, f: F) -> T where
111     F: FnOnce() -> T,
112 {
113     let start = Instant::now();
114     let rv = f();
115     let duration = start.elapsed();
116     accu.set(duration + accu.get());
117     rv
118 }
119
120 // Like std::macros::try!, but for Option<>.
121 #[cfg(unix)]
122 macro_rules! option_try(
123     ($e:expr) => (match $e { Some(e) => e, None => return None })
124 );
125
126 // Memory reporting
127 #[cfg(unix)]
128 fn get_resident() -> Option<usize> {
129     use std::fs::File;
130     use std::io::Read;
131
132     let field = 1;
133     let mut f = option_try!(File::open("/proc/self/statm").ok());
134     let mut contents = String::new();
135     option_try!(f.read_to_string(&mut contents).ok());
136     let s = option_try!(contents.split_whitespace().nth(field));
137     let npages = option_try!(s.parse::<usize>().ok());
138     Some(npages * 4096)
139 }
140
141 #[cfg(windows)]
142 fn get_resident() -> Option<usize> {
143     type BOOL = i32;
144     type DWORD = u32;
145     type HANDLE = *mut u8;
146     use libc::size_t;
147     use std::mem;
148     #[repr(C)]
149     #[allow(non_snake_case)]
150     struct PROCESS_MEMORY_COUNTERS {
151         cb: DWORD,
152         PageFaultCount: DWORD,
153         PeakWorkingSetSize: size_t,
154         WorkingSetSize: size_t,
155         QuotaPeakPagedPoolUsage: size_t,
156         QuotaPagedPoolUsage: size_t,
157         QuotaPeakNonPagedPoolUsage: size_t,
158         QuotaNonPagedPoolUsage: size_t,
159         PagefileUsage: size_t,
160         PeakPagefileUsage: size_t,
161     }
162     type PPROCESS_MEMORY_COUNTERS = *mut PROCESS_MEMORY_COUNTERS;
163     #[link(name = "psapi")]
164     extern "system" {
165         fn GetCurrentProcess() -> HANDLE;
166         fn GetProcessMemoryInfo(Process: HANDLE,
167                                 ppsmemCounters: PPROCESS_MEMORY_COUNTERS,
168                                 cb: DWORD) -> BOOL;
169     }
170     let mut pmc: PROCESS_MEMORY_COUNTERS = unsafe { mem::zeroed() };
171     pmc.cb = mem::size_of_val(&pmc) as DWORD;
172     match unsafe { GetProcessMemoryInfo(GetCurrentProcess(), &mut pmc, pmc.cb) } {
173         0 => None,
174         _ => Some(pmc.WorkingSetSize as usize),
175     }
176 }
177
178 pub fn indent<R, F>(op: F) -> R where
179     R: Debug,
180     F: FnOnce() -> R,
181 {
182     // Use in conjunction with the log post-processor like `src/etc/indenter`
183     // to make debug output more readable.
184     debug!(">>");
185     let r = op();
186     debug!("<< (Result = {:?})", r);
187     r
188 }
189
190 pub struct Indenter {
191     _cannot_construct_outside_of_this_module: (),
192 }
193
194 impl Drop for Indenter {
195     fn drop(&mut self) { debug!("<<"); }
196 }
197
198 pub fn indenter() -> Indenter {
199     debug!(">>");
200     Indenter { _cannot_construct_outside_of_this_module: () }
201 }
202
203 pub trait MemoizationMap {
204     type Key: Clone;
205     type Value: Clone;
206
207     /// If `key` is present in the map, return the valuee,
208     /// otherwise invoke `op` and store the value in the map.
209     ///
210     /// NB: if the receiver is a `DepTrackingMap`, special care is
211     /// needed in the `op` to ensure that the correct edges are
212     /// added into the dep graph. See the `DepTrackingMap` impl for
213     /// more details!
214     fn memoize<OP>(&self, tcx: TyCtxt, key: Self::Key, op: OP) -> Self::Value
215         where OP: FnOnce() -> Self::Value;
216 }
217
218 impl<K, V, S> MemoizationMap for RefCell<HashMap<K,V,S>>
219     where K: Hash+Eq+Clone, V: Clone, S: BuildHasher
220 {
221     type Key = K;
222     type Value = V;
223
224     fn memoize<OP>(&self, _tcx: TyCtxt, key: K, op: OP) -> V
225         where OP: FnOnce() -> V
226     {
227         let result = self.borrow().get(&key).cloned();
228         match result {
229             Some(result) => result,
230             None => {
231                 let result = op();
232                 self.borrow_mut().insert(key, result.clone());
233                 result
234             }
235         }
236     }
237 }
238
239 #[cfg(unix)]
240 pub fn path2cstr(p: &Path) -> CString {
241     use std::os::unix::prelude::*;
242     use std::ffi::OsStr;
243     let p: &OsStr = p.as_ref();
244     CString::new(p.as_bytes()).unwrap()
245 }
246 #[cfg(windows)]
247 pub fn path2cstr(p: &Path) -> CString {
248     CString::new(p.to_str().unwrap()).unwrap()
249 }
250
251
252 #[test]
253 fn test_to_readable_str() {
254     assert_eq!("0", to_readable_str(0));
255     assert_eq!("1", to_readable_str(1));
256     assert_eq!("99", to_readable_str(99));
257     assert_eq!("999", to_readable_str(999));
258     assert_eq!("1_000", to_readable_str(1_000));
259     assert_eq!("1_001", to_readable_str(1_001));
260     assert_eq!("999_999", to_readable_str(999_999));
261     assert_eq!("1_000_000", to_readable_str(1_000_000));
262     assert_eq!("1_234_567", to_readable_str(1_234_567));
263 }