mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
Support multiprocess in sampling profiler.
This commit is contained in:
parent
90f67c11e5
commit
8b7244f0d1
9 changed files with 165 additions and 117 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -152,7 +152,6 @@ dependencies = [
|
||||||
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossbeam-channel 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam-channel 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ipc-channel 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ipc-channel 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -16,7 +16,6 @@ doctest = false
|
||||||
backtrace = "0.3"
|
backtrace = "0.3"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
ipc-channel = "0.11"
|
ipc-channel = "0.11"
|
||||||
lazy_static = "1"
|
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
msg = {path = "../msg"}
|
msg = {path = "../msg"}
|
||||||
|
|
|
@ -4,15 +4,15 @@
|
||||||
|
|
||||||
use crate::sampler::{NativeStack, Sampler};
|
use crate::sampler::{NativeStack, Sampler};
|
||||||
use crossbeam_channel::{after, unbounded, Receiver, Sender};
|
use crossbeam_channel::{after, unbounded, Receiver, Sender};
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||||
|
use ipc_channel::router::ROUTER;
|
||||||
use msg::constellation_msg::MonitoredComponentId;
|
use msg::constellation_msg::MonitoredComponentId;
|
||||||
use msg::constellation_msg::{
|
use msg::constellation_msg::{
|
||||||
BackgroundHangMonitor, BackgroundHangMonitorClone, BackgroundHangMonitorRegister,
|
BackgroundHangMonitor, BackgroundHangMonitorClone, BackgroundHangMonitorRegister,
|
||||||
};
|
};
|
||||||
use msg::constellation_msg::{HangAlert, HangAnnotation, HangMonitorAlert};
|
use msg::constellation_msg::{HangAlert, HangAnnotation, HangMonitorAlert, SamplerControlMsg};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
@ -21,46 +21,22 @@ pub struct HangMonitorRegister {
|
||||||
sender: Sender<(MonitoredComponentId, MonitoredComponentMsg)>,
|
sender: Sender<(MonitoredComponentId, MonitoredComponentMsg)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
|
||||||
enum SamplerState {
|
|
||||||
NotSampling,
|
|
||||||
StartSampling(Duration),
|
|
||||||
Sampling,
|
|
||||||
Resolving,
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref SAMPLING_STATE: Mutex<SamplerState> = Mutex::new(SamplerState::NotSampling);
|
|
||||||
}
|
|
||||||
impl HangMonitorRegister {
|
impl HangMonitorRegister {
|
||||||
/// Start a new hang monitor worker, and return a handle to register components for monitoring.
|
/// Start a new hang monitor worker, and return a handle to register components for monitoring.
|
||||||
pub fn init(
|
pub fn init(
|
||||||
constellation_chan: IpcSender<HangMonitorAlert>,
|
constellation_chan: IpcSender<HangMonitorAlert>,
|
||||||
|
control_port: IpcReceiver<SamplerControlMsg>,
|
||||||
) -> Box<BackgroundHangMonitorRegister> {
|
) -> Box<BackgroundHangMonitorRegister> {
|
||||||
let (sender, port) = unbounded();
|
let (sender, port) = unbounded();
|
||||||
let _ = thread::Builder::new().spawn(move || {
|
let _ = thread::Builder::new().spawn(move || {
|
||||||
let mut monitor = BackgroundHangMonitorWorker::new(constellation_chan, port);
|
let mut monitor =
|
||||||
|
BackgroundHangMonitorWorker::new(constellation_chan, control_port, port);
|
||||||
while monitor.run() {
|
while monitor.run() {
|
||||||
// Monitoring until all senders have been dropped...
|
// Monitoring until all senders have been dropped...
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Box::new(HangMonitorRegister { sender })
|
Box::new(HangMonitorRegister { sender })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle(rate: Duration) {
|
|
||||||
let state = *SAMPLING_STATE.lock().unwrap();
|
|
||||||
match state {
|
|
||||||
SamplerState::NotSampling => {
|
|
||||||
println!("Starting profiler.");
|
|
||||||
*SAMPLING_STATE.lock().unwrap() = SamplerState::StartSampling(rate);
|
|
||||||
},
|
|
||||||
SamplerState::Sampling => {
|
|
||||||
println!("Stopping profiler.");
|
|
||||||
*SAMPLING_STATE.lock().unwrap() = SamplerState::Resolving;
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackgroundHangMonitorRegister for HangMonitorRegister {
|
impl BackgroundHangMonitorRegister for HangMonitorRegister {
|
||||||
|
@ -175,6 +151,7 @@ pub struct BackgroundHangMonitorWorker {
|
||||||
monitored_components: HashMap<MonitoredComponentId, MonitoredComponent>,
|
monitored_components: HashMap<MonitoredComponentId, MonitoredComponent>,
|
||||||
constellation_chan: IpcSender<HangMonitorAlert>,
|
constellation_chan: IpcSender<HangMonitorAlert>,
|
||||||
port: Receiver<(MonitoredComponentId, MonitoredComponentMsg)>,
|
port: Receiver<(MonitoredComponentId, MonitoredComponentMsg)>,
|
||||||
|
control_port: Receiver<SamplerControlMsg>,
|
||||||
sampling_duration: Option<Duration>,
|
sampling_duration: Option<Duration>,
|
||||||
last_sample: Instant,
|
last_sample: Instant,
|
||||||
creation: Instant,
|
creation: Instant,
|
||||||
|
@ -185,13 +162,16 @@ pub struct BackgroundHangMonitorWorker {
|
||||||
impl BackgroundHangMonitorWorker {
|
impl BackgroundHangMonitorWorker {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
constellation_chan: IpcSender<HangMonitorAlert>,
|
constellation_chan: IpcSender<HangMonitorAlert>,
|
||||||
|
control_port: IpcReceiver<SamplerControlMsg>,
|
||||||
port: Receiver<(MonitoredComponentId, MonitoredComponentMsg)>,
|
port: Receiver<(MonitoredComponentId, MonitoredComponentMsg)>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let control_port = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(control_port);
|
||||||
Self {
|
Self {
|
||||||
component_names: Default::default(),
|
component_names: Default::default(),
|
||||||
monitored_components: Default::default(),
|
monitored_components: Default::default(),
|
||||||
constellation_chan,
|
constellation_chan,
|
||||||
port,
|
port,
|
||||||
|
control_port,
|
||||||
sampling_duration: None,
|
sampling_duration: None,
|
||||||
last_sample: Instant::now(),
|
last_sample: Instant::now(),
|
||||||
sampling_baseline: Instant::now(),
|
sampling_baseline: Instant::now(),
|
||||||
|
@ -200,64 +180,48 @@ impl BackgroundHangMonitorWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_sampling(&mut self) {
|
fn finish_sampled_profile(&mut self) {
|
||||||
let state = *SAMPLING_STATE.lock().unwrap();
|
let mut bytes = vec![];
|
||||||
match state {
|
bytes.extend(
|
||||||
SamplerState::StartSampling(rate) => {
|
format!(
|
||||||
*SAMPLING_STATE.lock().unwrap() = SamplerState::Sampling;
|
"{{ \"rate\": {}, \"start\": {}, \"data\": [\n",
|
||||||
self.sampling_duration = Some(rate);
|
self.sampling_duration.unwrap().as_millis(),
|
||||||
self.sampling_baseline = Instant::now();
|
(self.sampling_baseline - self.creation).as_millis(),
|
||||||
},
|
)
|
||||||
SamplerState::Resolving => {
|
.as_bytes(),
|
||||||
let mut bytes = vec![];
|
);
|
||||||
bytes.extend(
|
|
||||||
format!(
|
|
||||||
"{{ \"rate\": {}, \"start\": {}, \"data\": [\n",
|
|
||||||
self.sampling_duration.unwrap().as_millis(),
|
|
||||||
(self.sampling_baseline - self.creation).as_millis(),
|
|
||||||
)
|
|
||||||
.as_bytes(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
let to_resolve = self.samples.len();
|
let to_resolve = self.samples.len();
|
||||||
for (i, Sample(id, instant, stack)) in self.samples.drain(..).enumerate() {
|
for (i, Sample(id, instant, stack)) in self.samples.drain(..).enumerate() {
|
||||||
println!("Resolving {}/{}", i + 1, to_resolve);
|
println!("Resolving {}/{}", i + 1, to_resolve);
|
||||||
let profile = stack.to_hangprofile();
|
let profile = stack.to_hangprofile();
|
||||||
let name = match self.component_names.get(&id) {
|
let name = match self.component_names.get(&id) {
|
||||||
Some(ref s) => format!("\"{}\"", s),
|
Some(ref s) => format!("\"{}\"", s),
|
||||||
None => format!("null"),
|
None => format!("null"),
|
||||||
};
|
};
|
||||||
let json = format!(
|
let json = format!(
|
||||||
"{}{{ \"name\": {}, \"namespace\": {}, \"index\": {}, \"type\": \"{:?}\", \
|
"{}{{ \"name\": {}, \"namespace\": {}, \"index\": {}, \"type\": \"{:?}\", \
|
||||||
\"time\": {}, \"frames\": {} }}",
|
\"time\": {}, \"frames\": {} }}",
|
||||||
if !first { ",\n" } else { "" },
|
if !first { ",\n" } else { "" },
|
||||||
name,
|
name,
|
||||||
id.0.namespace_id.0,
|
id.0.namespace_id.0,
|
||||||
id.0.index.0.get(),
|
id.0.index.0.get(),
|
||||||
id.1,
|
id.1,
|
||||||
(instant - self.sampling_baseline).as_millis(),
|
(instant - self.sampling_baseline).as_millis(),
|
||||||
serde_json::to_string(&profile.backtrace).unwrap(),
|
serde_json::to_string(&profile.backtrace).unwrap(),
|
||||||
);
|
);
|
||||||
bytes.extend(json.as_bytes());
|
bytes.extend(json.as_bytes());
|
||||||
first = false;
|
first = false;
|
||||||
}
|
|
||||||
|
|
||||||
bytes.extend(b"\n] }");
|
|
||||||
let _ = self
|
|
||||||
.constellation_chan
|
|
||||||
.send(HangMonitorAlert::Profile(bytes));
|
|
||||||
|
|
||||||
*SAMPLING_STATE.lock().unwrap() = SamplerState::NotSampling;
|
|
||||||
self.sampling_duration = None;
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes.extend(b"\n] }");
|
||||||
|
let _ = self
|
||||||
|
.constellation_chan
|
||||||
|
.send(HangMonitorAlert::Profile(bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> bool {
|
pub fn run(&mut self) -> bool {
|
||||||
self.handle_sampling();
|
|
||||||
|
|
||||||
let timeout = if let Some(duration) = self.sampling_duration {
|
let timeout = if let Some(duration) = self.sampling_duration {
|
||||||
duration
|
duration
|
||||||
.checked_sub(Instant::now() - self.last_sample)
|
.checked_sub(Instant::now() - self.last_sample)
|
||||||
|
@ -273,6 +237,23 @@ impl BackgroundHangMonitorWorker {
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
recv(self.control_port) -> event => {
|
||||||
|
match event {
|
||||||
|
Ok(SamplerControlMsg::Enable(rate)) => {
|
||||||
|
println!("Enabling profiler.");
|
||||||
|
self.sampling_duration = Some(rate);
|
||||||
|
self.sampling_baseline = Instant::now();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Ok(SamplerControlMsg::Disable) => {
|
||||||
|
println!("Disabling profiler.");
|
||||||
|
self.finish_sampled_profile();
|
||||||
|
self.sampling_duration = None;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Err(_) => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
recv(after(timeout)) -> _ => None,
|
recv(after(timeout)) -> _ => None,
|
||||||
};
|
};
|
||||||
if let Some(msg) = received {
|
if let Some(msg) = received {
|
||||||
|
@ -285,9 +266,10 @@ impl BackgroundHangMonitorWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(duration) = self.sampling_duration {
|
if let Some(duration) = self.sampling_duration {
|
||||||
if Instant::now() - self.last_sample > duration {
|
let now = Instant::now();
|
||||||
|
if now - self.last_sample > duration {
|
||||||
self.sample();
|
self.sample();
|
||||||
self.last_sample = Instant::now();
|
self.last_sample = now;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.perform_a_hang_monitor_checkpoint();
|
self.perform_a_hang_monitor_checkpoint();
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate crossbeam_channel;
|
extern crate crossbeam_channel;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
pub mod background_hang_monitor;
|
pub mod background_hang_monitor;
|
||||||
|
|
|
@ -124,7 +124,7 @@ use keyboard_types::webdriver::Event as WebDriverInputEvent;
|
||||||
use keyboard_types::KeyboardEvent;
|
use keyboard_types::KeyboardEvent;
|
||||||
use layout_traits::LayoutThreadFactory;
|
use layout_traits::LayoutThreadFactory;
|
||||||
use log::{Level, LevelFilter, Log, Metadata, Record};
|
use log::{Level, LevelFilter, Log, Metadata, Record};
|
||||||
use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert};
|
use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert, SamplerControlMsg};
|
||||||
use msg::constellation_msg::{
|
use msg::constellation_msg::{
|
||||||
BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId,
|
BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId,
|
||||||
};
|
};
|
||||||
|
@ -208,6 +208,9 @@ pub struct Constellation<Message, LTF, STF> {
|
||||||
/// None when in multiprocess mode.
|
/// None when in multiprocess mode.
|
||||||
background_monitor_register: Option<Box<BackgroundHangMonitorRegister>>,
|
background_monitor_register: Option<Box<BackgroundHangMonitorRegister>>,
|
||||||
|
|
||||||
|
/// Channels to control all sampling profilers.
|
||||||
|
sampling_profiler_control: Vec<IpcSender<SamplerControlMsg>>,
|
||||||
|
|
||||||
/// A channel for the background hang monitor to send messages
|
/// A channel for the background hang monitor to send messages
|
||||||
/// to the constellation.
|
/// to the constellation.
|
||||||
background_hang_monitor_sender: IpcSender<HangMonitorAlert>,
|
background_hang_monitor_sender: IpcSender<HangMonitorAlert>,
|
||||||
|
@ -614,12 +617,19 @@ where
|
||||||
// If we are in multiprocess mode,
|
// If we are in multiprocess mode,
|
||||||
// a dedicated per-process hang monitor will be initialized later inside the content process.
|
// a dedicated per-process hang monitor will be initialized later inside the content process.
|
||||||
// See run_content_process in servo/lib.rs
|
// See run_content_process in servo/lib.rs
|
||||||
let background_monitor_register = if opts::multiprocess() {
|
let (background_monitor_register, sampler_chan) = if opts::multiprocess() {
|
||||||
None
|
(None, vec![])
|
||||||
} else {
|
} else {
|
||||||
Some(HangMonitorRegister::init(
|
let (sampling_profiler_control, sampling_profiler_port) =
|
||||||
background_hang_monitor_sender.clone(),
|
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) =
|
let (ipc_layout_sender, ipc_layout_receiver) =
|
||||||
|
@ -640,6 +650,7 @@ where
|
||||||
background_hang_monitor_sender,
|
background_hang_monitor_sender,
|
||||||
background_hang_monitor_receiver,
|
background_hang_monitor_receiver,
|
||||||
background_monitor_register,
|
background_monitor_register,
|
||||||
|
sampling_profiler_control: sampler_chan,
|
||||||
layout_sender: ipc_layout_sender,
|
layout_sender: ipc_layout_sender,
|
||||||
script_receiver: script_receiver,
|
script_receiver: script_receiver,
|
||||||
compositor_receiver: compositor_receiver,
|
compositor_receiver: compositor_receiver,
|
||||||
|
@ -841,6 +852,10 @@ where
|
||||||
Err(e) => return self.handle_send_error(pipeline_id, e),
|
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 {
|
if let Some(host) = host {
|
||||||
debug!(
|
debug!(
|
||||||
"Adding new host entry {} for top-level browsing context {}.",
|
"Adding new host entry {} for top-level browsing context {}.",
|
||||||
|
@ -848,11 +863,11 @@ where
|
||||||
);
|
);
|
||||||
let _ = self
|
let _ = self
|
||||||
.event_loops
|
.event_loops
|
||||||
.insert(host, Rc::downgrade(&pipeline.event_loop));
|
.insert(host, Rc::downgrade(&pipeline.pipeline.event_loop));
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(!self.pipelines.contains_key(&pipeline_id));
|
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.
|
/// Get an iterator for the fully active browsing contexts in a subtree.
|
||||||
|
@ -1202,6 +1217,20 @@ where
|
||||||
self.forward_event(destination_pipeline_id, event);
|
self.forward_event(destination_pipeline_id, event);
|
||||||
},
|
},
|
||||||
FromCompositorMsg::SetCursor(cursor) => self.handle_set_cursor_msg(cursor),
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use crate::event_loop::EventLoop;
|
use crate::event_loop::EventLoop;
|
||||||
|
use background_hang_monitor::HangMonitorRegister;
|
||||||
use bluetooth_traits::BluetoothRequest;
|
use bluetooth_traits::BluetoothRequest;
|
||||||
use canvas_traits::webgl::WebGLPipeline;
|
use canvas_traits::webgl::WebGLPipeline;
|
||||||
use compositing::compositor_thread::Msg as CompositorMsg;
|
use compositing::compositor_thread::Msg as CompositorMsg;
|
||||||
|
@ -18,7 +19,7 @@ use ipc_channel::Error;
|
||||||
use layout_traits::LayoutThreadFactory;
|
use layout_traits::LayoutThreadFactory;
|
||||||
use metrics::PaintTimeMetrics;
|
use metrics::PaintTimeMetrics;
|
||||||
use msg::constellation_msg::TopLevelBrowsingContextId;
|
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 msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId, PipelineNamespaceId};
|
||||||
use net::image_cache::ImageCacheImpl;
|
use net::image_cache::ImageCacheImpl;
|
||||||
use net_traits::image_cache::ImageCache;
|
use net_traits::image_cache::ImageCache;
|
||||||
|
@ -188,10 +189,15 @@ pub struct InitialPipelineState {
|
||||||
pub webvr_chan: Option<IpcSender<WebVRMsg>>,
|
pub webvr_chan: Option<IpcSender<WebVRMsg>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct NewPipeline {
|
||||||
|
pub pipeline: Pipeline,
|
||||||
|
pub sampler_control_chan: Option<IpcSender<SamplerControlMsg>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Pipeline {
|
impl Pipeline {
|
||||||
/// Starts a layout thread, and possibly a script thread, in
|
/// Starts a layout thread, and possibly a script thread, in
|
||||||
/// a new process if requested.
|
/// 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
|
where
|
||||||
LTF: LayoutThreadFactory<Message = Message>,
|
LTF: LayoutThreadFactory<Message = Message>,
|
||||||
STF: ScriptThreadFactory<Message = Message>,
|
STF: ScriptThreadFactory<Message = Message>,
|
||||||
|
@ -210,7 +216,7 @@ impl Pipeline {
|
||||||
|
|
||||||
let url = state.load_data.url.clone();
|
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) => {
|
Some(script_chan) => {
|
||||||
let new_layout_info = NewLayoutInfo {
|
let new_layout_info = NewLayoutInfo {
|
||||||
parent_info: state.parent_pipeline_id,
|
parent_info: state.parent_pipeline_id,
|
||||||
|
@ -229,7 +235,7 @@ impl Pipeline {
|
||||||
{
|
{
|
||||||
warn!("Sending to script during pipeline creation failed ({})", e);
|
warn!("Sending to script during pipeline creation failed ({})", e);
|
||||||
}
|
}
|
||||||
script_chan
|
(script_chan, None)
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let (script_chan, script_port) = ipc::channel().expect("Pipeline script chan");
|
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) =
|
let (script_content_process_shutdown_chan, script_content_process_shutdown_port) =
|
||||||
ipc::channel().expect("Pipeline script content process shutdown chan");
|
ipc::channel().expect("Pipeline script content process shutdown chan");
|
||||||
|
|
||||||
let unprivileged_pipeline_content = UnprivilegedPipelineContent {
|
let mut unprivileged_pipeline_content = UnprivilegedPipelineContent {
|
||||||
id: state.id,
|
id: state.id,
|
||||||
browsing_context_id: state.browsing_context_id,
|
browsing_context_id: state.browsing_context_id,
|
||||||
top_level_browsing_context_id: state.top_level_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: state
|
||||||
.background_hang_monitor_to_constellation_chan
|
.background_hang_monitor_to_constellation_chan
|
||||||
.clone(),
|
.clone(),
|
||||||
|
sampling_profiler_port: None,
|
||||||
scheduler_chan: state.scheduler_chan,
|
scheduler_chan: state.scheduler_chan,
|
||||||
devtools_chan: script_to_devtools_chan,
|
devtools_chan: script_to_devtools_chan,
|
||||||
bluetooth_thread: state.bluetooth_thread,
|
bluetooth_thread: state.bluetooth_thread,
|
||||||
|
@ -302,21 +309,25 @@ impl Pipeline {
|
||||||
// Spawn the child process.
|
// Spawn the child process.
|
||||||
//
|
//
|
||||||
// Yes, that's all there is to it!
|
// 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()?;
|
let _ = unprivileged_pipeline_content.spawn_multiprocess()?;
|
||||||
|
Some(sampler_chan)
|
||||||
} else {
|
} else {
|
||||||
// Should not be None in single-process mode.
|
// Should not be None in single-process mode.
|
||||||
let register = state
|
let register = state
|
||||||
.background_monitor_register
|
.background_monitor_register
|
||||||
.expect("Couldn't start content, no background monitor has been initiated");
|
.expect("Couldn't start content, no background monitor has been initiated");
|
||||||
unprivileged_pipeline_content.start_all::<Message, LTF, STF>(false, register);
|
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.id,
|
||||||
state.browsing_context_id,
|
state.browsing_context_id,
|
||||||
state.top_level_browsing_context_id,
|
state.top_level_browsing_context_id,
|
||||||
|
@ -327,7 +338,11 @@ impl Pipeline {
|
||||||
url,
|
url,
|
||||||
state.prev_visibility,
|
state.prev_visibility,
|
||||||
state.load_data,
|
state.load_data,
|
||||||
))
|
);
|
||||||
|
Ok(NewPipeline {
|
||||||
|
pipeline,
|
||||||
|
sampler_control_chan: sampler_chan,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `Pipeline`, after the script and layout threads have been
|
/// Creates a new `Pipeline`, after the script and layout threads have been
|
||||||
|
@ -468,6 +483,7 @@ pub struct UnprivilegedPipelineContent {
|
||||||
opener: Option<BrowsingContextId>,
|
opener: Option<BrowsingContextId>,
|
||||||
script_to_constellation_chan: ScriptToConstellationChan,
|
script_to_constellation_chan: ScriptToConstellationChan,
|
||||||
background_hang_monitor_to_constellation_chan: IpcSender<HangMonitorAlert>,
|
background_hang_monitor_to_constellation_chan: IpcSender<HangMonitorAlert>,
|
||||||
|
sampling_profiler_port: Option<IpcReceiver<SamplerControlMsg>>,
|
||||||
layout_to_constellation_chan: IpcSender<LayoutMsg>,
|
layout_to_constellation_chan: IpcSender<LayoutMsg>,
|
||||||
scheduler_chan: IpcSender<TimerSchedulerMsg>,
|
scheduler_chan: IpcSender<TimerSchedulerMsg>,
|
||||||
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
||||||
|
@ -669,8 +685,13 @@ impl UnprivilegedPipelineContent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn background_hang_monitor_to_constellation_chan(&self) -> &IpcSender<HangMonitorAlert> {
|
pub fn register_with_background_hang_monitor(&mut self) -> Box<BackgroundHangMonitorRegister> {
|
||||||
&self.background_hang_monitor_to_constellation_chan
|
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 {
|
pub fn script_to_constellation_chan(&self) -> &ScriptToConstellationChan {
|
||||||
|
|
|
@ -490,3 +490,10 @@ pub trait BackgroundHangMonitor {
|
||||||
/// Unregister the component from monitor.
|
/// Unregister the component from monitor.
|
||||||
fn unregister(&self);
|
fn unregister(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Messages to control the sampling profiler.
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub enum SamplerControlMsg {
|
||||||
|
Enable(Duration),
|
||||||
|
Disable,
|
||||||
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ use servo_url::ServoUrl;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
use style_traits::SpeculativePainter;
|
use style_traits::SpeculativePainter;
|
||||||
use webrender_api::{
|
use webrender_api::{
|
||||||
|
@ -777,6 +778,10 @@ pub enum ConstellationMsg {
|
||||||
ForwardEvent(PipelineId, CompositorEvent),
|
ForwardEvent(PipelineId, CompositorEvent),
|
||||||
/// Requesting a change to the onscreen cursor.
|
/// Requesting a change to the onscreen cursor.
|
||||||
SetCursor(Cursor),
|
SetCursor(Cursor),
|
||||||
|
/// Enable the sampling profiler.
|
||||||
|
EnableProfiler(Duration),
|
||||||
|
/// Disable the sampling profiler.
|
||||||
|
DisableProfiler,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for ConstellationMsg {
|
impl fmt::Debug for ConstellationMsg {
|
||||||
|
@ -804,6 +809,8 @@ impl fmt::Debug for ConstellationMsg {
|
||||||
SelectBrowser(..) => "SelectBrowser",
|
SelectBrowser(..) => "SelectBrowser",
|
||||||
ForwardEvent(..) => "ForwardEvent",
|
ForwardEvent(..) => "ForwardEvent",
|
||||||
SetCursor(..) => "SetCursor",
|
SetCursor(..) => "SetCursor",
|
||||||
|
EnableProfiler(..) => "EnableProfiler",
|
||||||
|
DisableProfiler => "DisableProfiler",
|
||||||
};
|
};
|
||||||
write!(formatter, "ConstellationMsg::{}", variant)
|
write!(formatter, "ConstellationMsg::{}", variant)
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,6 @@ fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
|
||||||
#[cfg(not(feature = "webdriver"))]
|
#[cfg(not(feature = "webdriver"))]
|
||||||
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) {}
|
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) {}
|
||||||
|
|
||||||
use background_hang_monitor::HangMonitorRegister;
|
|
||||||
use bluetooth::BluetoothThreadFactory;
|
use bluetooth::BluetoothThreadFactory;
|
||||||
use bluetooth_traits::BluetoothRequest;
|
use bluetooth_traits::BluetoothRequest;
|
||||||
use canvas::gl_context::GLContextFactory;
|
use canvas::gl_context::GLContextFactory;
|
||||||
|
@ -133,6 +132,7 @@ pub struct Servo<Window: WindowMethods + 'static> {
|
||||||
constellation_chan: Sender<ConstellationMsg>,
|
constellation_chan: Sender<ConstellationMsg>,
|
||||||
embedder_receiver: EmbedderReceiver,
|
embedder_receiver: EmbedderReceiver,
|
||||||
embedder_events: Vec<(Option<BrowserId>, EmbedderMsg)>,
|
embedder_events: Vec<(Option<BrowserId>, EmbedderMsg)>,
|
||||||
|
profiler_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -318,6 +318,7 @@ where
|
||||||
constellation_chan: constellation_chan,
|
constellation_chan: constellation_chan,
|
||||||
embedder_receiver: embedder_receiver,
|
embedder_receiver: embedder_receiver,
|
||||||
embedder_events: Vec::new(),
|
embedder_events: Vec::new(),
|
||||||
|
profiler_enabled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,7 +408,15 @@ where
|
||||||
},
|
},
|
||||||
|
|
||||||
WindowEvent::ToggleSamplingProfiler(rate) => {
|
WindowEvent::ToggleSamplingProfiler(rate) => {
|
||||||
HangMonitorRegister::toggle(rate);
|
self.profiler_enabled = !self.profiler_enabled;
|
||||||
|
let msg = if self.profiler_enabled {
|
||||||
|
ConstellationMsg::EnableProfiler(rate)
|
||||||
|
} else {
|
||||||
|
ConstellationMsg::DisableProfiler
|
||||||
|
};
|
||||||
|
if let Err(e) = self.constellation_chan.send(msg) {
|
||||||
|
warn!("Sending profiler toggle to constellation failed ({:?}).", e);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
WindowEvent::ToggleWebRenderDebug(option) => {
|
WindowEvent::ToggleWebRenderDebug(option) => {
|
||||||
|
@ -714,7 +723,7 @@ pub fn run_content_process(token: String) {
|
||||||
.send(unprivileged_content_sender)
|
.send(unprivileged_content_sender)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
|
let mut unprivileged_content = unprivileged_content_receiver.recv().unwrap();
|
||||||
opts::set_options(unprivileged_content.opts());
|
opts::set_options(unprivileged_content.opts());
|
||||||
PREFS.extend(unprivileged_content.prefs());
|
PREFS.extend(unprivileged_content.prefs());
|
||||||
set_logger(unprivileged_content.script_to_constellation_chan().clone());
|
set_logger(unprivileged_content.script_to_constellation_chan().clone());
|
||||||
|
@ -724,11 +733,8 @@ pub fn run_content_process(token: String) {
|
||||||
create_sandbox();
|
create_sandbox();
|
||||||
}
|
}
|
||||||
|
|
||||||
let background_hang_monitor_register = HangMonitorRegister::init(
|
let background_hang_monitor_register =
|
||||||
unprivileged_content
|
unprivileged_content.register_with_background_hang_monitor();
|
||||||
.background_hang_monitor_to_constellation_chan()
|
|
||||||
.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// send the required channels to the service worker manager
|
// send the required channels to the service worker manager
|
||||||
let sw_senders = unprivileged_content.swmanager_senders();
|
let sw_senders = unprivileged_content.swmanager_senders();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue