about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Woerister <michaelwoerister@posteo>2019-09-27 14:03:09 +0200
committerMichael Woerister <michaelwoerister@posteo>2019-09-30 13:30:47 +0200
commitb0b073cdb07ccd392747fcaf1f1e949fe8921c1b (patch)
tree6e7195ff54e975c8f0779da5d4d84fb5fab3e4df
parentbf8491e72ef0d5ea0ef683e9d149408a717545e4 (diff)
Self-Profiling: Refactor SelfProfiler API to be RAII based where possible.
-rw-r--r--src/librustc/session/mod.rs24
-rw-r--r--src/librustc/ty/context.rs4
-rw-r--r--src/librustc/util/profiling.rs326
3 files changed, 244 insertions, 110 deletions
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 8e9c2735c39..e52f2788e6b 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -32,7 +32,7 @@ use syntax::source_map;
 use syntax::parse::{self, ParseSess};
 use syntax::symbol::Symbol;
 use syntax_pos::{MultiSpan, Span};
-use crate::util::profiling::SelfProfiler;
+use crate::util::profiling::{SelfProfiler, SelfProfilerRef};
 
 use rustc_target::spec::{PanicStrategy, RelroLevel, Target, TargetTriple};
 use rustc_data_structures::flock;
@@ -129,7 +129,7 @@ pub struct Session {
     pub profile_channel: Lock<Option<mpsc::Sender<ProfileQueriesMsg>>>,
 
     /// Used by `-Z self-profile`.
-    pub self_profiling: Option<Arc<SelfProfiler>>,
+    pub prof: SelfProfilerRef,
 
     /// Some measurements that are being gathered during compilation.
     pub perf_stats: PerfStats,
@@ -835,24 +835,6 @@ impl Session {
         }
     }
 
