| #![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 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; |
| |
| pub use errors::ErrorReported; |
| |
| thread_local!(static TIME_DEPTH: Cell<usize> = Cell::new(0)); |
| |
| #[allow(nonstandard_style)] |
| #[derive(Clone, Debug, PartialEq, Eq)] |
| pub struct QueryMsg { |
| pub query: &'static str, |
| pub msg: Option<String>, |
| } |
| |
| /// 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<T, F>(sess: &Session, what: &str, f: F) -> T where |
| F: FnOnce() -> T, |
| { |
| time_ext(sess.time_passes(), what, f) |
| } |
| |
| pub fn time_ext<T, F>(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); |
| } |
| |
| pub use rustc_session::utils::duration_to_secs_str; |
| |
| 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<T, F>(accu: &Lock<Duration>, 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<usize> { |
| 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::<usize>().ok()?; |
| Some(npages * 4096) |
| } |
| |
| #[cfg(windows)] |
| fn get_resident() -> Option<usize> { |
| 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<R, F>(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: () } |
| } |