From 3a22bb6c495182f6607a90e20b88deb05cb558f6 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Tue, 14 May 2019 05:00:45 +0000 Subject: [PATCH] style: Add Gecko profiler labels for when the style threads are doing work. Differential Revision: https://phabricator.services.mozilla.com/D30869 --- components/style/Cargo.toml | 1 + components/style/driver.rs | 1 + components/style/gecko/mod.rs | 2 + components/style/gecko/profiler.rs | 76 ++++++++++++++++++++++++++++++ components/style/macros.rs | 30 ++++++++++++ components/style/parallel.rs | 2 + 6 files changed, 112 insertions(+) create mode 100644 components/style/gecko/profiler.rs diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index bf953a13f8f9..ed6e76907fde 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -22,6 +22,7 @@ servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5eve "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" diff --git a/components/style/driver.rs b/components/style/driver.rs index e9433f73a031..6b05bc85281f 100644 --- a/components/style/driver.rs +++ b/components/style/driver.rs @@ -134,6 +134,7 @@ pub fn traverse_dom( let drain = discovered.drain(..); pool.install(|| { rayon::scope(|scope| { + profiler_label!(Style); parallel::traverse_nodes( drain, DispatchMode::TailCall, diff --git a/components/style/gecko/mod.rs b/components/style/gecko/mod.rs index fb332dcce6e2..43d38d7dbc1c 100644 --- a/components/style/gecko/mod.rs +++ b/components/style/gecko/mod.rs @@ -13,6 +13,8 @@ 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; diff --git a/components/style/gecko/profiler.rs b/components/style/gecko/profiler.rs new file mode 100644 index 000000000000..92ad068284e8 --- /dev/null +++ b/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 +} diff --git a/components/style/macros.rs b/components/style/macros.rs index 22f1966071bc..1ab2fe0720ea 100644 --- a/components/style/macros.rs +++ b/components/style/macros.rs @@ -104,3 +104,33 @@ macro_rules! define_keyword_type { } }; } + +/// 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) => {} +} diff --git a/components/style/parallel.rs b/components/style/parallel.rs index 237453084df7..619be351579d 100644 --- a/components/style/parallel.rs +++ b/components/style/parallel.rs @@ -277,6 +277,7 @@ pub fn traverse_nodes<'a, 'scope, E, D, I>( 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); }); @@ -286,6 +287,7 @@ pub fn traverse_nodes<'a, 'scope, E, D, I>( let nodes: WorkUnit = 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) });