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.
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.
11 #![allow(non_camel_case_types)]
13 use std::cell::{RefCell, Cell};
14 use std::collections::HashMap;
15 use std::ffi::CString;
17 use std::hash::{Hash, BuildHasher};
18 use std::iter::repeat;
20 use std::time::{Duration, Instant};
22 // The name of the associated type for `Fn` return types
23 pub const FN_OUTPUT_NAME: &'static str = "Output";
25 // Useful type to use with `Result<>` indicate that an error has already
26 // been reported to the user, so no need to continue checking.
27 #[derive(Clone, Copy, Debug)]
28 pub struct ErrorReported;
30 thread_local!(static TIME_DEPTH: Cell<usize> = Cell::new(0));
32 /// Read the current depth of `time()` calls. This is used to
33 /// encourage indentation across threads.
34 pub fn time_depth() -> usize {
35 TIME_DEPTH.with(|slot| slot.get())
38 /// Set the current depth of `time()` calls. The idea is to call
39 /// `set_time_depth()` with the result from `time_depth()` in the
41 pub fn set_time_depth(depth: usize) {
42 TIME_DEPTH.with(|slot| slot.set(depth));
45 pub fn time<T, F>(do_it: bool, what: &str, f: F) -> T where
48 if !do_it { return f(); }
50 let old = TIME_DEPTH.with(|slot| {
56 let start = Instant::now();
58 let dur = start.elapsed();
60 let mem_string = match get_resident() {
62 let mb = n as f64 / 1_000_000.0;
63 format!("; rss: {}MB", mb.round() as usize)
65 None => "".to_owned(),
67 println!("{}time: {}{}\t{}",
68 repeat(" ").take(old).collect::<String>(),
69 duration_to_secs_str(dur),
73 TIME_DEPTH.with(|slot| slot.set(old));
78 // Hack up our own formatting for the duration to make it easier for scripts
79 // to parse (always use the same number of decimal places and the same unit).
80 pub fn duration_to_secs_str(dur: Duration) -> String {
81 const NANOS_PER_SEC: f64 = 1_000_000_000.0;
82 let secs = dur.as_secs() as f64 +
83 dur.subsec_nanos() as f64 / NANOS_PER_SEC;
85 format!("{:.3}", secs)
88 pub fn to_readable_str(mut val: usize) -> String {
89 let mut groups = vec![];
91 let group = val % 1000;
96 groups.push(format!("{}", group));
99 groups.push(format!("{:03}", group));
108 pub fn record_time<T, F>(accu: &Cell<Duration>, f: F) -> T where
111 let start = Instant::now();
113 let duration = start.elapsed();
114 accu.set(duration + accu.get());
118 // Like std::macros::try!, but for Option<>.
120 macro_rules! option_try(
121 ($e:expr) => (match $e { Some(e) => e, None => return None })
126 fn get_resident() -> Option<usize> {
131 let mut f = option_try!(File::open("/proc/self/statm").ok());
132 let mut contents = String::new();
133 option_try!(f.read_to_string(&mut contents).ok());
134 let s = option_try!(contents.split_whitespace().nth(field));
135 let npages = option_try!(s.parse::<usize>().ok());
140 fn get_resident() -> Option<usize> {
143 type HANDLE = *mut u8;
147 #[allow(non_snake_case)]
148 struct PROCESS_MEMORY_COUNTERS {
150 PageFaultCount: DWORD,
151 PeakWorkingSetSize: size_t,
152 WorkingSetSize: size_t,
153 QuotaPeakPagedPoolUsage: size_t,
154 QuotaPagedPoolUsage: size_t,
155 QuotaPeakNonPagedPoolUsage: size_t,
156 QuotaNonPagedPoolUsage: size_t,
157 PagefileUsage: size_t,
158 PeakPagefileUsage: size_t,
160 type PPROCESS_MEMORY_COUNTERS = *mut PROCESS_MEMORY_COUNTERS;
161 #[link(name = "psapi")]
163 fn GetCurrentProcess() -> HANDLE;
164 fn GetProcessMemoryInfo(Process: HANDLE,
165 ppsmemCounters: PPROCESS_MEMORY_COUNTERS,
168 let mut pmc: PROCESS_MEMORY_COUNTERS = unsafe { mem::zeroed() };
169 pmc.cb = mem::size_of_val(&pmc) as DWORD;
170 match unsafe { GetProcessMemoryInfo(GetCurrentProcess(), &mut pmc, pmc.cb) } {
172 _ => Some(pmc.WorkingSetSize as usize),
176 pub fn indent<R, F>(op: F) -> R where
180 // Use in conjunction with the log post-processor like `src/etc/indenter`
181 // to make debug output more readable.
184 debug!("<< (Result = {:?})", r);
188 pub struct Indenter {
189 _cannot_construct_outside_of_this_module: (),
192 impl Drop for Indenter {
193 fn drop(&mut self) { debug!("<<"); }
196 pub fn indenter() -> Indenter {
198 Indenter { _cannot_construct_outside_of_this_module: () }
201 pub trait MemoizationMap {
205 /// If `key` is present in the map, return the valuee,
206 /// otherwise invoke `op` and store the value in the map.
208 /// NB: if the receiver is a `DepTrackingMap`, special care is
209 /// needed in the `op` to ensure that the correct edges are
210 /// added into the dep graph. See the `DepTrackingMap` impl for
212 fn memoize<OP>(&self, key: Self::Key, op: OP) -> Self::Value
213 where OP: FnOnce() -> Self::Value;
216 impl<K, V, S> MemoizationMap for RefCell<HashMap<K,V,S>>
217 where K: Hash+Eq+Clone, V: Clone, S: BuildHasher
222 fn memoize<OP>(&self, key: K, op: OP) -> V
223 where OP: FnOnce() -> V
225 let result = self.borrow().get(&key).cloned();
227 Some(result) => result,
230 self.borrow_mut().insert(key, result.clone());
238 pub fn path2cstr(p: &Path) -> CString {
239 use std::os::unix::prelude::*;
241 let p: &OsStr = p.as_ref();
242 CString::new(p.as_bytes()).unwrap()
245 pub fn path2cstr(p: &Path) -> CString {
246 CString::new(p.to_str().unwrap()).unwrap()
251 fn test_to_readable_str() {
252 assert_eq!("0", to_readable_str(0));
253 assert_eq!("1", to_readable_str(1));
254 assert_eq!("99", to_readable_str(99));
255 assert_eq!("999", to_readable_str(999));
256 assert_eq!("1_000", to_readable_str(1_000));
257 assert_eq!("1_001", to_readable_str(1_001));
258 assert_eq!("999_999", to_readable_str(999_999));
259 assert_eq!("1_000_000", to_readable_str(1_000_000));
260 assert_eq!("1_234_567", to_readable_str(1_234_567));