Bug 1505908 - Add Gecko profiler labels for when the style threads are doing work. r=emilio
authorCameron McCormack <cam@mcc.id.au>
Tue, 14 May 2019 05:00:45 +0000
changeset 532553 23396874909ff1d79eb198fa300c81bd66aafa2b
parent 532552 82c168608de778da5ab22efe0d884b2b283c0edd
child 532554 2d94f58711b3c1b501d72ed05845198fc3aeb6e8
push id11270
push userrgurzau@mozilla.com
push dateWed, 15 May 2019 15:07:19 +0000
treeherdermozilla-beta@571bc76da583 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1505908
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1505908 - Add Gecko profiler labels for when the style threads are doing work. r=emilio Differential Revision: https://phabricator.services.mozilla.com/D30869
layout/style/GeckoBindings.cpp
layout/style/GeckoBindings.h
layout/style/ServoBindings.toml
servo/components/style/Cargo.toml
servo/components/style/driver.rs
servo/components/style/gecko/mod.rs
servo/components/style/gecko/profiler.rs
servo/components/style/macros.rs
servo/components/style/parallel.rs
servo/ports/geckolib/Cargo.toml
servo/ports/geckolib/glue.rs
toolkit/library/rust/shared/Cargo.toml
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -2165,16 +2165,30 @@ NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsCSS
   }
 
 void Gecko_RegisterProfilerThread(const char* name) {
   PROFILER_REGISTER_THREAD(name);
 }
 
 void Gecko_UnregisterProfilerThread() { PROFILER_UNREGISTER_THREAD(); }
 
