1 #![allow(non_camel_case_types)]
3 use rustc_data_structures::{fx::FxHashMap, sync::Lock};
5 use std::cell::{RefCell, Cell};
10 use std::time::{Duration, Instant};
12 use std::sync::mpsc::{Sender};
13 use syntax_pos::{SpanData};
14 use syntax::symbol::{Symbol, sym};
15 use rustc_macros::HashStable;
16 use crate::ty::TyCtxt;
17 use crate::dep_graph::{DepNode};
19 use crate::session::Session;
24 // The name of the associated type for `Fn` return types.
25 pub const FN_OUTPUT_NAME: Symbol = sym::Output;
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, RustcEncodable, RustcDecodable, HashStable)]
30 pub struct ErrorReported;
32 thread_local!(static TIME_DEPTH: Cell<usize> = Cell::new(0));
35 static ref DEFAULT_HOOK: Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static> = {
36 let hook = panic::take_hook();
37 panic::set_hook(Box::new(panic_hook));
42 fn panic_hook(info: &panic::PanicInfo<'_>) {
43 (*DEFAULT_HOOK)(info);
45 let backtrace = env::var_os("RUST_BACKTRACE").map(|x| &x != "0").unwrap_or(false);
48 TyCtxt::try_print_query_stack();
53 if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
57 // Trigger a debugger if we crashed during bootstrap.
63 pub fn install_panic_hook() {
64 lazy_static::initialize(&DEFAULT_HOOK);
67 /// Parameters to the `Dump` variant of type `ProfileQueriesMsg`.
68 #[derive(Clone,Debug)]
69 pub struct ProfQDumpParams {
70 /// A base path for the files we will dump.
72 /// To ensure that the compiler waits for us to finish our dumps.
74 /// Toggle dumping a log file with every `ProfileQueriesMsg`.
75 pub dump_profq_msg_log:bool,
78 #[allow(nonstandard_style)]
79 #[derive(Clone, Debug, PartialEq, Eq)]
81 pub query: &'static str,
82 pub msg: Option<String>,
85 /// A sequence of these messages induce a trace of query-based incremental compilation.
86 // FIXME(matthewhammer): Determine whether we should include cycle detection here or not.
87 #[derive(Clone,Debug)]
88 pub enum ProfileQueriesMsg {
89 /// Begin a timed pass.
93 /// Begin a task (see `dep_graph::graph::with_task`).
97 /// Begin a new query.
98 /// Cannot use `Span` because queries are sent to other thread.
99 QueryBegin(SpanData, QueryMsg),
100 /// Query is satisfied by using an already-known value for the given key.
102 /// Query requires running a provider; providers may nest, permitting queries to nest.
104 /// Query is satisfied by a provider terminating with a value.
106 /// Dump a record of the queries to the given path.
107 Dump(ProfQDumpParams),
108 /// Halt the profiling/monitoring background thread.
112 /// If enabled, send a message to the profile-queries thread.
113 pub fn profq_msg(sess: &Session, msg: ProfileQueriesMsg) {
114 if let Some(s) = sess.profile_channel.borrow().as_ref() {
121 /// Set channel for profile queries channel.
122 pub fn profq_set_chan(sess: &Session, s: Sender<ProfileQueriesMsg>) -> bool {
123 let mut channel = sess.profile_channel.borrow_mut();
124 if channel.is_none() {
132 /// Read the current depth of `time()` calls. This is used to
133 /// encourage indentation across threads.
134 pub fn time_depth() -> usize {
135 TIME_DEPTH.with(|slot| slot.get())
138 /// Sets the current depth of `time()` calls. The idea is to call
139 /// `set_time_depth()` with the result from `time_depth()` in the
141 pub fn set_time_depth(depth: usize) {
142 TIME_DEPTH.with(|slot| slot.set(depth));
145 pub fn time<T, F>(sess: &Session, what: &str, f: F) -> T where
148 time_ext(sess.time_passes(), Some(sess), what, f)
151 pub fn time_ext<T, F>(do_it: bool, sess: Option<&Session>, what: &str, f: F) -> T where
154 if !do_it { return f(); }
156 let old = TIME_DEPTH.with(|slot| {
162 if let Some(sess) = sess {
163 if cfg!(debug_assertions) {
164 profq_msg(sess, ProfileQueriesMsg::TimeBegin(what.to_string()))
167 let start = Instant::now();
169 let dur = start.elapsed();
170 if let Some(sess) = sess {
171 if cfg!(debug_assertions) {
172 profq_msg(sess, ProfileQueriesMsg::TimeEnd)
176 print_time_passes_entry(true, what, dur);
178 TIME_DEPTH.with(|slot| slot.set(old));
183 pub fn print_time_passes_entry(do_it: bool, what: &str, dur: Duration) {
188 let indentation = TIME_DEPTH.with(|slot| slot.get());
190 let mem_string = match get_resident() {
192 let mb = n as f64 / 1_000_000.0;
193 format!("; rss: {}MB", mb.round() as usize)
195 None => String::new(),
197 println!("{}time: {}{}\t{}",
198 " ".repeat(indentation),
199 duration_to_secs_str(dur),
204 // Hack up our own formatting for the duration to make it easier for scripts
205 // to parse (always use the same number of decimal places and the same unit).
206 pub fn duration_to_secs_str(dur: Duration) -> String {
207 const NANOS_PER_SEC: f64 = 1_000_000_000.0;
208 let secs = dur.as_secs() as f64 +
209 dur.subsec_nanos() as f64 / NANOS_PER_SEC;
211 format!("{:.3}", secs)
214 pub fn to_readable_str(mut val: usize) -> String {
215 let mut groups = vec![];
217 let group = val % 1000;
222 groups.push(group.to_string());
225 groups.push(format!("{:03}", group));
234 pub fn record_time<T, F>(accu: &Lock<Duration>, f: F) -> T where
237 let start = Instant::now();
239 let duration = start.elapsed();
240 let mut accu = accu.lock();
241 *accu = *accu + duration;
247 fn get_resident() -> Option<usize> {
251 let contents = fs::read("/proc/self/statm").ok()?;
252 let contents = String::from_utf8(contents).ok()?;
253 let s = contents.split_whitespace().nth(field)?;
254 let npages = s.parse::<usize>().ok()?;
259 fn get_resident() -> Option<usize> {
262 type HANDLE = *mut u8;
266 #[allow(non_snake_case)]
267 struct PROCESS_MEMORY_COUNTERS {
269 PageFaultCount: DWORD,
270 PeakWorkingSetSize: size_t,
271 WorkingSetSize: size_t,
272 QuotaPeakPagedPoolUsage: size_t,
273 QuotaPagedPoolUsage: size_t,
274 QuotaPeakNonPagedPoolUsage: size_t,
275 QuotaNonPagedPoolUsage: size_t,
276 PagefileUsage: size_t,
277 PeakPagefileUsage: size_t,
279 type PPROCESS_MEMORY_COUNTERS = *mut PROCESS_MEMORY_COUNTERS;
280 #[link(name = "psapi")]
282 fn GetCurrentProcess() -> HANDLE;
283 fn GetProcessMemoryInfo(Process: HANDLE,
284 ppsmemCounters: PPROCESS_MEMORY_COUNTERS,
287 let mut pmc: PROCESS_MEMORY_COUNTERS = unsafe { mem::zeroed() };
288 pmc.cb = mem::size_of_val(&pmc) as DWORD;
289 match unsafe { GetProcessMemoryInfo(GetCurrentProcess(), &mut pmc, pmc.cb) } {
291 _ => Some(pmc.WorkingSetSize as usize),
295 pub fn indent<R, F>(op: F) -> R where
299 // Use in conjunction with the log post-processor like `src/etc/indenter`
300 // to make debug output more readable.
303 debug!("<< (Result = {:?})", r);
307 pub struct Indenter {
308 _cannot_construct_outside_of_this_module: (),
311 impl Drop for Indenter {
312 fn drop(&mut self) { debug!("<<"); }
315 pub fn indenter() -> Indenter {
317 Indenter { _cannot_construct_outside_of_this_module: () }
320 pub trait MemoizationMap {
324 /// If `key` is present in the map, return the value,
325 /// otherwise invoke `op` and store the value in the map.
327 /// N.B., if the receiver is a `DepTrackingMap`, special care is
328 /// needed in the `op` to ensure that the correct edges are
329 /// added into the dep graph. See the `DepTrackingMap` impl for
331 fn memoize<OP>(&self, key: Self::Key, op: OP) -> Self::Value
332 where OP: FnOnce() -> Self::Value;
335 impl<K, V> MemoizationMap for RefCell<FxHashMap<K,V>>
336 where K: Hash+Eq+Clone, V: Clone
341 fn memoize<OP>(&self, key: K, op: OP) -> V
342 where OP: FnOnce() -> V
344 let result = self.borrow().get(&key).cloned();
346 Some(result) => result,
349 self.borrow_mut().insert(key, result.clone());