-    #[inline(never)]
-    #[cold]
-    fn profiler_active<F: FnOnce(&SelfProfiler) -> ()>(&self, f: F) {
-        match &self.self_profiling {
-            None => bug!("profiler_active() called but there was no profiler active"),
-            Some(profiler) => {
-                f(&profiler);
-            }
-        }
-    }
-
-    #[inline(always)]
-    pub fn profiler<F: FnOnce(&SelfProfiler) -> ()>(&self, f: F) {
-        if unlikely!(self.self_profiling.is_some()) {
-            self.profiler_active(f)
-        }
-    }
-
     pub fn print_perf_stats(&self) {
         println!(
             "Total time spent computing symbol hashes:      {}",
@@ -1257,7 +1239,7 @@ fn build_session_(
         imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())),
         incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
         cgu_reuse_tracker,
-        self_profiling: self_profiler,
+        prof: SelfProfilerRef::new(self_profiler),
         profile_channel: Lock::new(None),
         perf_stats: PerfStats {
             symbol_hash_time: Lock::new(Duration::from_secs(0)),
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 6c5d9a6dfdf..467f4c84ab3 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -45,6 +45,7 @@ use crate::ty::CanonicalPolyFnSig;
 use crate::util::common::ErrorReported;
 use crate::util::nodemap::{DefIdMap, DefIdSet, ItemLocalMap, ItemLocalSet};
 use crate::util::nodemap::{FxHashMap, FxHashSet};
+use crate::util::profiling::SelfProfilerRef;
 
 use errors::DiagnosticBuilder;
 use arena::SyncDroplessArena;
@@ -995,6 +996,8 @@ pub struct GlobalCtxt<'tcx> {
 
     pub dep_graph: DepGraph,
 
+    pub prof: SelfProfilerRef,
+
     /// Common objects.
     pub common: Common<'tcx>,
 
@@ -1225,6 +1228,7 @@ impl<'tcx> TyCtxt<'tcx> {
             arena: WorkerLocal::new(|_| Arena::default()),
             interners,
             dep_graph,
+            prof: s.prof.clone(),
             common,
             types: common_types,
             lifetimes: common_lifetimes,
diff --git a/src/librustc/util/profiling.rs b/src/librustc/util/profiling.rs
index 8624856a4f5..bd02e7f5a14 100644
--- a/src/librustc/util/profiling.rs
+++ b/src/librustc/util/profiling.rs
@@ -1,9 +1,9 @@
-use std::borrow::Cow;
 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;
 
@@ -62,6 +62,206 @@ fn thread_id_to_u64(tid: ThreadId) -> u64 {
     unsafe { mem::transmute::<ThreadId, u64>(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<Arc<SelfProfiler>>,
+
+    // 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<Arc<SelfProfiler>>) -> 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<F>(&self, event_filter: EventFilter, f: F) -> TimingGuard<'_>
+        where F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a>
+    {
+        #[inline(never)]
+        fn cold_call<F>(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 generic activity. Profiling continues until
+    /// `generic_activity_end` is called. The RAII-based `generic_activity`
+    /// usually is the better alternative.
+    #[inline(always)]
+    pub fn generic_activity_start(&self, event_id: &str) {
+        self.non_guard_generic_event(
+            |profiler| profiler.generic_activity_event_kind,
+            |profiler| profiler.profiler.alloc_string(event_id),
+            EventFilter::GENERIC_ACTIVITIES,
+            TimestampKind::Start,
+        );
+    }
+
+    /// End profiling a generic activity that was started with
+    /// `generic_activity_start`. The RAII-based `generic_activity` usually is
+    /// the better alternative.
+    #[inline(always)]
+    pub fn generic_activity_end(&self, event_id: &str) {
+        self.non_guard_generic_event(
+            |profiler| profiler.generic_activity_event_kind,
+            |profiler| profiler.profiler.alloc_string(event_id),
+            EventFilter::GENERIC_ACTIVITIES,
+            TimestampKind::End,
+        );
+    }
+
+    /// 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 `query_blocked_end` is called.
+    #[inline(always)]
+    pub fn query_blocked_start(&self, query_name: QueryName) {
+        self.non_guard_query_event(
+            |profiler| profiler.query_blocked_event_kind,
+            query_name,
+            EventFilter::QUERY_BLOCKED,
+            TimestampKind::Start,
+        );
+    }
+
+    /// End profiling a query being blocked on a concurrent execution.
+    #[inline(always)]
+    pub fn query_blocked_end(&self, query_name: QueryName) {
+        self.non_guard_query_event(
+            |profiler| profiler.query_blocked_event_kind,
+            query_name,
+            EventFilter::QUERY_BLOCKED,
+            TimestampKind::End,
+        );
+    }
+
+    /// 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()
+        }));
+    }
+
+    #[inline(always)]
+    fn non_guard_generic_event<F: FnOnce(&SelfProfiler) -> StringId>(
+        &self,
+        event_kind: fn(&SelfProfiler) -> StringId,
+        event_id: F,
+        event_filter: EventFilter,
+        timestamp_kind: TimestampKind
+    ) {
+        drop(self.exec(event_filter, |profiler| {
+            let thread_id = thread_id_to_u64(std::thread::current().id());
+
+            profiler.profiler.record_event(
+                event_kind(profiler),
+                event_id(profiler),
+                thread_id,
+                timestamp_kind,
+            );
+
+            TimingGuard::none()
+        }));
+    }
+}
+
 pub struct SelfProfiler {
     profiler: Profiler,
     event_filter_mask: EventFilter,
@@ -143,103 +343,51 @@ impl SelfProfiler {
         let id = SelfProfiler::get_query_name_string_id(query_name);
         self.profiler.alloc_string_with_reserved_id(id, query_name.as_str());
     }
+}
 
-    #[inline]
-    pub fn start_activity(
-        &self,
-        label: impl Into<Cow<'static, str>>,
-    ) {
-        if self.event_filter_mask.contains(EventFilter::GENERIC_ACTIVITIES) {
-            self.record(&label.into(), self.generic_activity_event_kind, TimestampKind::Start);
-        }
-    }
-
-    #[inline]
-    pub fn end_activity(
-        &self,
-        label: impl Into<Cow<'static, str>>,
-    ) {
-        if self.event_filter_mask.contains(EventFilter::GENERIC_ACTIVITIES) {
-            self.record(&label.into(), self.generic_activity_event_kind, TimestampKind::End);
-        }
-    }
-
-    #[inline]
-    pub fn record_query_hit(&self, query_name: QueryName) {
-        if self.event_filter_mask.contains(EventFilter::QUERY_CACHE_HITS) {
-            self.record_query(query_name, self.query_cache_hit_event_kind, TimestampKind::Instant);
-        }
-    }
-
-    #[inline]
-    pub fn start_query(&self, query_name: QueryName) {
-        if self.event_filter_mask.contains(EventFilter::QUERY_PROVIDERS) {
-            self.record_query(query_name, self.query_event_kind, TimestampKind::Start);
-        }
-    }
-
-    #[inline]
-    pub fn end_query(&self, query_name: QueryName) {
-        if self.event_filter_mask.contains(EventFilter::QUERY_PROVIDERS) {
-            self.record_query(query_name, self.query_event_kind, TimestampKind::End);
-        }
-    }
-
-    #[inline]
-    pub fn incremental_load_result_start(&self, query_name: QueryName) {
-        if self.event_filter_mask.contains(EventFilter::INCR_CACHE_LOADS) {
-            self.record_query(
-                query_name,
-                self.incremental_load_result_event_kind,
-                TimestampKind::Start
-            );
-        }
-    }
-
-    #[inline]
-    pub fn incremental_load_result_end(&self, query_name: QueryName) {
-        if self.event_filter_mask.contains(EventFilter::INCR_CACHE_LOADS) {
-            self.record_query(
-                query_name,
-                self.incremental_load_result_event_kind,
-                TimestampKind::End
-            );
-        }
-    }
+#[must_use]
+pub struct TimingGuard<'a>(Option<TimingGuardInternal<'a>>);
 
-    #[inline]
-    pub fn query_blocked_start(&self, query_name: QueryName) {
-        if self.event_filter_mask.contains(EventFilter::QUERY_BLOCKED) {
-            self.record_query(query_name, self.query_blocked_event_kind, TimestampKind::Start);
-        }
-    }
+struct TimingGuardInternal<'a> {
+    raw_profiler: &'a Profiler,
+    event_id: StringId,
+    event_kind: StringId,
+    thread_id: u64,
+}
 
+impl<'a> TimingGuard<'a> {
     #[inline]
-    pub fn query_blocked_end(&self, query_name: QueryName) {
-        if self.event_filter_mask.contains(EventFilter::QUERY_BLOCKED) {
-            self.record_query(query_name, self.query_blocked_event_kind, TimestampKind::End);
-        }
+    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;
+        raw_profiler.record_event(event_kind, event_id, thread_id, TimestampKind::Start);
+
+        TimingGuard(Some(TimingGuardInternal {
+            raw_profiler,
+            event_kind,
+            event_id,
+            thread_id,
+        }))
     }
 
     #[inline]
-    fn record(&self, event_id: &str, event_kind: StringId, timestamp_kind: TimestampKind) {
-        let thread_id = thread_id_to_u64(std::thread::current().id());
-
-        let event_id = self.profiler.alloc_string(event_id);
-        self.profiler.record_event(event_kind, event_id, thread_id, timestamp_kind);
+    pub fn none() -> TimingGuard<'a> {
+        TimingGuard(None)
     }
+}
 
+impl<'a> Drop for TimingGuardInternal<'a> {
     #[inline]
-    fn record_query(
-        &self,
-        query_name: QueryName,
-        event_kind: StringId,
-        timestamp_kind: TimestampKind,
-    ) {
-        let dep_node_name = SelfProfiler::get_query_name_string_id(query_name);
-
-        let thread_id = thread_id_to_u64(std::thread::current().id());
-
-        self.profiler.record_event(event_kind, dep_node_name, thread_id, timestamp_kind);
+    fn drop(&mut self) {
+        self.raw_profiler.record_event(
+            self.event_kind,
+            self.event_id,
+            self.thread_id,
+            TimestampKind::End
+        );
     }
 }