diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2019-03-26 16:36:19 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-03-26 16:36:19 -0400 |
commit | 2e0191b8394a318cd88e4660cf09fe6c8d88552b (patch) | |
tree | 4ee54d356017cc613a4db9d117dff94ab0e88477 /components/constellation/pipeline.rs | |
parent | 7b8a898ac2ffef5ced8202dfd8bb40e37b972334 (diff) | |
parent | aee2974c330ed955958e90764f9a985c32fbc863 (diff) | |
download | servo-2e0191b8394a318cd88e4660cf09fe6c8d88552b.tar.gz servo-2e0191b8394a318cd88e4660cf09fe6c8d88552b.zip |
Auto merge of #23080 - jdm:sampling-profiler, r=gterzian
Add a sampling profiler
This uses the code already built for the background hang monitor and adds the ability to repeatedly sample all monitored threads. This sampling allows us to generate profiles that we can translate into the format used by https://perf-html.io/, allowing us to benefit from modern Gecko performance tooling.
You can run Servo with `PROFILE_OUTPUT=foo.json` and `SAMPLING_RATE=50` (for example), otherwise these values will default to `samples.json` and 10ms, respectively. To activate the profiler, press cmd+p, and to stop profiling, press cmd+p again. This will the captured samples to be symbolicated, which will take a very long time, and eventually there will be a new JSON profile in the output location.
To create a profile for use by Gecko's tools, run `python etc/profilicate.py path/to/profile.json >gecko_profile.json`, and load `gecko_profile.json` in the https://perf-html.io/ to see something like [this](https://profiler.firefox.com/public/8137e2b11fbb92afb80090bc534fd83015c87ee6/calltree/?globalTrackOrder=0-1&thread=1&v=3);
---
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #13103
- [x] These changes do not require tests because way too many pieces to automate
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23080)
<!-- Reviewable:end -->
Diffstat (limited to 'components/constellation/pipeline.rs')
-rw-r--r-- | components/constellation/pipeline.rs | 49 |
1 files changed, 35 insertions, 14 deletions
diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index 71d3f0aea62..2e8649b46bb 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, HangAlert}; +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; @@ -122,7 +123,7 @@ pub struct InitialPipelineState { pub background_monitor_register: Option<Box<BackgroundHangMonitorRegister>>, /// A channel for the background hang monitor to send messages to the constellation. - pub background_hang_monitor_to_constellation_chan: IpcSender<HangAlert>, + pub background_hang_monitor_to_constellation_chan: IpcSender<HangMonitorAlert>, /// A channel for the layout thread to send messages to the constellation. pub layout_to_constellation_chan: IpcSender<LayoutMsg>, @@ -188,10 +189,15 @@ pub struct InitialPipelineState { pub webvr_chan: Option<IpcSender<WebVRMsg>>, } +pub struct NewPipeline { + pub pipeline: Pipeline, + pub sampler_control_chan: Option<IpcSender<SamplerControlMsg>>, +} + impl Pipeline { /// Starts a layout thread, and possibly a script thread, in /// a new process if requested. - pub fn spawn<Message, LTF, STF>(state: InitialPipelineState) -> Result<Pipeline, Error> + pub fn spawn<Message, LTF, STF>(state: InitialPipelineState) -> Result<NewPipeline, Error> where LTF: LayoutThreadFactory<Message = Message>, STF: ScriptThreadFactory<Message = Message>, @@ -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::<Message, LTF, STF>(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 @@ -467,7 +482,8 @@ pub struct UnprivilegedPipelineContent { parent_pipeline_id: Option<PipelineId>, opener: Option<BrowsingContextId>, script_to_constellation_chan: ScriptToConstellationChan, - background_hang_monitor_to_constellation_chan: IpcSender<HangAlert>, + background_hang_monitor_to_constellation_chan: IpcSender<HangMonitorAlert>, + sampling_profiler_port: Option<IpcReceiver<SamplerControlMsg>>, layout_to_constellation_chan: IpcSender<LayoutMsg>, scheduler_chan: IpcSender<TimerSchedulerMsg>, devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>, @@ -669,8 +685,13 @@ impl UnprivilegedPipelineContent { } } - pub fn background_hang_monitor_to_constellation_chan(&self) -> &IpcSender<HangAlert> { - &self.background_hang_monitor_to_constellation_chan + pub fn register_with_background_hang_monitor(&mut self) -> Box<BackgroundHangMonitorRegister> { + 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 { |