#![allow(non_camel_case_types)] use rustc_data_structures::sync::Lock; use std::cell::Cell; use std::fmt::Debug; use std::time::{Duration, Instant}; use syntax::symbol::{Symbol, sym}; use rustc_macros::HashStable; use crate::session::Session; #[cfg(test)] mod tests; // The name of the associated type for `Fn` return types. pub const FN_OUTPUT_NAME: Symbol = sym::Output; // Useful type to use with `Result<>` indicate that an error has already // been reported to the user, so no need to continue checking. #[derive(Clone, Copy, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct ErrorReported; thread_local!(static TIME_DEPTH: Cell = Cell::new(0)); #[allow(nonstandard_style)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct QueryMsg { pub query: &'static str, pub msg: Option, } /// Read the current depth of `time()` calls. This is used to /// encourage indentation across threads. pub fn time_depth() -> usize { TIME_DEPTH.with(|slot| slot.get()) } /// Sets the current depth of `time()` calls. The idea is to call /// `set_time_depth()` with the result from `time_depth()` in the /// parent thread. pub fn set_time_depth(depth: usize) { TIME_DEPTH.with(|slot| slot.set(depth)); } pub fn time(sess: &Session, what: &str, f: F) -> T where F: FnOnce() -> T, { time_ext(sess.time_passes(), what, f) } pub fn time_ext(do_it: bool, what: &str, f: F) -> T where F: FnOnce() -> T, { if !do_it { return f(); } let old = TIME_DEPTH.with(|slot| { let r = slot.get(); slot.set(r + 1); r }); let start = Instant::now(); let rv = f(); let dur = start.elapsed(); print_time_passes_entry(true, what, dur); TIME_DEPTH.with(|slot| slot.set(old)); rv } pub fn print_time_passes_entry(do_it: bool, what: &str, dur: Duration) { if !do_it { return } let indentation = TIME_DEPTH.with(|slot| slot.get()); let mem_string = match get_resident() { Some(n) => { let mb = n as f64 / 1_000_000.0; format!("; rss: {}MB", mb.round() as usize) } None => String::new(), }; println!("{}time: {}{}\t{}", " ".repeat(indentation), duration_to_secs_str(dur), mem_string, what); } // Hack up our own formatting for the duration to make it easier for scripts // to parse (always use the same number of decimal places and the same unit). pub fn duration_to_secs_str(dur: Duration) -> String { const NANOS_PER_SEC: f64 = 1_000_000_000.0; let secs = dur.as_secs() as f64 + dur.subsec_nanos() as f64 / NANOS_PER_SEC; format!("{:.3}", secs) } pub fn to_readable_str(mut val: usize) -> String { let mut groups = vec![]; loop { let group = val % 1000; val /= 1000; if val == 0 { groups.push(group.to_string()); break; } else { groups.push(format!("{:03}", group)); } } groups.reverse(); groups.join("_") } pub fn record_time(accu: &Lock, f: F) -> T where F: FnOnce() -> T, { let start = Instant::now(); let rv = f(); let duration = start.elapsed(); let mut accu = accu.lock(); *accu = *accu + duration; rv } // Memory reporting #[cfg(unix)] fn get_resident() -> Option { use std::fs; let field = 1; let contents = fs::read("/proc/self/statm").ok()?; let contents = String::from_utf8(contents).ok()?; let s = contents.split_whitespace().nth(field)?; let npages = s.parse::().ok()?; Some(npages * 4096) } #[cfg(windows)] fn get_resident() -> Option { type BOOL = i32; type DWORD = u32; type HANDLE = *mut u8; use libc::size_t; use std::mem; #[repr(C)] #[allow(non_snake_case)] struct PROCESS_MEMORY_COUNTERS { cb: DWORD, PageFaultCount: DWORD, PeakWorkingSetSize: size_t, WorkingSetSize: size_t, QuotaPeakPagedPoolUsage: size_t, QuotaPagedPoolUsage: size_t, QuotaPeakNonPagedPoolUsage: size_t, QuotaNonPagedPoolUsage: size_t, PagefileUsage: size_t, PeakPagefileUsage: size_t, } type PPROCESS_MEMORY_COUNTERS = *mut PROCESS_MEMORY_COUNTERS; #[link(name = "psapi")] extern "system" { fn GetCurrentProcess() -> HANDLE; fn GetProcessMemoryInfo(Process: HANDLE, ppsmemCounters: PPROCESS_MEMORY_COUNTERS, cb: DWORD) -> BOOL; } let mut pmc: PROCESS_MEMORY_COUNTERS = unsafe { mem::zeroed() }; pmc.cb = mem::size_of_val(&pmc) as DWORD; match unsafe { GetProcessMemoryInfo(GetCurrentProcess(), &mut pmc, pmc.cb) } { 0 => None, _ => Some(pmc.WorkingSetSize as usize), } } pub fn indent(op: F) -> R where R: Debug, F: FnOnce() -> R, { // Use in conjunction with the log post-processor like `src/etc/indenter` // to make debug output more readable. debug!(">>"); let r = op(); debug!("<< (Result = {:?})", r); r } pub struct Indenter { _cannot_construct_outside_of_this_module: (), } impl Drop for Indenter { fn drop(&mut self) { debug!("<<"); } } pub fn indenter() -> Indenter { debug!(">>"); Indenter { _cannot_construct_outside_of_this_module: () } }