From f696b21c5f1017ebb451d1e9d2d8fe261b26507f Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Mon, 11 Nov 2019 17:15:36 -0500 Subject: Move self-profile infrastructure to data structures The single dependency on queries (QueryName) can be fairly easily abstracted via a trait and this further decouples Session from librustc (the primary goal). --- Cargo.lock | 3 +- src/librustc/Cargo.toml | 1 - src/librustc/lib.rs | 1 - src/librustc/session/mod.rs | 2 +- src/librustc/ty/context.rs | 2 +- src/librustc/ty/query/config.rs | 2 +- src/librustc/ty/query/mod.rs | 2 +- src/librustc/ty/query/plumbing.rs | 14 +- src/librustc/util/profiling.rs | 313 ----------------------------- src/librustc_codegen_ssa/back/write.rs | 2 +- src/librustc_data_structures/Cargo.toml | 2 + src/librustc_data_structures/lib.rs | 1 + src/librustc_data_structures/profiling.rs | 315 ++++++++++++++++++++++++++++++ 13 files changed, 337 insertions(+), 323 deletions(-) delete mode 100644 src/librustc/util/profiling.rs create mode 100644 src/librustc_data_structures/profiling.rs diff --git a/Cargo.lock b/Cargo.lock index 16f2ffc2815..7c074fb18a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3120,7 +3120,6 @@ dependencies = [ "graphviz", "jobserver", "log", - "measureme", "num_cpus", "parking_lot 0.9.0", "polonius-engine", @@ -3470,6 +3469,7 @@ dependencies = [ name = "rustc_data_structures" version = "0.0.0" dependencies = [ + "bitflags", "cfg-if", "crossbeam-utils 0.6.5", "ena", @@ -3478,6 +3478,7 @@ dependencies = [ "jobserver", "lazy_static 1.3.0", "log", + "measureme", "parking_lot 0.9.0", "rustc-hash", "rustc-rayon 0.3.0", diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index 92b94af75d7..bcbe765b850 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -40,4 +40,3 @@ byteorder = { version = "1.3" } chalk-engine = { version = "0.9.0", default-features=false } rustc_fs_util = { path = "../librustc_fs_util" } smallvec = { version = "1.0", features = ["union", "may_dangle"] } -measureme = "0.4" diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 996f5b12412..1fd5bcb4915 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -126,7 +126,6 @@ pub mod util { pub mod captures; pub mod common; pub mod nodemap; - pub mod profiling; pub mod bug; } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 9792223ea15..bba377392b4 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -29,11 +29,11 @@ use syntax::source_map; use syntax::sess::{ParseSess, ProcessCfgMod}; use syntax::symbol::Symbol; use syntax_pos::{MultiSpan, Span}; -use crate::util::profiling::{SelfProfiler, SelfProfilerRef}; use rustc_target::spec::{PanicStrategy, RelroLevel, Target, TargetTriple}; use rustc_data_structures::flock; use rustc_data_structures::jobserver; +use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; use ::jobserver::Client; use std; diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 04e0f6f4b56..2ba670f509f 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -46,11 +46,11 @@ use crate::ty::CanonicalPolyFnSig; use crate::util::common::ErrorReported; use crate::util::nodemap::{DefIdMap, DefIdSet, ItemLocalMap, ItemLocalSet, NodeMap}; use crate::util::nodemap::{FxHashMap, FxHashSet}; -use crate::util::profiling::SelfProfilerRef; use errors::DiagnosticBuilder; use arena::SyncDroplessArena; use smallvec::SmallVec; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::stable_hasher::{ HashStable, StableHasher, StableVec, hash_stable_hashmap, }; diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs index c1c6a655d96..7e126459dcc 100644 --- a/src/librustc/ty/query/config.rs +++ b/src/librustc/ty/query/config.rs @@ -6,7 +6,7 @@ use crate::ty::query::queries; use crate::ty::query::{Query, QueryName}; use crate::ty::query::QueryCache; use crate::ty::query::plumbing::CycleError; -use crate::util::profiling::ProfileCategory; +use rustc_data_structures::profiling::ProfileCategory; use std::borrow::Cow; use std::hash::Hash; diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index 0615004125b..a1eb1c43335 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -39,7 +39,7 @@ use crate::ty::util::NeedsDrop; use crate::ty::subst::SubstsRef; use crate::util::nodemap::{DefIdSet, DefIdMap}; use crate::util::common::ErrorReported; -use crate::util::profiling::ProfileCategory::*; +use rustc_data_structures::profiling::ProfileCategory::*; use rustc_data_structures::svh::Svh; use rustc_index::vec::IndexVec; diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index 538154b035a..0f77a637b4d 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -672,7 +672,7 @@ macro_rules! define_queries_inner { rustc_data_structures::stable_hasher::StableHasher, ich::StableHashingContext }; - use crate::util::profiling::ProfileCategory; + use rustc_data_structures::profiling::ProfileCategory; define_queries_struct! { tcx: $tcx, @@ -816,8 +816,18 @@ macro_rules! define_queries_inner { $($name),* } + impl rustc_data_structures::profiling::QueryName for QueryName { + fn discriminant(self) -> std::mem::Discriminant { + std::mem::discriminant(&self) + } + + fn as_str(self) -> &'static str { + QueryName::as_str(&self) + } + } + impl QueryName { - pub fn register_with_profiler(profiler: &crate::util::profiling::SelfProfiler) { + pub fn register_with_profiler(profiler: &rustc_data_structures::profiling::SelfProfiler) { $(profiler.register_query_name(QueryName::$name);)* } diff --git a/src/librustc/util/profiling.rs b/src/librustc/util/profiling.rs deleted file mode 100644 index 5a1b7f3aa4c..00000000000 --- a/src/librustc/util/profiling.rs +++ /dev/null @@ -1,313 +0,0 @@ -use std::error::Error; -use std::fs; -use std::mem::{self, Discriminant}; -use std::path::Path; -use std::process; -use std::sync::Arc; -use std::thread::ThreadId; -use std::u32; - -use crate::ty::query::QueryName; - -use measureme::{StringId, TimestampKind}; - -/// MmapSerializatioSink is faster on macOS and Linux -/// but FileSerializationSink is faster on Windows -#[cfg(not(windows))] -type SerializationSink = measureme::MmapSerializationSink; -#[cfg(windows)] -type SerializationSink = measureme::FileSerializationSink; - -type Profiler = measureme::Profiler; - - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)] -pub enum ProfileCategory { - Parsing, - Expansion, - TypeChecking, - BorrowChecking, - Codegen, - Linking, - Other, -} - -bitflags! { - struct EventFilter: u32 { - const GENERIC_ACTIVITIES = 1 << 0; - const QUERY_PROVIDERS = 1 << 1; - const QUERY_CACHE_HITS = 1 << 2; - const QUERY_BLOCKED = 1 << 3; - const INCR_CACHE_LOADS = 1 << 4; - - const DEFAULT = Self::GENERIC_ACTIVITIES.bits | - Self::QUERY_PROVIDERS.bits | - Self::QUERY_BLOCKED.bits | - Self::INCR_CACHE_LOADS.bits; - - // empty() and none() aren't const-fns unfortunately - const NONE = 0; - const ALL = !Self::NONE.bits; - } -} - -const EVENT_FILTERS_BY_NAME: &[(&str, EventFilter)] = &[ - ("none", EventFilter::NONE), - ("all", EventFilter::ALL), - ("generic-activity", EventFilter::GENERIC_ACTIVITIES), - ("query-provider", EventFilter::QUERY_PROVIDERS), - ("query-cache-hit", EventFilter::QUERY_CACHE_HITS), - ("query-blocked" , EventFilter::QUERY_BLOCKED), - ("incr-cache-load", EventFilter::INCR_CACHE_LOADS), -]; - -fn thread_id_to_u64(tid: ThreadId) -> u64 { - unsafe { mem::transmute::(tid) } -} - - -/// A reference to the SelfProfiler. It can be cloned and sent across thread -/// boundaries at will. -#[derive(Clone)] -pub struct SelfProfilerRef { - // This field is `None` if self-profiling is disabled for the current - // compilation session. - profiler: Option>, - - // We store the filter mask directly in the reference because that doesn't - // cost anything and allows for filtering with checking if the profiler is - // actually enabled. - event_filter_mask: EventFilter, -} - -impl SelfProfilerRef { - - pub fn new(profiler: Option>) -> SelfProfilerRef { - // If there is no SelfProfiler then the filter mask is set to NONE, - // ensuring that nothing ever tries to actually access it. - let event_filter_mask = profiler - .as_ref() - .map(|p| p.event_filter_mask) - .unwrap_or(EventFilter::NONE); - - SelfProfilerRef { - profiler, - event_filter_mask, - } - } - - // This shim makes sure that calls only get executed if the filter mask - // lets them pass. It also contains some trickery to make sure that - // code is optimized for non-profiling compilation sessions, i.e. anything - // past the filter check is never inlined so it doesn't clutter the fast - // path. - #[inline(always)] - fn exec(&self, event_filter: EventFilter, f: F) -> TimingGuard<'_> - where F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a> - { - #[inline(never)] - fn cold_call(profiler_ref: &SelfProfilerRef, f: F) -> TimingGuard<'_> - where F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a> - { - let profiler = profiler_ref.profiler.as_ref().unwrap(); - f(&**profiler) - } - - if unlikely!(self.event_filter_mask.contains(event_filter)) { - cold_call(self, f) - } else { - TimingGuard::none() - } - } - - /// Start profiling a generic activity. Profiling continues until the - /// TimingGuard returned from this call is dropped. - #[inline(always)] - pub fn generic_activity(&self, event_id: &str) -> TimingGuard<'_> { - self.exec(EventFilter::GENERIC_ACTIVITIES, |profiler| { - let event_id = profiler.profiler.alloc_string(event_id); - TimingGuard::start( - profiler, - profiler.generic_activity_event_kind, - event_id - ) - }) - } - - /// Start profiling a query provider. Profiling continues until the - /// TimingGuard returned from this call is dropped. - #[inline(always)] - pub fn query_provider(&self, query_name: QueryName) -> TimingGuard<'_> { - self.exec(EventFilter::QUERY_PROVIDERS, |profiler| { - let event_id = SelfProfiler::get_query_name_string_id(query_name); - TimingGuard::start(profiler, profiler.query_event_kind, event_id) - }) - } - - /// Record a query in-memory cache hit. - #[inline(always)] - pub fn query_cache_hit(&self, query_name: QueryName) { - self.non_guard_query_event( - |profiler| profiler.query_cache_hit_event_kind, - query_name, - EventFilter::QUERY_CACHE_HITS, - TimestampKind::Instant, - ); - } - - /// Start profiling a query being blocked on a concurrent execution. - /// Profiling continues until the TimingGuard returned from this call is - /// dropped. - #[inline(always)] - pub fn query_blocked(&self, query_name: QueryName) -> TimingGuard<'_> { - self.exec(EventFilter::QUERY_BLOCKED, |profiler| { - let event_id = SelfProfiler::get_query_name_string_id(query_name); - TimingGuard::start(profiler, profiler.query_blocked_event_kind, event_id) - }) - } - - /// Start profiling how long it takes to load a query result from the - /// incremental compilation on-disk cache. Profiling continues until the - /// TimingGuard returned from this call is dropped. - #[inline(always)] - pub fn incr_cache_loading(&self, query_name: QueryName) -> TimingGuard<'_> { - self.exec(EventFilter::INCR_CACHE_LOADS, |profiler| { - let event_id = SelfProfiler::get_query_name_string_id(query_name); - TimingGuard::start( - profiler, - profiler.incremental_load_result_event_kind, - event_id - ) - }) - } - - #[inline(always)] - fn non_guard_query_event( - &self, - event_kind: fn(&SelfProfiler) -> StringId, - query_name: QueryName, - event_filter: EventFilter, - timestamp_kind: TimestampKind - ) { - drop(self.exec(event_filter, |profiler| { - let event_id = SelfProfiler::get_query_name_string_id(query_name); - let thread_id = thread_id_to_u64(std::thread::current().id()); - - profiler.profiler.record_event( - event_kind(profiler), - event_id, - thread_id, - timestamp_kind, - ); - - TimingGuard::none() - })); - } -} - -pub struct SelfProfiler { - profiler: Profiler, - event_filter_mask: EventFilter, - query_event_kind: StringId, - generic_activity_event_kind: StringId, - incremental_load_result_event_kind: StringId, - query_blocked_event_kind: StringId, - query_cache_hit_event_kind: StringId, -} - -impl SelfProfiler { - pub fn new( - output_directory: &Path, - crate_name: Option<&str>, - event_filters: &Option> - ) -> Result> { - fs::create_dir_all(output_directory)?; - - let crate_name = crate_name.unwrap_or("unknown-crate"); - let filename = format!("{}-{}.rustc_profile", crate_name, process::id()); - let path = output_directory.join(&filename); - let profiler = Profiler::new(&path)?; - - let query_event_kind = profiler.alloc_string("Query"); - let generic_activity_event_kind = profiler.alloc_string("GenericActivity"); - let incremental_load_result_event_kind = profiler.alloc_string("IncrementalLoadResult"); - let query_blocked_event_kind = profiler.alloc_string("QueryBlocked"); - let query_cache_hit_event_kind = profiler.alloc_string("QueryCacheHit"); - - let mut event_filter_mask = EventFilter::empty(); - - if let Some(ref event_filters) = *event_filters { - let mut unknown_events = vec![]; - for item in event_filters { - if let Some(&(_, mask)) = EVENT_FILTERS_BY_NAME.iter() - .find(|&(name, _)| name == item) { - event_filter_mask |= mask; - } else { - unknown_events.push(item.clone()); - } - } - - // Warn about any unknown event names - if unknown_events.len() > 0 { - unknown_events.sort(); - unknown_events.dedup(); - - warn!("Unknown self-profiler events specified: {}. Available options are: {}.", - unknown_events.join(", "), - EVENT_FILTERS_BY_NAME.iter() - .map(|&(name, _)| name.to_string()) - .collect::>() - .join(", ")); - } - } else { - event_filter_mask = EventFilter::DEFAULT; - } - - Ok(SelfProfiler { - profiler, - event_filter_mask, - query_event_kind, - generic_activity_event_kind, - incremental_load_result_event_kind, - query_blocked_event_kind, - query_cache_hit_event_kind, - }) - } - - fn get_query_name_string_id(query_name: QueryName) -> StringId { - let discriminant = unsafe { - mem::transmute::, u64>(mem::discriminant(&query_name)) - }; - - StringId::reserved(discriminant as u32) - } - - pub fn register_query_name(&self, query_name: QueryName) { - let id = SelfProfiler::get_query_name_string_id(query_name); - self.profiler.alloc_string_with_reserved_id(id, query_name.as_str()); - } -} - -#[must_use] -pub struct TimingGuard<'a>(Option>); - -impl<'a> TimingGuard<'a> { - #[inline] - pub fn start( - profiler: &'a SelfProfiler, - event_kind: StringId, - event_id: StringId, - ) -> TimingGuard<'a> { - let thread_id = thread_id_to_u64(std::thread::current().id()); - let raw_profiler = &profiler.profiler; - let timing_guard = raw_profiler.start_recording_interval_event(event_kind, - event_id, - thread_id); - TimingGuard(Some(timing_guard)) - } - - #[inline] - pub fn none() -> TimingGuard<'a> { - TimingGuard(None) - } -} diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index b302b9ae7f0..ed901fa064a 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs @@ -19,7 +19,7 @@ use rustc::util::nodemap::FxHashMap; use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc::ty::TyCtxt; use rustc::util::common::{time_depth, set_time_depth, print_time_passes_entry}; -use rustc::util::profiling::SelfProfilerRef; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_fs_util::link_or_copy; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::Lrc; diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index e79b3a81b96..0fd47115022 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -25,6 +25,8 @@ rayon-core = { version = "0.3.0", package = "rustc-rayon-core" } rustc-hash = "1.0.1" smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_index = { path = "../librustc_index", package = "rustc_index" } +bitflags = "1.2.1" +measureme = "0.4" [dependencies.parking_lot] version = "0.9" diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 474a42644d9..fb541637e5f 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -94,6 +94,7 @@ pub use ena::unify; pub mod vec_linked_list; pub mod work_queue; pub mod fingerprint; +pub mod profiling; pub struct OnDrop(pub F); diff --git a/src/librustc_data_structures/profiling.rs b/src/librustc_data_structures/profiling.rs new file mode 100644 index 00000000000..b89170cccdb --- /dev/null +++ b/src/librustc_data_structures/profiling.rs @@ -0,0 +1,315 @@ +use std::error::Error; +use std::fs; +use std::mem::{self, Discriminant}; +use std::path::Path; +use std::process; +use std::sync::Arc; +use std::thread::ThreadId; +use std::u32; + +use measureme::{StringId, TimestampKind}; + +/// MmapSerializatioSink is faster on macOS and Linux +/// but FileSerializationSink is faster on Windows +#[cfg(not(windows))] +type SerializationSink = measureme::MmapSerializationSink; +#[cfg(windows)] +type SerializationSink = measureme::FileSerializationSink; + +type Profiler = measureme::Profiler; + +pub trait QueryName: Sized + Copy { + fn discriminant(self) -> Discriminant; + fn as_str(self) -> &'static str; +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)] +pub enum ProfileCategory { + Parsing, + Expansion, + TypeChecking, + BorrowChecking, + Codegen, + Linking, + Other, +} + +bitflags::bitflags! { + struct EventFilter: u32 { + const GENERIC_ACTIVITIES = 1 << 0; + const QUERY_PROVIDERS = 1 << 1; + const QUERY_CACHE_HITS = 1 << 2; + const QUERY_BLOCKED = 1 << 3; + const INCR_CACHE_LOADS = 1 << 4; + + const DEFAULT = Self::GENERIC_ACTIVITIES.bits | + Self::QUERY_PROVIDERS.bits | + Self::QUERY_BLOCKED.bits | + Self::INCR_CACHE_LOADS.bits; + + // empty() and none() aren't const-fns unfortunately + const NONE = 0; + const ALL = !Self::NONE.bits; + } +} + +const EVENT_FILTERS_BY_NAME: &[(&str, EventFilter)] = &[ + ("none", EventFilter::NONE), + ("all", EventFilter::ALL), + ("generic-activity", EventFilter::GENERIC_ACTIVITIES), + ("query-provider", EventFilter::QUERY_PROVIDERS), + ("query-cache-hit", EventFilter::QUERY_CACHE_HITS), + ("query-blocked" , EventFilter::QUERY_BLOCKED), + ("incr-cache-load", EventFilter::INCR_CACHE_LOADS), +]; + +fn thread_id_to_u64(tid: ThreadId) -> u64 { + unsafe { mem::transmute::(tid) } +} + + +/// A reference to the SelfProfiler. It can be cloned and sent across thread +/// boundaries at will. +#[derive(Clone)] +pub struct SelfProfilerRef { + // This field is `None` if self-profiling is disabled for the current + // compilation session. + profiler: Option>, + + // We store the filter mask directly in the reference because that doesn't + // cost anything and allows for filtering with checking if the profiler is + // actually enabled. + event_filter_mask: EventFilter, +} + +impl SelfProfilerRef { + + pub fn new(profiler: Option>) -> SelfProfilerRef { + // If there is no SelfProfiler then the filter mask is set to NONE, + // ensuring that nothing ever tries to actually access it. + let event_filter_mask = profiler + .as_ref() + .map(|p| p.event_filter_mask) + .unwrap_or(EventFilter::NONE); + + SelfProfilerRef { + profiler, + event_filter_mask, + } + } + + // This shim makes sure that calls only get executed if the filter mask + // lets them pass. It also contains some trickery to make sure that + // code is optimized for non-profiling compilation sessions, i.e. anything + // past the filter check is never inlined so it doesn't clutter the fast + // path. + #[inline(always)] + fn exec(&self, event_filter: EventFilter, f: F) -> TimingGuard<'_> + where F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a> + { + #[inline(never)] + fn cold_call(profiler_ref: &SelfProfilerRef, f: F) -> TimingGuard<'_> + where F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a> + { + let profiler = profiler_ref.profiler.as_ref().unwrap(); + f(&**profiler) + } + + if unlikely!(self.event_filter_mask.contains(event_filter)) { + cold_call(self, f) + } else { + TimingGuard::none() + } + } + + /// Start profiling a generic activity. Profiling continues until the + /// TimingGuard returned from this call is dropped. + #[inline(always)] + pub fn generic_activity(&self, event_id: &str) -> TimingGuard<'_> { + self.exec(EventFilter::GENERIC_ACTIVITIES, |profiler| { + let event_id = profiler.profiler.alloc_string(event_id); + TimingGuard::start( + profiler, + profiler.generic_activity_event_kind, + event_id + ) + }) + } + + /// Start profiling a query provider. Profiling continues until the + /// TimingGuard returned from this call is dropped. + #[inline(always)] + pub fn query_provider(&self, query_name: impl QueryName) -> TimingGuard<'_> { + self.exec(EventFilter::QUERY_PROVIDERS, |profiler| { + let event_id = SelfProfiler::get_query_name_string_id(query_name); + TimingGuard::start(profiler, profiler.query_event_kind, event_id) + }) + } + + /// Record a query in-memory cache hit. + #[inline(always)] + pub fn query_cache_hit(&self, query_name: impl QueryName) { + self.non_guard_query_event( + |profiler| profiler.query_cache_hit_event_kind, + query_name, + EventFilter::QUERY_CACHE_HITS, + TimestampKind::Instant, + ); + } + + /// Start profiling a query being blocked on a concurrent execution. + /// Profiling continues until the TimingGuard returned from this call is + /// dropped. + #[inline(always)] + pub fn query_blocked(&self, query_name: impl QueryName) -> TimingGuard<'_> { + self.exec(EventFilter::QUERY_BLOCKED, |profiler| { + let event_id = SelfProfiler::get_query_name_string_id(query_name); + TimingGuard::start(profiler, profiler.query_blocked_event_kind, event_id) + }) + } + + /// Start profiling how long it takes to load a query result from the + /// incremental compilation on-disk cache. Profiling continues until the + /// TimingGuard returned from this call is dropped. + #[inline(always)] + pub fn incr_cache_loading(&self, query_name: impl QueryName) -> TimingGuard<'_> { + self.exec(EventFilter::INCR_CACHE_LOADS, |profiler| { + let event_id = SelfProfiler::get_query_name_string_id(query_name); + TimingGuard::start( + profiler, + profiler.incremental_load_result_event_kind, + event_id + ) + }) + } + + #[inline(always)] + fn non_guard_query_event( + &self, + event_kind: fn(&SelfProfiler) -> StringId, + query_name: impl QueryName, + event_filter: EventFilter, + timestamp_kind: TimestampKind + ) { + drop(self.exec(event_filter, |profiler| { + let event_id = SelfProfiler::get_query_name_string_id(query_name); + let thread_id = thread_id_to_u64(std::thread::current().id()); + + profiler.profiler.record_event( + event_kind(profiler), + event_id, + thread_id, + timestamp_kind, + ); + + TimingGuard::none() + })); + } +} + +pub struct SelfProfiler { + profiler: Profiler, + event_filter_mask: EventFilter, + query_event_kind: StringId, + generic_activity_event_kind: StringId, + incremental_load_result_event_kind: StringId, + query_blocked_event_kind: StringId, + query_cache_hit_event_kind: StringId, +} + +impl SelfProfiler { + pub fn new( + output_directory: &Path, + crate_name: Option<&str>, + event_filters: &Option> + ) -> Result> { + fs::create_dir_all(output_directory)?; + + let crate_name = crate_name.unwrap_or("unknown-crate"); + let filename = format!("{}-{}.rustc_profile", crate_name, process::id()); + let path = output_directory.join(&filename); + let profiler = Profiler::new(&path)?; + + let query_event_kind = profiler.alloc_string("Query"); + let generic_activity_event_kind = profiler.alloc_string("GenericActivity"); + let incremental_load_result_event_kind = profiler.alloc_string("IncrementalLoadResult"); + let query_blocked_event_kind = profiler.alloc_string("QueryBlocked"); + let query_cache_hit_event_kind = profiler.alloc_string("QueryCacheHit"); + + let mut event_filter_mask = EventFilter::empty(); + + if let Some(ref event_filters) = *event_filters { + let mut unknown_events = vec![]; + for item in event_filters { + if let Some(&(_, mask)) = EVENT_FILTERS_BY_NAME.iter() + .find(|&(name, _)| name == item) { + event_filter_mask |= mask; + } else { + unknown_events.push(item.clone()); + } + } + + // Warn about any unknown event names + if unknown_events.len() > 0 { + unknown_events.sort(); + unknown_events.dedup(); + + warn!("Unknown self-profiler events specified: {}. Available options are: {}.", + unknown_events.join(", "), + EVENT_FILTERS_BY_NAME.iter() + .map(|&(name, _)| name.to_string()) + .collect::>() + .join(", ")); + } + } else { + event_filter_mask = EventFilter::DEFAULT; + } + + Ok(SelfProfiler { + profiler, + event_filter_mask, + query_event_kind, + generic_activity_event_kind, + incremental_load_result_event_kind, + query_blocked_event_kind, + query_cache_hit_event_kind, + }) + } + + fn get_query_name_string_id(query_name: impl QueryName) -> StringId { + let discriminant = unsafe { + mem::transmute::, u64>(query_name.discriminant()) + }; + + StringId::reserved(discriminant as u32) + } + + pub fn register_query_name(&self, query_name: impl QueryName) { + let id = SelfProfiler::get_query_name_string_id(query_name); + self.profiler.alloc_string_with_reserved_id(id, query_name.as_str()); + } +} + +#[must_use] +pub struct TimingGuard<'a>(Option>); + +impl<'a> TimingGuard<'a> { + #[inline] + pub fn start( + profiler: &'a SelfProfiler, + event_kind: StringId, + event_id: StringId, + ) -> TimingGuard<'a> { + let thread_id = thread_id_to_u64(std::thread::current().id()); + let raw_profiler = &profiler.profiler; + let timing_guard = raw_profiler.start_recording_interval_event(event_kind, + event_id, + thread_id); + TimingGuard(Some(timing_guard)) + } + + #[inline] + pub fn none() -> TimingGuard<'a> { + TimingGuard(None) + } +} -- cgit 1.4.1-3-g733a5