+#ifdef MOZ_GECKO_PROFILER
+void Gecko_Construct_AutoProfilerLabel(AutoProfilerLabel* aAutoLabel,
+                                       JS::ProfilingCategoryPair aCatPair) {
+  new (aAutoLabel) AutoProfilerLabel(
+      "", nullptr, aCatPair,
+      uint32_t(
+          js::ProfilingStackFrame::Flags::LABEL_DETERMINED_BY_CATEGORY_PAIR));
+}
+
+void Gecko_Destroy_AutoProfilerLabel(AutoProfilerLabel* aAutoLabel) {
+  aAutoLabel->~AutoProfilerLabel();
+}
+#endif
+
 bool Gecko_DocumentRule_UseForPresentation(
     const Document* aDocument, const nsACString* aPattern,
     DocumentMatchingFunction aMatchingFunction) {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsIURI* docURI = aDocument->GetDocumentURI();
   nsAutoCString docURISpec;
   if (docURI) {
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -730,16 +730,22 @@ void Gecko_AddPropertyToSet(nsCSSPropert
                                          const nsStyle##name* other);        \
   void Gecko_Destroy_nsStyle##name(nsStyle##name* ptr);
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 
 void Gecko_RegisterProfilerThread(const char* name);
 void Gecko_UnregisterProfilerThread();
 
+#ifdef MOZ_GECKO_PROFILER
+void Gecko_Construct_AutoProfilerLabel(mozilla::AutoProfilerLabel*,
+                                       JS::ProfilingCategoryPair);
+void Gecko_Destroy_AutoProfilerLabel(mozilla::AutoProfilerLabel*);
+#endif
+
 bool Gecko_DocumentRule_UseForPresentation(
     const mozilla::dom::Document*, const nsACString* aPattern,
     mozilla::css::DocumentMatchingFunction);
 
 // Allocator hinting.
 void Gecko_SetJemallocThreadLocalArena(bool enabled);
 
 // Pseudo-element flags.
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -30,16 +30,17 @@ headers = [
     "mozilla/ComputedStyle.h",
     "mozilla/ServoTraversalStatistics.h",
     "mozilla/SizeOfState.h",
     "nsCSSProps.h",
     "nsContentUtils.h",
     "nsNameSpaceManager.h",
     "nsMediaFeatures.h",
     "nsXBLBinding.h",
+    "GeckoProfiler.h",
 ]
 raw-lines = [
     # FIXME(emilio): Incrementally remove these "pub use"s. Probably
     # mozilla::css and mozilla::dom are easier.
     "pub use self::root::*;",
     "pub use self::root::mozilla::*;",
     "pub use self::root::mozilla::css::*;",
     "pub use self::root::mozilla::dom::*;",
@@ -154,16 +155,17 @@ whitelist-vars = [
     "SERVO_CSS_PSEUDO_ELEMENT_FLAGS_.*",
     "kNameSpaceID_.*",
     "kGenericFont_.*",
     "kPresContext_.*",
     "nsContentUtils_.*",
     "GECKO_IS_NIGHTLY",
     "mozilla::detail::gGkAtoms",
     "mozilla::detail::kGkAtomsArrayOffset",
+    "mozilla::profiler::detail::RacyFeatures::sActiveAndFeatures",
 ]
 # TODO(emilio): A bunch of types here can go away once we generate bindings and
 # structs together.
 whitelist-types = [
     "RawGecko.*",
     "RawServo.*",
     "ServoCssRules",
     "nsFontFaceRuleContainer",
@@ -323,16 +325,17 @@ whitelist-types = [
     "mozilla::DefaultDelete",
     "mozilla::Side",
     "mozilla::binding_danger::AssertAndSuppressCleanupPolicy",
     "mozilla::ParsingMode",
     "mozilla::InheritTarget",
     "mozilla::dom::MediaList",
     "mozilla::StyleRuleInclusion",
     "nsStyleTransformMatrix::MatrixTransformOperator",
+    "mozilla::profiler::detail::RacyFeatures",
 ]
 opaque-types = [
     "mozilla::StyleThinArc", # https://github.com/rust-lang/rust-bindgen/issues/1557
     "std::pair__PCCP",
     "std::namespace::atomic___base", "std::atomic__My_base",
     "std::atomic",
     "std::atomic___base",
     # We want everything but FontVariation and Float to be opaque but we don't
--- a/servo/components/style/Cargo.toml
+++ b/servo/components/style/Cargo.toml
@@ -17,16 +17,17 @@ doctest = false
 
 [features]
 gecko = ["nsstring", "style_traits/gecko", "fallible/known_system_malloc"]
 use_bindgen = ["bindgen", "regex", "toml"]
 servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever",
          "cssparser/serde", "encoding_rs", "malloc_size_of/servo", "arrayvec/use_union",
          "servo_url", "string_cache", "crossbeam-channel", "to_shmem/servo", "servo_arc/servo"]
 gecko_debug = []
+gecko_profiler = []
 
 [dependencies]
 app_units = "0.7"
 arrayvec = "0.4.6"
 atomic_refcell = "0.1"
 bitflags = "1.0"
 byteorder = "1.0"
 cssparser = "0.25"
--- a/servo/components/style/driver.rs
+++ b/servo/components/style/driver.rs
@@ -129,16 +129,17 @@ pub fn traverse_dom<E, D>(
             // depth for all the children.
             if pool.is_some() && discovered.len() > WORK_UNIT_MAX {
                 let pool = pool.unwrap();
                 maybe_tls = Some(ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool));
                 let root_opaque = root.as_node().opaque();
                 let drain = discovered.drain(..);
                 pool.install(|| {
                     rayon::scope(|scope| {
+                        profiler_label!(Style);
                         parallel::traverse_nodes(
                             drain,
                             DispatchMode::TailCall,
                             /* recursion_ok = */ true,
                             root_opaque,
                             PerLevelTraversalData {
                                 current_dom_depth: depth,
                             },
--- a/servo/components/style/gecko/mod.rs
+++ b/servo/components/style/gecko/mod.rs
@@ -8,16 +8,18 @@
 mod non_ts_pseudo_class_list;
 
 pub mod arc_types;
 pub mod boxed_types;
 pub mod conversions;
 pub mod data;
 pub mod media_features;
 pub mod media_queries;
+#[cfg(feature = "gecko_profiler")]
+pub mod profiler;
 pub mod pseudo_element;
 pub mod restyle_damage;
 pub mod rules;
 pub mod selector_parser;
 pub mod snapshot;
 pub mod snapshot_helpers;
 pub mod traversal;
 pub mod url;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/gecko/profiler.rs
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Gecko profiler support.
+//!
+//! Use the `profiler_label!` macro from macros.rs.
+
+use crate::gecko_bindings::structs;
+
+/// A label describing a category of work that style threads can perform.
+pub enum ProfilerLabel {
+    /// Style computation.
+    Style,
+    /// Style sheet parsing.
+    Parse,
+}
+
+/// RAII object that constructs and destroys a C++ AutoProfilerLabel object
+/// pointed to be the specified reference.
+#[cfg(feature = "gecko_profiler")]
+pub struct AutoProfilerLabel<'a>(&'a mut structs::AutoProfilerLabel);
+
+#[cfg(feature = "gecko_profiler")]
+impl<'a> AutoProfilerLabel<'a> {
+    /// Creates a new AutoProfilerLabel with the specified label type.
+    ///
+    /// unsafe since the caller must ensure that `label` is allocated on the
+    /// stack.
+    #[inline]
+    pub unsafe fn new(
+        label: &mut structs::AutoProfilerLabel,
+        label_type: ProfilerLabel,
+    ) -> AutoProfilerLabel {
+        let category_pair = match label_type {
+            ProfilerLabel::Style => structs::JS::ProfilingCategoryPair_LAYOUT_StyleComputation,
+            ProfilerLabel::Parse => structs::JS::ProfilingCategoryPair_LAYOUT_CSSParsing,
+        };
+        structs::Gecko_Construct_AutoProfilerLabel(label, category_pair);
+        AutoProfilerLabel(label)
+    }
+}
+
+#[cfg(feature = "gecko_profiler")]
+impl<'a> Drop for AutoProfilerLabel<'a> {
+    #[inline]
+    fn drop(&mut self) {
+        unsafe {
+            structs::Gecko_Destroy_AutoProfilerLabel(self.0);
+        }
+    }
+}
+
+/// Whether the Gecko profiler is currently active.
+///
+/// This implementation must be kept in sync with
+/// `mozilla::profiler::detail::RacyFeatures::IsActive`.
+#[cfg(feature = "gecko_profiler")]
+#[inline]
+pub fn profiler_is_active() -> bool {
+    use self::structs::profiler::detail;
+    use std::mem;
+    use std::sync::atomic::{AtomicU32, Ordering};
+
+    let active_and_features: &AtomicU32 = unsafe {
+        mem::transmute(&detail::RacyFeatures_sActiveAndFeatures)
+    };
+    (active_and_features.load(Ordering::Relaxed) & detail::RacyFeatures_Active) != 0
+}
+
+/// Always false when the Gecko profiler is disabled.
+#[cfg(not(feature = "gecko_profiler"))]
+#[inline]
+pub fn profiler_is_active() -> bool {
+    false
+}
--- a/servo/components/style/macros.rs
+++ b/servo/components/style/macros.rs
@@ -99,8 +99,38 @@ macro_rules! define_keyword_type {
                 input
                     .expect_ident_matching($css)
                     .map(|_| $name)
                     .map_err(|e| e.into())
             }
         }
     };
 }
+
+/// Place a Gecko profiler label on the stack.
+///
+/// The `label_type` argument must be the name of a variant of `ProfilerLabel`.
+#[cfg(feature = "gecko_profiler")]
+#[macro_export]
+macro_rules! profiler_label {
+    ($label_type:ident) => {
+        let mut _profiler_label: $crate::gecko_bindings::structs::AutoProfilerLabel = unsafe {
+            ::std::mem::uninitialized()
+        };
+        let _profiler_label = if $crate::gecko::profiler::profiler_is_active() {
+            unsafe {
+                Some($crate::gecko::profiler::AutoProfilerLabel::new(
+                    &mut _profiler_label,
+                    $crate::gecko::profiler::ProfilerLabel::$label_type,
+                ))
+            }
+        } else {
+            None
+        };
+    }
+}
+
+/// No-op when the Gecko profiler is not available.
+#[cfg(not(feature = "gecko_profiler"))]
+#[macro_export]
+macro_rules! profiler_label {
+    ($label_type:ident) => {}
+}
--- a/servo/components/style/parallel.rs
+++ b/servo/components/style/parallel.rs
@@ -272,23 +272,25 @@ pub fn traverse_nodes<'a, 'scope, E, D, 
     // In the common case, our children fit within a single work unit, in which
     // case we can pass the SmallVec directly and avoid extra allocation.
     if nodes.len() <= WORK_UNIT_MAX {
         let work: WorkUnit<E::ConcreteNode> = nodes.collect();
         if may_dispatch_tail {
             top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls);
         } else {
             scope.spawn(move |scope| {
+                profiler_label!(Style);
                 let work = work;
                 top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls);
             });
         }
     } else {
         for chunk in nodes.chunks(WORK_UNIT_MAX).into_iter() {
             let nodes: WorkUnit<E::ConcreteNode> = chunk.collect();
             let traversal_data_copy = traversal_data.clone();
             scope.spawn(move |scope| {
+                profiler_label!(Style);
                 let n = nodes;
                 top_down_dom(&*n, root, traversal_data_copy, scope, pool, traversal, tls)
             });
         }
     }
 }
--- a/servo/ports/geckolib/Cargo.toml
+++ b/servo/ports/geckolib/Cargo.toml
@@ -6,16 +6,17 @@ license = "MPL-2.0"
 
 [lib]
 name = "geckoservo"
 path = "lib.rs"
 
 [features]
 bindgen = ["style/use_bindgen"]
 gecko_debug = ["style/gecko_debug", "nsstring/gecko_debug"]
+gecko_profiler = ["style/gecko_profiler"]
 
 [dependencies]
 atomic_refcell = "0.1"
 cssparser = "0.25"
 cstr = "0.1.2"
 libc = "0.2"
 log = {version = "0.4", features = ["release_max_level_info"]}
 malloc_size_of = {path = "../../components/malloc_size_of"}
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -13,16 +13,17 @@ use selectors::{NthIndexCache, SelectorL
 use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
 use smallvec::SmallVec;
 use std::cell::RefCell;
 use std::collections::BTreeSet;
 use std::fmt::Write;
 use std::iter;
 use std::os::raw::c_void;
 use std::ptr;
+use style::profiler_label;
 use style::applicable_declarations::ApplicableDeclarationBlock;
 use style::author_styles::AuthorStyles;
 use style::context::ThreadLocalStyleContext;
 use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext};
 use style::counter_style;
 use style::data::{self, ElementStyles};
 use style::dom::{ShowSubtreeData, TDocument, TElement, TNode};
 use style::driver;
@@ -1406,16 +1407,17 @@ pub unsafe extern "C" fn Servo_StyleShee
         mode_to_origin(mode),
         quirks_mode.into(),
         line_number_offset,
         should_record_use_counters,
     );
 
     if let Some(thread_pool) = STYLE_THREAD_POOL.style_thread_pool.as_ref() {
         thread_pool.spawn(|| {
+            profiler_label!(Parse);
             async_parser.parse();
         });
     } else {
         async_parser.parse();
     }
 }
 
 #[no_mangle]
--- a/toolkit/library/rust/shared/Cargo.toml
+++ b/toolkit/library/rust/shared/Cargo.toml
@@ -54,17 +54,17 @@ gecko_debug = ["geckoservo/gecko_debug",
 simd-accel = ["encoding_c/simd-accel", "encoding_glue/simd-accel"]
 moz_memory = ["mp4parse_capi/mp4parse_fallible"]
 moz_places = ["bookmark_sync"]
 spidermonkey_rust = ["jsrust_shared"]
 cranelift_x86 = ["jsrust_shared/cranelift_x86"]
 cranelift_arm32 = ["jsrust_shared/cranelift_arm32"]
 cranelift_arm64 = ["jsrust_shared/cranelift_arm64"]
 cranelift_none = ["jsrust_shared/cranelift_none"]
-gecko_profiler = ["profiler_helper"]
+gecko_profiler = ["profiler_helper", "geckoservo/gecko_profiler"]
 gecko_profiler_parse_elf = ["profiler_helper/parse_elf"]
 new_xulstore = ["xulstore"]
 new_cert_storage = ["cert_storage"]
 
 [lib]
 path = "lib.rs"
 test = false
 doctest = false