From 90f67c11e5de39341b8b212a022ce997f9382eb3 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 22 Mar 2019 13:15:50 -0400 Subject: Add a sampling profiler and a script to generate profiles for use with Gecko tooling. --- components/constellation/constellation.rs | 23 +++++++++++++++-------- components/constellation/pipeline.rs | 8 ++++---- 2 files changed, 19 insertions(+), 12 deletions(-) (limited to 'components/constellation') diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index e4120579912..7dc9074eb2f 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -124,7 +124,7 @@ use keyboard_types::webdriver::Event as WebDriverInputEvent; use keyboard_types::KeyboardEvent; use layout_traits::LayoutThreadFactory; use log::{Level, LevelFilter, Log, Metadata, Record}; -use msg::constellation_msg::{BackgroundHangMonitorRegister, HangAlert}; +use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert}; use msg::constellation_msg::{ BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId, }; @@ -210,11 +210,11 @@ pub struct Constellation { /// A channel for the background hang monitor to send messages /// to the constellation. - background_hang_monitor_sender: IpcSender, + background_hang_monitor_sender: IpcSender, /// A channel for the constellation to receiver messages /// from the background hang monitor. - background_hang_monitor_receiver: Receiver>, + background_hang_monitor_receiver: Receiver>, /// An IPC channel for layout threads to send messages to the constellation. /// This is the layout threads' view of `layout_receiver`. @@ -942,7 +942,7 @@ where #[derive(Debug)] enum Request { Script((PipelineId, FromScriptMsg)), - BackgroundHangMonitor(HangAlert), + BackgroundHangMonitor(HangMonitorAlert), Compositor(FromCompositorMsg), Layout(FromLayoutMsg), NetworkListener((PipelineId, FetchResponseMsg)), @@ -1008,10 +1008,17 @@ where } } - fn handle_request_from_background_hang_monitor(&self, message: HangAlert) { - // TODO: In case of a permanent hang being reported, add a "kill script" workflow, - // via the embedder? - warn!("Component hang alert: {:?}", message); + fn handle_request_from_background_hang_monitor(&self, message: HangMonitorAlert) { + match message { + HangMonitorAlert::Profile(bytes) => self + .embedder_proxy + .send((None, EmbedderMsg::ReportProfile(bytes))), + HangMonitorAlert::Hang(hang) => { + // TODO: In case of a permanent hang being reported, add a "kill script" workflow, + // via the embedder? + warn!("Component hang alert: {:?}", hang); + }, + } } fn handle_request_from_network_listener(&mut self, message: (PipelineId, FetchResponseMsg)) { diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index be369c0c08e..4e9d795d459 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -18,7 +18,7 @@ use ipc_channel::Error; use layout_traits::LayoutThreadFactory; use metrics::PaintTimeMetrics; use msg::constellation_msg::TopLevelBrowsingContextId; -use msg::constellation_msg::{BackgroundHangMonitorRegister, HangAlert}; +use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert}; use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId, PipelineNamespaceId}; use net::image_cache::ImageCacheImpl; use net_traits::image_cache::ImageCache; @@ -122,7 +122,7 @@ pub struct InitialPipelineState { pub background_monitor_register: Option>, /// A channel for the background hang monitor to send messages to the constellation. - pub background_hang_monitor_to_constellation_chan: IpcSender, + pub background_hang_monitor_to_constellation_chan: IpcSender, /// A channel for the layout thread to send messages to the constellation. pub layout_to_constellation_chan: IpcSender, @@ -467,7 +467,7 @@ pub struct UnprivilegedPipelineContent { parent_pipeline_id: Option, opener: Option, script_to_constellation_chan: ScriptToConstellationChan, - background_hang_monitor_to_constellation_chan: IpcSender, + background_hang_monitor_to_constellation_chan: IpcSender, layout_to_constellation_chan: IpcSender, scheduler_chan: IpcSender, devtools_chan: Option>, @@ -669,7 +669,7 @@ impl UnprivilegedPipelineContent { } } - pub fn background_hang_monitor_to_constellation_chan(&self) -> &IpcSender { + pub fn background_hang_monitor_to_constellation_chan(&self) -> &IpcSender { &self.background_hang_monitor_to_constellation_chan } -- cgit v1.2.3 From 8b7244f0d1d259971547e76aa299f404b5baedfd Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 25 Mar 2019 14:10:44 -0400 Subject: Support multiprocess in sampling profiler. --- components/constellation/constellation.rs | 45 +++++++++++++++++++++++++------ components/constellation/pipeline.rs | 45 ++++++++++++++++++++++--------- 2 files changed, 70 insertions(+), 20 deletions(-) (limited to 'components/constellation') diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 7dc9074eb2f..188f2ed7c3b 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -124,7 +124,7 @@ use keyboard_types::webdriver::Event as WebDriverInputEvent; use keyboard_types::KeyboardEvent; use layout_traits::LayoutThreadFactory; use log::{Level, LevelFilter, Log, Metadata, Record}; -use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert}; +use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert, SamplerControlMsg}; use msg::constellation_msg::{ BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId, }; @@ -208,6 +208,9 @@ pub struct Constellation { /// None when in multiprocess mode. background_monitor_register: Option>, + /// Channels to control all sampling profilers. + sampling_profiler_control: Vec>, + /// A channel for the background hang monitor to send messages /// to the constellation. background_hang_monitor_sender: IpcSender, @@ -614,12 +617,19 @@ where // If we are in multiprocess mode, // a dedicated per-process hang monitor will be initialized later inside the content process. // See run_content_process in servo/lib.rs - let background_monitor_register = if opts::multiprocess() { - None + let (background_monitor_register, sampler_chan) = if opts::multiprocess() { + (None, vec![]) } else { - Some(HangMonitorRegister::init( - background_hang_monitor_sender.clone(), - )) + let (sampling_profiler_control, sampling_profiler_port) = + ipc::channel().expect("ipc channel failure"); + + ( + Some(HangMonitorRegister::init( + background_hang_monitor_sender.clone(), + sampling_profiler_port, + )), + vec![sampling_profiler_control], + ) }; let (ipc_layout_sender, ipc_layout_receiver) = @@ -640,6 +650,7 @@ where background_hang_monitor_sender, background_hang_monitor_receiver, background_monitor_register, + sampling_profiler_control: sampler_chan, layout_sender: ipc_layout_sender, script_receiver: script_receiver, compositor_receiver: compositor_receiver, @@ -841,6 +852,10 @@ where Err(e) => return self.handle_send_error(pipeline_id, e), }; + if let Some(sampler_chan) = pipeline.sampler_control_chan { + self.sampling_profiler_control.push(sampler_chan); + } + if let Some(host) = host { debug!( "Adding new host entry {} for top-level browsing context {}.", @@ -848,11 +863,11 @@ where ); let _ = self .event_loops - .insert(host, Rc::downgrade(&pipeline.event_loop)); + .insert(host, Rc::downgrade(&pipeline.pipeline.event_loop)); } assert!(!self.pipelines.contains_key(&pipeline_id)); - self.pipelines.insert(pipeline_id, pipeline); + self.pipelines.insert(pipeline_id, pipeline.pipeline); } /// Get an iterator for the fully active browsing contexts in a subtree. @@ -1202,6 +1217,20 @@ where self.forward_event(destination_pipeline_id, event); }, FromCompositorMsg::SetCursor(cursor) => self.handle_set_cursor_msg(cursor), + FromCompositorMsg::EnableProfiler(rate) => { + for chan in &self.sampling_profiler_control { + if let Err(e) = chan.send(SamplerControlMsg::Enable(rate)) { + warn!("error communicating with sampling profiler: {}", e); + } + } + }, + FromCompositorMsg::DisableProfiler => { + for chan in &self.sampling_profiler_control { + if let Err(e) = chan.send(SamplerControlMsg::Disable) { + warn!("error communicating with sampling profiler: {}", e); + } + } + }, } } diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index 4e9d795d459..f518f48f295 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::event_loop::EventLoop; +use background_hang_monitor::HangMonitorRegister; use bluetooth_traits::BluetoothRequest; use canvas_traits::webgl::WebGLPipeline; use compositing::compositor_thread::Msg as CompositorMsg; @@ -18,7 +19,7 @@ use ipc_channel::Error; use layout_traits::LayoutThreadFactory; use metrics::PaintTimeMetrics; use msg::constellation_msg::TopLevelBrowsingContextId; -use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert}; +use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert, SamplerControlMsg}; use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId, PipelineNamespaceId}; use net::image_cache::ImageCacheImpl; use net_traits::image_cache::ImageCache; @@ -188,10 +189,15 @@ pub struct InitialPipelineState { pub webvr_chan: Option>, } +pub struct NewPipeline { + pub pipeline: Pipeline, + pub sampler_control_chan: Option>, +} + impl Pipeline { /// Starts a layout thread, and possibly a script thread, in /// a new process if requested. - pub fn spawn(state: InitialPipelineState) -> Result + pub fn spawn(state: InitialPipelineState) -> Result where LTF: LayoutThreadFactory, STF: ScriptThreadFactory, @@ -210,7 +216,7 @@ impl Pipeline { let url = state.load_data.url.clone(); - let script_chan = match state.event_loop { + let (script_chan, sampler_chan) = match state.event_loop { Some(script_chan) => { let new_layout_info = NewLayoutInfo { parent_info: state.parent_pipeline_id, @@ -229,7 +235,7 @@ impl Pipeline { { warn!("Sending to script during pipeline creation failed ({})", e); } - script_chan + (script_chan, None) }, None => { let (script_chan, script_port) = ipc::channel().expect("Pipeline script chan"); @@ -262,7 +268,7 @@ impl Pipeline { let (script_content_process_shutdown_chan, script_content_process_shutdown_port) = ipc::channel().expect("Pipeline script content process shutdown chan"); - let unprivileged_pipeline_content = UnprivilegedPipelineContent { + let mut unprivileged_pipeline_content = UnprivilegedPipelineContent { id: state.id, browsing_context_id: state.browsing_context_id, top_level_browsing_context_id: state.top_level_browsing_context_id, @@ -272,6 +278,7 @@ impl Pipeline { background_hang_monitor_to_constellation_chan: state .background_hang_monitor_to_constellation_chan .clone(), + sampling_profiler_port: None, scheduler_chan: state.scheduler_chan, devtools_chan: script_to_devtools_chan, bluetooth_thread: state.bluetooth_thread, @@ -302,21 +309,25 @@ impl Pipeline { // Spawn the child process. // // Yes, that's all there is to it! - if opts::multiprocess() { + let sampler_chan = if opts::multiprocess() { + let (sampler_chan, sampler_port) = ipc::channel().expect("Sampler chan"); + unprivileged_pipeline_content.sampling_profiler_port = Some(sampler_port); let _ = unprivileged_pipeline_content.spawn_multiprocess()?; + Some(sampler_chan) } else { // Should not be None in single-process mode. let register = state .background_monitor_register .expect("Couldn't start content, no background monitor has been initiated"); unprivileged_pipeline_content.start_all::(false, register); - } + None + }; - EventLoop::new(script_chan) + (EventLoop::new(script_chan), sampler_chan) }, }; - Ok(Pipeline::new( + let pipeline = Pipeline::new( state.id, state.browsing_context_id, state.top_level_browsing_context_id, @@ -327,7 +338,11 @@ impl Pipeline { url, state.prev_visibility, state.load_data, - )) + ); + Ok(NewPipeline { + pipeline, + sampler_control_chan: sampler_chan, + }) } /// Creates a new `Pipeline`, after the script and layout threads have been @@ -468,6 +483,7 @@ pub struct UnprivilegedPipelineContent { opener: Option, script_to_constellation_chan: ScriptToConstellationChan, background_hang_monitor_to_constellation_chan: IpcSender, + sampling_profiler_port: Option>, layout_to_constellation_chan: IpcSender, scheduler_chan: IpcSender, devtools_chan: Option>, @@ -669,8 +685,13 @@ impl UnprivilegedPipelineContent { } } - pub fn background_hang_monitor_to_constellation_chan(&self) -> &IpcSender { - &self.background_hang_monitor_to_constellation_chan + pub fn register_with_background_hang_monitor(&mut self) -> Box { + HangMonitorRegister::init( + self.background_hang_monitor_to_constellation_chan.clone(), + self.sampling_profiler_port + .take() + .expect("no sampling profiler?"), + ) } pub fn script_to_constellation_chan(&self) -> &ScriptToConstellationChan { -- cgit v1.2.3