mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
introduce a background-hang-monitor:
Mac-Os implementation of a thread sampler, Linux and Windows skeleton implementations.
This commit is contained in:
parent
7c65505df3
commit
4eb785cdc0
23 changed files with 1134 additions and 11 deletions
27
Cargo.lock
generated
27
Cargo.lock
generated
|
@ -135,6 +135,22 @@ dependencies = [
|
|||
"servo-skia 0.30000020.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "background_hang_monitor"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"backtrace 0.3.9 (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)",
|
||||
"ipc-channel 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.42 (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)",
|
||||
"msg 0.0.1",
|
||||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.9"
|
||||
|
@ -561,6 +577,7 @@ dependencies = [
|
|||
name = "constellation"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"background_hang_monitor 0.0.1",
|
||||
"backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bluetooth_traits 0.0.1",
|
||||
"canvas 0.0.1",
|
||||
|
@ -2095,6 +2112,7 @@ dependencies = [
|
|||
name = "libservo"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"background_hang_monitor 0.0.1",
|
||||
"bluetooth 0.0.1",
|
||||
"bluetooth_traits 0.0.1",
|
||||
"canvas 0.0.1",
|
||||
|
@ -2217,6 +2235,14 @@ name = "mac"
|
|||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
|
@ -4976,6 +5002,7 @@ dependencies = [
|
|||
"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f"
|
||||
"checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084"
|
||||
"checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||
"checksum mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1"
|
||||
"checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
|
||||
"checksum markup5ever 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c220b3a3d75543b76e5c1fcab6635a8430ab5f9bfa011d003c3787ae0abf4ffa"
|
||||
"checksum matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efd7622e3022e1a6eaa602c4cea8912254e5582c9c692e9167714182244801b1"
|
||||
|
|
27
components/background_hang_monitor/Cargo.toml
Normal file
27
components/background_hang_monitor/Cargo.toml
Normal file
|
@ -0,0 +1,27 @@
|
|||
[package]
|
||||
name = "background_hang_monitor"
|
||||
version = "0.0.1"
|
||||
authors = ["The Servo Project Developers"]
|
||||
license = "MPL-2.0"
|
||||
publish = false
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "background_hang_monitor"
|
||||
path = "lib.rs"
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
backtrace = "0.3"
|
||||
bitflags = "1.0"
|
||||
ipc-channel = "0.11"
|
||||
lazy_static = "1"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
msg = {path = "../msg"}
|
||||
serde = "1.0.60"
|
||||
crossbeam-channel = "0.3"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
mach = "0.2.3"
|
260
components/background_hang_monitor/background_hang_monitor.rs
Normal file
260
components/background_hang_monitor/background_hang_monitor.rs
Normal file
|
@ -0,0 +1,260 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::sampler::Sampler;
|
||||
use crossbeam_channel::{after, unbounded, Receiver, Sender};
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use msg::constellation_msg::MonitoredComponentId;
|
||||
use msg::constellation_msg::{
|
||||
BackgroundHangMonitor, BackgroundHangMonitorClone, BackgroundHangMonitorRegister,
|
||||
};
|
||||
use msg::constellation_msg::{HangAlert, HangAnnotation};
|
||||
use std::cell::Cell;
|
||||
use std::collections::HashMap;
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HangMonitorRegister {
|
||||
sender: Sender<(MonitoredComponentId, MonitoredComponentMsg)>,
|
||||
}
|
||||
|
||||
impl HangMonitorRegister {
|
||||
/// Start a new hang monitor worker, and return a handle to register components for monitoring.
|
||||
pub fn init(constellation_chan: IpcSender<HangAlert>) -> Box<BackgroundHangMonitorRegister> {
|
||||
let (sender, port) = unbounded();
|
||||
let _ = thread::Builder::new().spawn(move || {
|
||||
let mut monitor = { BackgroundHangMonitorWorker::new(constellation_chan, port) };
|
||||
while monitor.run() {
|
||||
// Monitoring until all senders have been dropped...
|
||||
}
|
||||
});
|
||||
Box::new(HangMonitorRegister { sender })
|
||||
}
|
||||
}
|
||||
|
||||
impl BackgroundHangMonitorRegister for HangMonitorRegister {
|
||||
/// Register a component for monitoring.
|
||||
/// Returns a dedicated wrapper around a sender
|
||||
/// to be used for communication with the hang monitor worker.
|
||||
fn register_component(
|
||||
&self,
|
||||
component_id: MonitoredComponentId,
|
||||
transient_hang_timeout: Duration,
|
||||
permanent_hang_timeout: Duration,
|
||||
) -> Box<BackgroundHangMonitor> {
|
||||
let bhm_chan = BackgroundHangMonitorChan::new(self.sender.clone(), component_id);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let sampler = crate::sampler_windows::WindowsSampler::new();
|
||||
#[cfg(target_os = "macos")]
|
||||
let sampler = crate::sampler_mac::MacOsSampler::new();
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
let sampler = crate::sampler_linux::LinuxSampler::new();
|
||||
|
||||
bhm_chan.send(MonitoredComponentMsg::Register(
|
||||
sampler,
|
||||
transient_hang_timeout,
|
||||
permanent_hang_timeout,
|
||||
));
|
||||
Box::new(bhm_chan)
|
||||
}
|
||||
}
|
||||
|
||||
impl BackgroundHangMonitorClone for HangMonitorRegister {
|
||||
fn clone_box(&self) -> Box<BackgroundHangMonitorRegister> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Messages sent from monitored components to the monitor.
|
||||
pub enum MonitoredComponentMsg {
|
||||
/// Register component for monitoring,
|
||||
Register(Box<Sampler>, Duration, Duration),
|
||||
/// Notify start of new activity for a given component,
|
||||
NotifyActivity(HangAnnotation),
|
||||
/// Notify start of waiting for a new task to come-in for processing.
|
||||
NotifyWait,
|
||||
}
|
||||
|
||||
/// A wrapper around a sender to the monitor,
|
||||
/// which will send the Id of the monitored component along with each message,
|
||||
/// and keep track of whether the monitor is still listening on the other end.
|
||||
pub struct BackgroundHangMonitorChan {
|
||||
sender: Sender<(MonitoredComponentId, MonitoredComponentMsg)>,
|
||||
component_id: MonitoredComponentId,
|
||||
disconnected: Cell<bool>,
|
||||
}
|
||||
|
||||
impl BackgroundHangMonitorChan {
|
||||
pub fn new(
|
||||
sender: Sender<(MonitoredComponentId, MonitoredComponentMsg)>,
|
||||
component_id: MonitoredComponentId,
|
||||
) -> Self {
|
||||
BackgroundHangMonitorChan {
|
||||
sender,
|
||||
component_id: component_id,
|
||||
disconnected: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(&self, msg: MonitoredComponentMsg) {
|
||||
if self.disconnected.get() {
|
||||
return;
|
||||
}
|
||||
if let Err(_) = self.sender.send((self.component_id.clone(), msg)) {
|
||||
warn!("BackgroundHangMonitor has gone away");
|
||||
self.disconnected.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BackgroundHangMonitor for BackgroundHangMonitorChan {
|
||||
fn notify_activity(&self, annotation: HangAnnotation) {
|
||||
let msg = MonitoredComponentMsg::NotifyActivity(annotation);
|
||||
self.send(msg);
|
||||
}
|
||||
fn notify_wait(&self) {
|
||||
let msg = MonitoredComponentMsg::NotifyWait;
|
||||
self.send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
struct MonitoredComponent {
|
||||
sampler: Box<Sampler>,
|
||||
last_activity: Instant,
|
||||
last_annotation: Option<HangAnnotation>,
|
||||
transient_hang_timeout: Duration,
|
||||
permanent_hang_timeout: Duration,
|
||||
sent_transient_alert: bool,
|
||||
sent_permanent_alert: bool,
|
||||
is_waiting: bool,
|
||||
}
|
||||
|
||||
pub struct BackgroundHangMonitorWorker {
|
||||
monitored_components: HashMap<MonitoredComponentId, MonitoredComponent>,
|
||||
constellation_chan: IpcSender<HangAlert>,
|
||||
port: Receiver<(MonitoredComponentId, MonitoredComponentMsg)>,
|
||||
}
|
||||
|
||||
impl BackgroundHangMonitorWorker {
|
||||
pub fn new(
|
||||
constellation_chan: IpcSender<HangAlert>,
|
||||
port: Receiver<(MonitoredComponentId, MonitoredComponentMsg)>,
|
||||
) -> Self {
|
||||
Self {
|
||||
monitored_components: Default::default(),
|
||||
constellation_chan,
|
||||
port,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> bool {
|
||||
let received = select! {
|
||||
recv(self.port) -> event => {
|
||||
match event {
|
||||
Ok(msg) => Some(msg),
|
||||
// Our sender has been dropped, quit.
|
||||
Err(_) => return false,
|
||||
}
|
||||
},
|
||||
recv(after(Duration::from_millis(100))) -> _ => None,
|
||||
};
|
||||
if let Some(msg) = received {
|
||||
self.handle_msg(msg);
|
||||
while let Ok(another_msg) = self.port.try_recv() {
|
||||
// Handle any other incoming messages,
|
||||
// before performing a hang checkpoint.
|
||||
self.handle_msg(another_msg);
|
||||
}
|
||||
}
|
||||
self.perform_a_hang_monitor_checkpoint();
|
||||
true
|
||||
}
|
||||
|
||||
fn handle_msg(&mut self, msg: (MonitoredComponentId, MonitoredComponentMsg)) {
|
||||
match msg {
|
||||
(
|
||||
component_id,
|
||||
MonitoredComponentMsg::Register(
|
||||
sampler,
|
||||
transient_hang_timeout,
|
||||
permanent_hang_timeout,
|
||||
),
|
||||
) => {
|
||||
let component = MonitoredComponent {
|
||||
sampler,
|
||||
last_activity: Instant::now(),
|
||||
last_annotation: None,
|
||||
transient_hang_timeout,
|
||||
permanent_hang_timeout,
|
||||
sent_transient_alert: false,
|
||||
sent_permanent_alert: false,
|
||||
is_waiting: true,
|
||||
};
|
||||
assert!(
|
||||
self.monitored_components
|
||||
.insert(component_id, component)
|
||||
.is_none(),
|
||||
"This component was already registered for monitoring."
|
||||
);
|
||||
},
|
||||
(component_id, MonitoredComponentMsg::NotifyActivity(annotation)) => {
|
||||
let component = self
|
||||
.monitored_components
|
||||
.get_mut(&component_id)
|
||||
.expect("Received NotifyActivity for an unknown component");
|
||||
component.last_activity = Instant::now();
|
||||
component.last_annotation = Some(annotation);
|
||||
component.sent_transient_alert = false;
|
||||
component.sent_permanent_alert = false;
|
||||
component.is_waiting = false;
|
||||
},
|
||||
(component_id, MonitoredComponentMsg::NotifyWait) => {
|
||||
let component = self
|
||||
.monitored_components
|
||||
.get_mut(&component_id)
|
||||
.expect("Received NotifyWait for an unknown component");
|
||||
component.last_activity = Instant::now();
|
||||
component.sent_transient_alert = false;
|
||||
component.sent_permanent_alert = false;
|
||||
component.is_waiting = true;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn perform_a_hang_monitor_checkpoint(&mut self) {
|
||||
for (component_id, monitored) in self.monitored_components.iter_mut() {
|
||||
if monitored.is_waiting {
|
||||
continue;
|
||||
}
|
||||
let last_annotation = monitored.last_annotation.unwrap();
|
||||
if monitored.last_activity.elapsed() > monitored.permanent_hang_timeout {
|
||||
if monitored.sent_permanent_alert {
|
||||
continue;
|
||||
}
|
||||
let profile = match monitored.sampler.suspend_and_sample_thread() {
|
||||
Ok(native_stack) => Some(native_stack.to_hangprofile()),
|
||||
Err(()) => None,
|
||||
};
|
||||
let _ = self.constellation_chan.send(HangAlert::Permanent(
|
||||
component_id.clone(),
|
||||
last_annotation,
|
||||
profile,
|
||||
));
|
||||
monitored.sent_permanent_alert = true;
|
||||
continue;
|
||||
}
|
||||
if monitored.last_activity.elapsed() > monitored.transient_hang_timeout {
|
||||
if monitored.sent_transient_alert {
|
||||
continue;
|
||||
}
|
||||
let _ = self
|
||||
.constellation_chan
|
||||
.send(HangAlert::Transient(component_id.clone(), last_annotation));
|
||||
monitored.sent_transient_alert = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
components/background_hang_monitor/lib.rs
Normal file
21
components/background_hang_monitor/lib.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate crossbeam_channel;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod background_hang_monitor;
|
||||
mod sampler;
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
mod sampler_linux;
|
||||
#[cfg(target_os = "macos")]
|
||||
mod sampler_mac;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod sampler_windows;
|
||||
|
||||
pub use self::background_hang_monitor::*;
|
83
components/background_hang_monitor/sampler.rs
Normal file
83
components/background_hang_monitor/sampler.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use backtrace;
|
||||
use msg::constellation_msg::{HangProfile, HangProfileSymbol};
|
||||
use std::ptr;
|
||||
|
||||
const MAX_NATIVE_FRAMES: usize = 1024;
|
||||
|
||||
pub trait Sampler: Send {
|
||||
fn suspend_and_sample_thread(&self) -> Result<NativeStack, ()>;
|
||||
}
|
||||
|
||||
// Several types in this file are currently not used in a Linux or Windows build.
|
||||
#[allow(dead_code)]
|
||||
pub type Address = *const libc::uint8_t;
|
||||
|
||||
/// The registers used for stack unwinding
|
||||
#[allow(dead_code)]
|
||||
pub struct Registers {
|
||||
/// Instruction pointer.
|
||||
pub instruction_ptr: Address,
|
||||
/// Stack pointer.
|
||||
pub stack_ptr: Address,
|
||||
/// Frame pointer.
|
||||
pub frame_ptr: Address,
|
||||
}
|
||||
|
||||
pub struct NativeStack {
|
||||
instruction_ptrs: [*mut std::ffi::c_void; MAX_NATIVE_FRAMES],
|
||||
stack_ptrs: [*mut std::ffi::c_void; MAX_NATIVE_FRAMES],
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl NativeStack {
|
||||
pub fn new() -> Self {
|
||||
NativeStack {
|
||||
instruction_ptrs: [ptr::null_mut(); MAX_NATIVE_FRAMES],
|
||||
stack_ptrs: [ptr::null_mut(); MAX_NATIVE_FRAMES],
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_register(
|
||||
&mut self,
|
||||
instruction_ptr: *mut std::ffi::c_void,
|
||||
stack_ptr: *mut std::ffi::c_void,
|
||||
) -> Result<(), ()> {
|
||||
if !(self.count < MAX_NATIVE_FRAMES) {
|
||||
return Err(());
|
||||
}
|
||||
self.instruction_ptrs[self.count] = instruction_ptr;
|
||||
self.stack_ptrs[self.count] = stack_ptr;
|
||||
self.count = self.count + 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn to_hangprofile(&self) -> HangProfile {
|
||||
let mut profile = HangProfile {
|
||||
backtrace: Vec::new(),
|
||||
};
|
||||
for ip in self.instruction_ptrs.iter().rev() {
|
||||
if ip.is_null() {
|
||||
continue;
|
||||
}
|
||||
backtrace::resolve(*ip, |symbol| {
|
||||
// TODO: use the demangled or C++ demangled symbols if available.
|
||||
let name = symbol
|
||||
.name()
|
||||
.map(|n| String::from_utf8_lossy(&n.as_bytes()).to_string());
|
||||
let filename = symbol.filename().map(|n| n.to_string_lossy().to_string());
|
||||
let lineno = symbol.lineno();
|
||||
profile.backtrace.push(HangProfileSymbol {
|
||||
name,
|
||||
filename,
|
||||
lineno,
|
||||
});
|
||||
});
|
||||
}
|
||||
profile
|
||||
}
|
||||
}
|
35
components/background_hang_monitor/sampler_linux.rs
Normal file
35
components/background_hang_monitor/sampler_linux.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::sampler::{NativeStack, Sampler};
|
||||
use libc;
|
||||
|
||||
type MonitoredThreadId = libc::pthread_t;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct LinuxSampler {
|
||||
thread_id: MonitoredThreadId,
|
||||
}
|
||||
|
||||
impl LinuxSampler {
|
||||
#[allow(unsafe_code, dead_code)]
|
||||
pub fn new() -> Box<Sampler> {
|
||||
let thread_id = unsafe { libc::pthread_self() };
|
||||
Box::new(LinuxSampler { thread_id })
|
||||
}
|
||||
}
|
||||
|
||||
impl Sampler for LinuxSampler {
|
||||
#[allow(unsafe_code)]
|
||||
fn suspend_and_sample_thread(&self) -> Result<NativeStack, ()> {
|
||||
// Warning: The "critical section" begins here.
|
||||
// In the critical section:
|
||||
// we must not do any dynamic memory allocation,
|
||||
// nor try to acquire any lock
|
||||
// or any other unshareable resource.
|
||||
|
||||
// NOTE: End of "critical section".
|
||||
Err(())
|
||||
}
|
||||
}
|
118
components/background_hang_monitor/sampler_mac.rs
Normal file
118
components/background_hang_monitor/sampler_mac.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::sampler::{Address, NativeStack, Registers, Sampler};
|
||||
use libc;
|
||||
use mach;
|
||||
use std::panic;
|
||||
use std::process;
|
||||
|
||||
type MonitoredThreadId = mach::mach_types::thread_act_t;
|
||||
|
||||
pub struct MacOsSampler {
|
||||
thread_id: MonitoredThreadId,
|
||||
}
|
||||
|
||||
impl MacOsSampler {
|
||||
#[allow(unsafe_code)]
|
||||
pub fn new() -> Box<Sampler> {
|
||||
let thread_id = unsafe { mach::mach_init::mach_thread_self() };
|
||||
Box::new(MacOsSampler { thread_id })
|
||||
}
|
||||
}
|
||||
|
||||
impl Sampler for MacOsSampler {
|
||||
#[allow(unsafe_code)]
|
||||
fn suspend_and_sample_thread(&self) -> Result<NativeStack, ()> {
|
||||
// Warning: The "critical section" begins here.
|
||||
// In the critical section:
|
||||
// we must not do any dynamic memory allocation,
|
||||
// nor try to acquire any lock
|
||||
// or any other unshareable resource.
|
||||
let current_hook = panic::take_hook();
|
||||
panic::set_hook(Box::new(|_| {
|
||||
// Avoiding any allocation or locking as part of standard panicking.
|
||||
process::abort();
|
||||
}));
|
||||
let native_stack = unsafe {
|
||||
if let Err(()) = suspend_thread(self.thread_id) {
|
||||
panic::set_hook(current_hook);
|
||||
return Err(());
|
||||
};
|
||||
let native_stack = match get_registers(self.thread_id) {
|
||||
Ok(regs) => Ok(frame_pointer_stack_walk(regs)),
|
||||
Err(()) => Err(()),
|
||||
};
|
||||
if let Err(()) = resume_thread(self.thread_id) {
|
||||
process::abort();
|
||||
}
|
||||
native_stack
|
||||
};
|
||||
panic::set_hook(current_hook);
|
||||
// NOTE: End of "critical section".
|
||||
native_stack
|
||||
}
|
||||
}
|
||||
|
||||
fn check_kern_return(kret: mach::kern_return::kern_return_t) -> Result<(), ()> {
|
||||
if kret != mach::kern_return::KERN_SUCCESS {
|
||||
return Err(());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn suspend_thread(thread_id: MonitoredThreadId) -> Result<(), ()> {
|
||||
check_kern_return(mach::thread_act::thread_suspend(thread_id))
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn get_registers(thread_id: MonitoredThreadId) -> Result<Registers, ()> {
|
||||
let mut state = mach::structs::x86_thread_state64_t::new();
|
||||
let mut state_count = mach::structs::x86_thread_state64_t::count();
|
||||
let kret = mach::thread_act::thread_get_state(
|
||||
thread_id,
|
||||
mach::thread_status::x86_THREAD_STATE64,
|
||||
(&mut state) as *mut _ as *mut _,
|
||||
&mut state_count,
|
||||
);
|
||||
check_kern_return(kret)?;
|
||||
Ok(Registers {
|
||||
instruction_ptr: state.__rip as Address,
|
||||
stack_ptr: state.__rsp as Address,
|
||||
frame_ptr: state.__rbp as Address,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn resume_thread(thread_id: MonitoredThreadId) -> Result<(), ()> {
|
||||
check_kern_return(mach::thread_act::thread_resume(thread_id))
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn frame_pointer_stack_walk(regs: Registers) -> NativeStack {
|
||||
// Note: this function will only work with code build with:
|
||||
// --dev,
|
||||
// or --with-frame-pointer.
|
||||
|
||||
let stackaddr = libc::pthread_get_stackaddr_np(libc::pthread_self());
|
||||
let mut native_stack = NativeStack::new();
|
||||
let pc = regs.instruction_ptr as *mut std::ffi::c_void;
|
||||
let stack = regs.stack_ptr as *mut std::ffi::c_void;
|
||||
let _ = native_stack.process_register(pc, stack);
|
||||
let mut current = regs.frame_ptr as *mut *mut std::ffi::c_void;
|
||||
while !current.is_null() {
|
||||
if (current as usize) < stackaddr as usize {
|
||||
break;
|
||||
}
|
||||
let next = *current as *mut *mut std::ffi::c_void;
|
||||
let pc = current.add(1);
|
||||
let stack = current.add(2);
|
||||
if let Err(()) = native_stack.process_register(*pc, *stack) {
|
||||
break;
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
native_stack
|
||||
}
|
40
components/background_hang_monitor/sampler_windows.rs
Normal file
40
components/background_hang_monitor/sampler_windows.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::sampler::{NativeStack, Sampler};
|
||||
|
||||
type MonitoredThreadId = usize; // TODO: use winapi
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct WindowsSampler {
|
||||
thread_id: MonitoredThreadId,
|
||||
}
|
||||
|
||||
impl WindowsSampler {
|
||||
#[allow(unsafe_code, dead_code)]
|
||||
pub fn new() -> Box<Sampler> {
|
||||
let thread_id = 0; // TODO: use winapi::um::processthreadsapi::GetThreadId
|
||||
Box::new(WindowsSampler { thread_id })
|
||||
}
|
||||
}
|
||||
|
||||
impl Sampler for WindowsSampler {
|
||||
fn suspend_and_sample_thread(&self) -> Result<NativeStack, ()> {
|
||||
// Warning: The "critical section" begins here.
|
||||
// In the critical section:
|
||||
// we must not do any dynamic memory allocation,
|
||||
// nor try to acquire any lock
|
||||
// or any other unshareable resource.
|
||||
|
||||
// TODO:
|
||||
// 1: use winapi::um::processthreadsapi::SuspendThread
|
||||
// 2: use winapi::um::processthreadsapi::GetThreadContext
|
||||
// 3: populate registers using the context, see
|
||||
// https://dxr.mozilla.org/mozilla-central/source/tools/profiler/core/platform-win32.cpp#129
|
||||
// 4: use winapi::um::processthreadsapi::ResumeThread
|
||||
|
||||
// NOTE: End of "critical section".
|
||||
Err(())
|
||||
}
|
||||
}
|
107
components/background_hang_monitor/tests/hang_monitor_tests.rs
Normal file
107
components/background_hang_monitor/tests/hang_monitor_tests.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use background_hang_monitor::HangMonitorRegister;
|
||||
use ipc_channel::ipc;
|
||||
use msg::constellation_msg::ScriptHangAnnotation;
|
||||
use msg::constellation_msg::TEST_PIPELINE_ID;
|
||||
use msg::constellation_msg::{HangAlert, HangAnnotation};
|
||||
use msg::constellation_msg::{MonitoredComponentId, MonitoredComponentType};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn test_hang_monitoring() {
|
||||
let (background_hang_monitor_ipc_sender, background_hang_monitor_receiver) =
|
||||
ipc::channel().expect("ipc channel failure");
|
||||
|
||||
let background_hang_monitor_register =
|
||||
HangMonitorRegister::init(background_hang_monitor_ipc_sender.clone());
|
||||
let background_hang_monitor = background_hang_monitor_register.register_component(
|
||||
MonitoredComponentId(TEST_PIPELINE_ID, MonitoredComponentType::Script),
|
||||
Duration::from_millis(10),
|
||||
Duration::from_millis(1000),
|
||||
);
|
||||
|
||||
// Start an activity.
|
||||
let hang_annotation = HangAnnotation::Script(ScriptHangAnnotation::AttachLayout);
|
||||
background_hang_monitor.notify_activity(hang_annotation);
|
||||
|
||||
// Sleep until the "transient" timeout has been reached.
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
|
||||
// Check for a transient hang alert.
|
||||
match background_hang_monitor_receiver.recv().unwrap() {
|
||||
HangAlert::Transient(component_id, _annotation) => {
|
||||
let expected = MonitoredComponentId(TEST_PIPELINE_ID, MonitoredComponentType::Script);
|
||||
assert_eq!(expected, component_id);
|
||||
},
|
||||
HangAlert::Permanent(..) => unreachable!(),
|
||||
}
|
||||
|
||||
// Sleep until the "permanent" timeout has been reached.
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
|
||||
// Check for a permanent hang alert.
|
||||
match background_hang_monitor_receiver.recv().unwrap() {
|
||||
HangAlert::Permanent(component_id, _annotation, _profile) => {
|
||||
let expected = MonitoredComponentId(TEST_PIPELINE_ID, MonitoredComponentType::Script);
|
||||
assert_eq!(expected, component_id);
|
||||
},
|
||||
HangAlert::Transient(..) => unreachable!(),
|
||||
}
|
||||
|
||||
// Now the component is not hanging anymore.
|
||||
background_hang_monitor.notify_activity(hang_annotation);
|
||||
assert!(background_hang_monitor_receiver.try_recv().is_err());
|
||||
|
||||
// Sleep for a while.
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
|
||||
// Check for a transient hang alert.
|
||||
match background_hang_monitor_receiver.recv().unwrap() {
|
||||
HangAlert::Transient(component_id, _annotation) => {
|
||||
let expected = MonitoredComponentId(TEST_PIPELINE_ID, MonitoredComponentType::Script);
|
||||
assert_eq!(expected, component_id);
|
||||
},
|
||||
HangAlert::Permanent(..) => unreachable!(),
|
||||
}
|
||||
|
||||
// Now the component is waiting for a new task.
|
||||
background_hang_monitor.notify_wait();
|
||||
|
||||
// Sleep for a while.
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
|
||||
// The component is still waiting, but not hanging.
|
||||
assert!(background_hang_monitor_receiver.try_recv().is_err());
|
||||
|
||||
// New task handling starts.
|
||||
background_hang_monitor.notify_activity(hang_annotation);
|
||||
|
||||
// Sleep for a while.
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
|
||||
// We're getting new hang alerts for the latest task.
|
||||
match background_hang_monitor_receiver.recv().unwrap() {
|
||||
HangAlert::Transient(component_id, _annotation) => {
|
||||
let expected = MonitoredComponentId(TEST_PIPELINE_ID, MonitoredComponentType::Script);
|
||||
assert_eq!(expected, component_id);
|
||||
},
|
||||
HangAlert::Permanent(..) => unreachable!(),
|
||||
}
|
||||
|
||||
// No new alert yet
|
||||
assert!(background_hang_monitor_receiver.try_recv().is_err());
|
||||
|
||||
// Shut-down the hang monitor
|
||||
drop(background_hang_monitor_register);
|
||||
drop(background_hang_monitor);
|
||||
|
||||
// Sleep until the "max-timeout" has been reached.
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
|
||||
// Still no new alerts because the hang monitor has shut-down already.
|
||||
assert!(background_hang_monitor_receiver.try_recv().is_err());
|
||||
}
|
|
@ -11,6 +11,7 @@ name = "constellation"
|
|||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
background_hang_monitor = { path = "../background_hang_monitor"}
|
||||
backtrace = "0.3"
|
||||
bluetooth_traits = { path = "../bluetooth_traits" }
|
||||
canvas = {path = "../canvas"}
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
//!
|
||||
//! See https://github.com/servo/servo/issues/14704
|
||||
|
||||
use background_hang_monitor::HangMonitorRegister;
|
||||
use backtrace::Backtrace;
|
||||
use bluetooth_traits::BluetoothRequest;
|
||||
use canvas::canvas_paint_thread::CanvasPaintThread;
|
||||
|
@ -123,6 +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::{
|
||||
BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId,
|
||||
};
|
||||
|
@ -200,6 +202,18 @@ pub struct Constellation<Message, LTF, STF> {
|
|||
/// This is the constellation's view of `script_sender`.
|
||||
script_receiver: Receiver<Result<(PipelineId, FromScriptMsg), IpcError>>,
|
||||
|
||||
/// A handle to register components for hang monitoring.
|
||||
/// None when in multiprocess mode.
|
||||
background_monitor_register: Option<Box<BackgroundHangMonitorRegister>>,
|
||||
|
||||
/// A channel for the background hang monitor to send messages
|
||||
/// to the constellation.
|
||||
background_hang_monitor_sender: IpcSender<HangAlert>,
|
||||
|
||||
/// A channel for the constellation to receiver messages
|
||||
/// from the background hang monitor.
|
||||
background_hang_monitor_receiver: Receiver<Result<HangAlert, IpcError>>,
|
||||
|
||||
/// An IPC channel for layout threads to send messages to the constellation.
|
||||
/// This is the layout threads' view of `layout_receiver`.
|
||||
layout_sender: IpcSender<FromLayoutMsg>,
|
||||
|
@ -587,6 +601,21 @@ where
|
|||
let script_receiver =
|
||||
route_ipc_receiver_to_new_mpsc_receiver_preserving_errors(ipc_script_receiver);
|
||||
|
||||
let (background_hang_monitor_sender, ipc_bhm_receiver) =
|
||||
ipc::channel().expect("ipc channel failure");
|
||||
let background_hang_monitor_receiver =
|
||||
route_ipc_receiver_to_new_mpsc_receiver_preserving_errors(ipc_bhm_receiver);
|
||||
|
||||
// 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 = match opts::multiprocess() {
|
||||
true => None,
|
||||
false => Some(HangMonitorRegister::init(
|
||||
background_hang_monitor_sender.clone(),
|
||||
)),
|
||||
};
|
||||
|
||||
let (ipc_layout_sender, ipc_layout_receiver) =
|
||||
ipc::channel().expect("ipc channel failure");
|
||||
let layout_receiver =
|
||||
|
@ -602,6 +631,9 @@ where
|
|||
|
||||
let mut constellation: Constellation<Message, LTF, STF> = Constellation {
|
||||
script_sender: ipc_script_sender,
|
||||
background_hang_monitor_sender,
|
||||
background_hang_monitor_receiver,
|
||||
background_monitor_register,
|
||||
layout_sender: ipc_layout_sender,
|
||||
script_receiver: script_receiver,
|
||||
compositor_receiver: compositor_receiver,
|
||||
|
@ -768,6 +800,10 @@ where
|
|||
sender: self.script_sender.clone(),
|
||||
pipeline_id: pipeline_id,
|
||||
},
|
||||
background_monitor_register: self.background_monitor_register.clone(),
|
||||
background_hang_monitor_to_constellation_chan: self
|
||||
.background_hang_monitor_sender
|
||||
.clone(),
|
||||
layout_to_constellation_chan: self.layout_sender.clone(),
|
||||
scheduler_chan: self.scheduler_chan.clone(),
|
||||
compositor_proxy: self.compositor_proxy.clone(),
|
||||
|
@ -889,6 +925,7 @@ where
|
|||
#[derive(Debug)]
|
||||
enum Request {
|
||||
Script((PipelineId, FromScriptMsg)),
|
||||
BackgroundHangMonitor(HangAlert),
|
||||
Compositor(FromCompositorMsg),
|
||||
Layout(FromLayoutMsg),
|
||||
NetworkListener((PipelineId, FetchResponseMsg)),
|
||||
|
@ -910,6 +947,9 @@ where
|
|||
recv(self.script_receiver) -> msg => {
|
||||
msg.expect("Unexpected script channel panic in constellation").map(Request::Script)
|
||||
}
|
||||
recv(self.background_hang_monitor_receiver) -> msg => {
|
||||
msg.expect("Unexpected BHM channel panic in constellation").map(Request::BackgroundHangMonitor)
|
||||
}
|
||||
recv(self.compositor_receiver) -> msg => {
|
||||
Ok(Request::Compositor(msg.expect("Unexpected compositor channel panic in constellation")))
|
||||
}
|
||||
|
@ -936,6 +976,9 @@ where
|
|||
Request::Script(message) => {
|
||||
self.handle_request_from_script(message);
|
||||
},
|
||||
Request::BackgroundHangMonitor(message) => {
|
||||
self.handle_request_from_background_hang_monitor(message);
|
||||
},
|
||||
Request::Layout(message) => {
|
||||
self.handle_request_from_layout(message);
|
||||
},
|
||||
|
@ -948,6 +991,12 @@ 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_network_listener(&mut self, message: (PipelineId, FetchResponseMsg)) {
|
||||
let (id, message_) = message;
|
||||
let result = match self.pipelines.get(&id) {
|
||||
|
|
|
@ -18,6 +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::{BrowsingContextId, HistoryStateId, PipelineId, PipelineNamespaceId};
|
||||
use net::image_cache::ImageCacheImpl;
|
||||
use net_traits::image_cache::ImageCache;
|
||||
|
@ -113,6 +114,13 @@ pub struct InitialPipelineState {
|
|||
/// A channel to the associated constellation.
|
||||
pub script_to_constellation_chan: ScriptToConstellationChan,
|
||||
|
||||
/// A handle to register components for hang monitoring.
|
||||
/// None when in multiprocess mode.
|
||||
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>,
|
||||
|
||||
/// A channel for the layout thread to send messages to the constellation.
|
||||
pub layout_to_constellation_chan: IpcSender<LayoutMsg>,
|
||||
|
||||
|
@ -262,6 +270,9 @@ impl Pipeline {
|
|||
parent_pipeline_id: state.parent_pipeline_id,
|
||||
opener: state.opener,
|
||||
script_to_constellation_chan: state.script_to_constellation_chan.clone(),
|
||||
background_hang_monitor_to_constellation_chan: state
|
||||
.background_hang_monitor_to_constellation_chan
|
||||
.clone(),
|
||||
scheduler_chan: state.scheduler_chan,
|
||||
devtools_chan: script_to_devtools_chan,
|
||||
bluetooth_thread: state.bluetooth_thread,
|
||||
|
@ -295,7 +306,11 @@ impl Pipeline {
|
|||
if opts::multiprocess() {
|
||||
let _ = unprivileged_pipeline_content.spawn_multiprocess()?;
|
||||
} else {
|
||||
unprivileged_pipeline_content.start_all::<Message, LTF, STF>(false);
|
||||
// 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);
|
||||
}
|
||||
|
||||
EventLoop::new(script_chan)
|
||||
|
@ -452,6 +467,7 @@ pub struct UnprivilegedPipelineContent {
|
|||
parent_pipeline_id: Option<PipelineId>,
|
||||
opener: Option<BrowsingContextId>,
|
||||
script_to_constellation_chan: ScriptToConstellationChan,
|
||||
background_hang_monitor_to_constellation_chan: IpcSender<HangAlert>,
|
||||
layout_to_constellation_chan: IpcSender<LayoutMsg>,
|
||||
scheduler_chan: IpcSender<TimerSchedulerMsg>,
|
||||
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
||||
|
@ -480,8 +496,11 @@ pub struct UnprivilegedPipelineContent {
|
|||
}
|
||||
|
||||
impl UnprivilegedPipelineContent {
|
||||
pub fn start_all<Message, LTF, STF>(self, wait_for_completion: bool)
|
||||
where
|
||||
pub fn start_all<Message, LTF, STF>(
|
||||
self,
|
||||
wait_for_completion: bool,
|
||||
background_hang_monitor_register: Box<BackgroundHangMonitorRegister>,
|
||||
) where
|
||||
LTF: LayoutThreadFactory<Message = Message>,
|
||||
STF: ScriptThreadFactory<Message = Message>,
|
||||
{
|
||||
|
@ -503,6 +522,7 @@ impl UnprivilegedPipelineContent {
|
|||
control_chan: self.script_chan.clone(),
|
||||
control_port: self.script_port,
|
||||
script_to_constellation_chan: self.script_to_constellation_chan.clone(),
|
||||
background_hang_monitor_register: background_hang_monitor_register.clone(),
|
||||
layout_to_constellation_chan: self.layout_to_constellation_chan.clone(),
|
||||
scheduler_chan: self.scheduler_chan,
|
||||
bluetooth_thread: self.bluetooth_thread,
|
||||
|
@ -529,6 +549,7 @@ impl UnprivilegedPipelineContent {
|
|||
self.parent_pipeline_id.is_some(),
|
||||
layout_pair,
|
||||
self.pipeline_port,
|
||||
background_hang_monitor_register,
|
||||
self.layout_to_constellation_chan,
|
||||
self.script_chan,
|
||||
image_cache.clone(),
|
||||
|
@ -626,6 +647,10 @@ impl UnprivilegedPipelineContent {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn background_hang_monitor_to_constellation_chan(&self) -> &IpcSender<HangAlert> {
|
||||
&self.background_hang_monitor_to_constellation_chan
|
||||
}
|
||||
|
||||
pub fn script_to_constellation_chan(&self) -> &ScriptToConstellationChan {
|
||||
&self.script_to_constellation_chan
|
||||
}
|
||||
|
|
|
@ -65,8 +65,11 @@ use layout_traits::LayoutThreadFactory;
|
|||
use libc::c_void;
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory, ProgressiveWebMetric};
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use msg::constellation_msg::TopLevelBrowsingContextId;
|
||||
use msg::constellation_msg::{
|
||||
BackgroundHangMonitor, BackgroundHangMonitorRegister, HangAnnotation,
|
||||
};
|
||||
use msg::constellation_msg::{LayoutHangAnnotation, MonitoredComponentType, PipelineId};
|
||||
use msg::constellation_msg::{MonitoredComponentId, TopLevelBrowsingContextId};
|
||||
use net_traits::image_cache::{ImageCache, UsePlaceholder};
|
||||
use parking_lot::RwLock;
|
||||
use profile_traits::mem::{self as profile_mem, Report, ReportKind, ReportsChan};
|
||||
|
@ -96,6 +99,7 @@ use std::process;
|
|||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use style::animation::Animation;
|
||||
use style::context::{QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters};
|
||||
use style::context::{SharedStyleContext, StyleSystemOptions, ThreadLocalStyleContextCreationInfo};
|
||||
|
@ -150,6 +154,9 @@ pub struct LayoutThread {
|
|||
/// The channel on which the font cache can send messages to us.
|
||||
font_cache_sender: IpcSender<()>,
|
||||
|
||||
/// A means of communication with the background hang monitor.
|
||||
background_hang_monitor: Box<BackgroundHangMonitor>,
|
||||
|
||||
/// The channel on which messages can be sent to the constellation.
|
||||
constellation_chan: IpcSender<ConstellationMsg>,
|
||||
|
||||
|
@ -253,6 +260,7 @@ impl LayoutThreadFactory for LayoutThread {
|
|||
is_iframe: bool,
|
||||
chan: (Sender<Msg>, Receiver<Msg>),
|
||||
pipeline_port: IpcReceiver<LayoutControlMsg>,
|
||||
background_hang_monitor_register: Box<BackgroundHangMonitorRegister>,
|
||||
constellation_chan: IpcSender<ConstellationMsg>,
|
||||
script_chan: IpcSender<ConstellationControlMsg>,
|
||||
image_cache: Arc<dyn ImageCache>,
|
||||
|
@ -276,6 +284,14 @@ impl LayoutThreadFactory for LayoutThread {
|
|||
{
|
||||
// Ensures layout thread is destroyed before we send shutdown message
|
||||
let sender = chan.0;
|
||||
|
||||
let background_hang_monitor = background_hang_monitor_register
|
||||
.register_component(
|
||||
MonitoredComponentId(id, MonitoredComponentType::Layout),
|
||||
Duration::from_millis(1000),
|
||||
Duration::from_millis(5000),
|
||||
);
|
||||
|
||||
let layout = LayoutThread::new(
|
||||
id,
|
||||
top_level_browsing_context_id,
|
||||
|
@ -283,6 +299,7 @@ impl LayoutThreadFactory for LayoutThread {
|
|||
is_iframe,
|
||||
chan.1,
|
||||
pipeline_port,
|
||||
background_hang_monitor,
|
||||
constellation_chan,
|
||||
script_chan,
|
||||
image_cache.clone(),
|
||||
|
@ -444,6 +461,7 @@ impl LayoutThread {
|
|||
is_iframe: bool,
|
||||
port: Receiver<Msg>,
|
||||
pipeline_port: IpcReceiver<LayoutControlMsg>,
|
||||
background_hang_monitor: Box<BackgroundHangMonitor>,
|
||||
constellation_chan: IpcSender<ConstellationMsg>,
|
||||
script_chan: IpcSender<ConstellationControlMsg>,
|
||||
image_cache: Arc<dyn ImageCache>,
|
||||
|
@ -493,6 +511,7 @@ impl LayoutThread {
|
|||
port: port,
|
||||
pipeline_port: pipeline_receiver,
|
||||
script_chan: script_chan.clone(),
|
||||
background_hang_monitor,
|
||||
constellation_chan: constellation_chan.clone(),
|
||||
time_profiler_chan: time_profiler_chan,
|
||||
mem_profiler_chan: mem_profiler_chan,
|
||||
|
@ -605,6 +624,34 @@ impl LayoutThread {
|
|||
}
|
||||
}
|
||||
|
||||
fn notify_activity_to_hang_monitor(&self, request: &Msg) {
|
||||
let hang_annotation = match request {
|
||||
Msg::AddStylesheet(..) => LayoutHangAnnotation::AddStylesheet,
|
||||
Msg::RemoveStylesheet(..) => LayoutHangAnnotation::RemoveStylesheet,
|
||||
Msg::SetQuirksMode(..) => LayoutHangAnnotation::SetQuirksMode,
|
||||
Msg::Reflow(..) => LayoutHangAnnotation::Reflow,
|
||||
Msg::GetRPC(..) => LayoutHangAnnotation::GetRPC,
|
||||
Msg::TickAnimations => LayoutHangAnnotation::TickAnimations,
|
||||
Msg::AdvanceClockMs(..) => LayoutHangAnnotation::AdvanceClockMs,
|
||||
Msg::ReapStyleAndLayoutData(..) => LayoutHangAnnotation::ReapStyleAndLayoutData,
|
||||
Msg::CollectReports(..) => LayoutHangAnnotation::CollectReports,
|
||||
Msg::PrepareToExit(..) => LayoutHangAnnotation::PrepareToExit,
|
||||
Msg::ExitNow => LayoutHangAnnotation::ExitNow,
|
||||
Msg::GetCurrentEpoch(..) => LayoutHangAnnotation::GetCurrentEpoch,
|
||||
Msg::GetWebFontLoadState(..) => LayoutHangAnnotation::GetWebFontLoadState,
|
||||
Msg::CreateLayoutThread(..) => LayoutHangAnnotation::CreateLayoutThread,
|
||||
Msg::SetFinalUrl(..) => LayoutHangAnnotation::SetFinalUrl,
|
||||
Msg::SetScrollStates(..) => LayoutHangAnnotation::SetScrollStates,
|
||||
Msg::UpdateScrollStateFromScript(..) => {
|
||||
LayoutHangAnnotation::UpdateScrollStateFromScript
|
||||
},
|
||||
Msg::RegisterPaint(..) => LayoutHangAnnotation::RegisterPaint,
|
||||
Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart,
|
||||
};
|
||||
self.background_hang_monitor
|
||||
.notify_activity(HangAnnotation::Layout(hang_annotation));
|
||||
}
|
||||
|
||||
/// Receives and dispatches messages from the script and constellation threads
|
||||
fn handle_request<'a, 'b>(&mut self, possibly_locked_rw_data: &mut RwData<'a, 'b>) -> bool {
|
||||
enum Request {
|
||||
|
@ -613,6 +660,9 @@ impl LayoutThread {
|
|||
FromFontCache,
|
||||
}
|
||||
|
||||
// Notify the background-hang-monitor we are waiting for an event.
|
||||
self.background_hang_monitor.notify_wait();
|
||||
|
||||
let request = select! {
|
||||
recv(self.pipeline_port) -> msg => Request::FromPipeline(msg.unwrap()),
|
||||
recv(self.port) -> msg => Request::FromScript(msg.unwrap()),
|
||||
|
@ -659,6 +709,8 @@ impl LayoutThread {
|
|||
request: Msg,
|
||||
possibly_locked_rw_data: &mut RwData<'a, 'b>,
|
||||
) -> bool {
|
||||
self.notify_activity_to_hang_monitor(&request);
|
||||
|
||||
match request {
|
||||
Msg::AddStylesheet(stylesheet, before_stylesheet) => {
|
||||
let guard = stylesheet.shared_lock.read();
|
||||
|
@ -815,6 +867,7 @@ impl LayoutThread {
|
|||
info.is_parent,
|
||||
info.layout_pair,
|
||||
info.pipeline_port,
|
||||
info.background_hang_monitor_register,
|
||||
info.constellation_chan,
|
||||
info.script_chan.clone(),
|
||||
info.image_cache.clone(),
|
||||
|
|
|
@ -13,8 +13,8 @@ use crossbeam_channel::{Receiver, Sender};
|
|||
use gfx::font_cache_thread::FontCacheThread;
|
||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||
use metrics::PaintTimeMetrics;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use msg::constellation_msg::TopLevelBrowsingContextId;
|
||||
use msg::constellation_msg::{BackgroundHangMonitorRegister, PipelineId};
|
||||
use net_traits::image_cache::ImageCache;
|
||||
use profile_traits::{mem, time};
|
||||
use script_traits::LayoutMsg as ConstellationMsg;
|
||||
|
@ -33,6 +33,7 @@ pub trait LayoutThreadFactory {
|
|||
is_iframe: bool,
|
||||
chan: (Sender<Self::Message>, Receiver<Self::Message>),
|
||||
pipeline_port: IpcReceiver<LayoutControlMsg>,
|
||||
background_hang_monitor: Box<BackgroundHangMonitorRegister>,
|
||||
constellation_chan: IpcSender<ConstellationMsg>,
|
||||
script_chan: IpcSender<ConstellationControlMsg>,
|
||||
image_cache: Arc<dyn ImageCache>,
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
|
||||
use std::cell::Cell;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::num::NonZeroU32;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub enum TraversalDirection {
|
||||
|
@ -285,3 +287,185 @@ pub enum InputMethodType {
|
|||
Url,
|
||||
Week,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
/// The equivalent of script_layout_interface::message::Msg
|
||||
pub enum LayoutHangAnnotation {
|
||||
AddStylesheet,
|
||||
RemoveStylesheet,
|
||||
SetQuirksMode,
|
||||
Reflow,
|
||||
GetRPC,
|
||||
TickAnimations,
|
||||
AdvanceClockMs,
|
||||
ReapStyleAndLayoutData,
|
||||
CollectReports,
|
||||
PrepareToExit,
|
||||
ExitNow,
|
||||
GetCurrentEpoch,
|
||||
GetWebFontLoadState,
|
||||
CreateLayoutThread,
|
||||
SetFinalUrl,
|
||||
SetScrollStates,
|
||||
UpdateScrollStateFromScript,
|
||||
RegisterPaint,
|
||||
SetNavigationStart,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
/// The equivalent of script::script_runtime::ScriptEventCategory
|
||||
pub enum ScriptHangAnnotation {
|
||||
AttachLayout,
|
||||
ConstellationMsg,
|
||||
DevtoolsMsg,
|
||||
DocumentEvent,
|
||||
DomEvent,
|
||||
FileRead,
|
||||
FormPlannedNavigation,
|
||||
ImageCacheMsg,
|
||||
InputEvent,
|
||||
HistoryEvent,
|
||||
NetworkEvent,
|
||||
Resize,
|
||||
ScriptEvent,
|
||||
SetScrollState,
|
||||
SetViewport,
|
||||
StylesheetLoad,
|
||||
TimerEvent,
|
||||
UpdateReplacedElement,
|
||||
WebSocketEvent,
|
||||
WorkerEvent,
|
||||
WorkletEvent,
|
||||
ServiceWorkerEvent,
|
||||
EnterFullscreen,
|
||||
ExitFullscreen,
|
||||
WebVREvent,
|
||||
PerformanceTimelineTask,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub enum HangAnnotation {
|
||||
Layout(LayoutHangAnnotation),
|
||||
Script(ScriptHangAnnotation),
|
||||
}
|
||||
|
||||
/// Hang-alerts are sent by the monitor to the constellation.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub enum HangAlert {
|
||||
/// Report a transient hang.
|
||||
Transient(MonitoredComponentId, HangAnnotation),
|
||||
/// Report a permanent hang.
|
||||
Permanent(MonitoredComponentId, HangAnnotation, Option<HangProfile>),
|
||||
}
|
||||
|
||||
impl fmt::Debug for HangAlert {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let (annotation, profile) = match self {
|
||||
HangAlert::Transient(component_id, annotation) => {
|
||||
write!(
|
||||
fmt,
|
||||
"\n The following component is experiencing a transient hang: \n {:?}",
|
||||
component_id
|
||||
)?;
|
||||
(annotation.clone(), None)
|
||||
},
|
||||
HangAlert::Permanent(component_id, annotation, profile) => {
|
||||
write!(
|
||||
fmt,
|
||||
"\n The following component is experiencing a permanent hang: \n {:?}",
|
||||
component_id
|
||||
)?;
|
||||
(annotation.clone(), profile.clone())
|
||||
},
|
||||
};
|
||||
|
||||
write!(fmt, "\n Annotation for the hang:\n{:?}", annotation)?;
|
||||
if let Some(profile) = profile {
|
||||
write!(fmt, "\n {:?}", profile)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct HangProfileSymbol {
|
||||
pub name: Option<String>,
|
||||
pub filename: Option<String>,
|
||||
pub lineno: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
/// Info related to the activity of an hanging component.
|
||||
pub struct HangProfile {
|
||||
pub backtrace: Vec<HangProfileSymbol>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for HangProfile {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let hex_width = mem::size_of::<usize>() * 2 + 2;
|
||||
|
||||
write!(fmt, "HangProfile backtrace:")?;
|
||||
|
||||
if self.backtrace.len() == 0 {
|
||||
write!(fmt, "backtrace failed to resolve")?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for symbol in self.backtrace.iter() {
|
||||
write!(fmt, "\n {:1$}", "", hex_width)?;
|
||||
|
||||
if let Some(ref name) = symbol.name {
|
||||
write!(fmt, " - {}", name)?;
|
||||
} else {
|
||||
write!(fmt, " - <unknown>")?;
|
||||
}
|
||||
|
||||
if let (Some(ref file), Some(ref line)) = (symbol.filename.as_ref(), symbol.lineno) {
|
||||
write!(fmt, "\n {:3$}at {}:{}", "", file, line, hex_width)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub enum MonitoredComponentType {
|
||||
Layout,
|
||||
Script,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct MonitoredComponentId(pub PipelineId, pub MonitoredComponentType);
|
||||
|
||||
/// A handle to register components for hang monitoring,
|
||||
/// and to receive a means to communicate with the underlying hang monitor worker.
|
||||
pub trait BackgroundHangMonitorRegister: BackgroundHangMonitorClone + Send {
|
||||
/// Register a component for hang monitoring:
|
||||
/// to be called from within the thread to be monitored for hangs.
|
||||
fn register_component(
|
||||
&self,
|
||||
component: MonitoredComponentId,
|
||||
transient_hang_timeout: Duration,
|
||||
permanent_hang_timeout: Duration,
|
||||
) -> Box<BackgroundHangMonitor>;
|
||||
}
|
||||
|
||||
impl Clone for Box<BackgroundHangMonitorRegister> {
|
||||
fn clone(&self) -> Box<BackgroundHangMonitorRegister> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BackgroundHangMonitorClone {
|
||||
fn clone_box(&self) -> Box<BackgroundHangMonitorRegister>;
|
||||
}
|
||||
|
||||
/// Proxy methods to communicate with the background hang monitor
|
||||
pub trait BackgroundHangMonitor {
|
||||
/// Notify the start of handling an event.
|
||||
fn notify_activity(&self, annotation: HangAnnotation);
|
||||
/// Notify the start of waiting for a new event to come in.
|
||||
fn notify_wait(&self);
|
||||
}
|
||||
|
|
|
@ -101,7 +101,11 @@ use js::jsapi::{JSTracer, SetWindowProxyClass};
|
|||
use js::jsval::UndefinedValue;
|
||||
use metrics::{PaintTimeMetrics, MAX_TASK_NS};
|
||||
use mime::{self, Mime};
|
||||
use msg::constellation_msg::{
|
||||
BackgroundHangMonitor, BackgroundHangMonitorRegister, ScriptHangAnnotation,
|
||||
};
|
||||
use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId};
|
||||
use msg::constellation_msg::{HangAnnotation, MonitoredComponentId, MonitoredComponentType};
|
||||
use msg::constellation_msg::{PipelineNamespace, TopLevelBrowsingContextId};
|
||||
use net_traits::image_cache::{ImageCache, PendingImageResponse};
|
||||
use net_traits::request::{CredentialsMode, Destination, RedirectMode, RequestInit};
|
||||
|
@ -140,7 +144,7 @@ use std::rc::Rc;
|
|||
use std::result::Result;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::SystemTime;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use style::thread_state::{self, ThreadState};
|
||||
use time::{at_utc, get_time, precise_time_ns, Timespec};
|
||||
use url::percent_encoding::percent_decode;
|
||||
|
@ -483,6 +487,9 @@ unsafe_no_jsmanaged_fields!(RefCell<IncompleteParserContexts>);
|
|||
|
||||
unsafe_no_jsmanaged_fields!(TaskQueue<MainThreadScriptMsg>);
|
||||
|
||||
unsafe_no_jsmanaged_fields!(BackgroundHangMonitorRegister);
|
||||
unsafe_no_jsmanaged_fields!(BackgroundHangMonitor);
|
||||
|
||||
#[derive(JSTraceable)]
|
||||
// ScriptThread instances are rooted on creation, so this is okay
|
||||
#[allow(unrooted_must_root)]
|
||||
|
@ -511,6 +518,11 @@ pub struct ScriptThread {
|
|||
/// A queue of tasks to be executed in this script-thread.
|
||||
task_queue: TaskQueue<MainThreadScriptMsg>,
|
||||
|
||||
/// A handle to register associated layout threads for hang-monitoring.
|
||||
background_hang_monitor_register: Box<BackgroundHangMonitorRegister>,
|
||||
/// The dedicated means of communication with the background-hang-monitor for this script-thread.
|
||||
background_hang_monitor: Box<BackgroundHangMonitor>,
|
||||
|
||||
/// A channel to hand out to script thread-based entities that need to be able to enqueue
|
||||
/// events in the event queue.
|
||||
chan: MainThreadScriptChan,
|
||||
|
@ -668,6 +680,7 @@ impl ScriptThreadFactory for ScriptThread {
|
|||
let opener = state.opener;
|
||||
let mem_profiler_chan = state.mem_profiler_chan.clone();
|
||||
let window_size = state.window_size;
|
||||
|
||||
let script_thread = ScriptThread::new(state, script_port, script_chan.clone());
|
||||
|
||||
SCRIPT_THREAD_ROOT.with(|root| {
|
||||
|
@ -1019,6 +1032,12 @@ impl ScriptThread {
|
|||
|
||||
let task_queue = TaskQueue::new(port, chan.clone());
|
||||
|
||||
let background_hang_monitor = state.background_hang_monitor_register.register_component(
|
||||
MonitoredComponentId(state.id, MonitoredComponentType::Script),
|
||||
Duration::from_millis(1000),
|
||||
Duration::from_millis(5000),
|
||||
);
|
||||
|
||||
ScriptThread {
|
||||
documents: DomRefCell::new(Documents::new()),
|
||||
window_proxies: DomRefCell::new(HashMap::new()),
|
||||
|
@ -1036,6 +1055,9 @@ impl ScriptThread {
|
|||
|
||||
task_queue,
|
||||
|
||||
background_hang_monitor_register: state.background_hang_monitor_register,
|
||||
background_hang_monitor,
|
||||
|
||||
chan: MainThreadScriptChan(chan.clone()),
|
||||
dom_manipulation_task_sender: boxed_script_sender.clone(),
|
||||
media_element_task_sender: chan.clone(),
|
||||
|
@ -1129,6 +1151,9 @@ impl ScriptThread {
|
|||
// Store new resizes, and gather all other events.
|
||||
let mut sequential = vec![];
|
||||
|
||||
// Notify the background-hang-monitor we are waiting for an event.
|
||||
self.background_hang_monitor.notify_wait();
|
||||
|
||||
// Receive at least one message so we don't spinloop.
|
||||
debug!("Waiting for event.");
|
||||
let mut event = select! {
|
||||
|
@ -1325,6 +1350,47 @@ impl ScriptThread {
|
|||
}
|
||||
}
|
||||
|
||||
fn notify_activity_to_hang_monitor(&self, category: &ScriptThreadEventCategory) {
|
||||
let hang_annotation = match category {
|
||||
ScriptThreadEventCategory::AttachLayout => ScriptHangAnnotation::AttachLayout,
|
||||
ScriptThreadEventCategory::ConstellationMsg => ScriptHangAnnotation::ConstellationMsg,
|
||||
ScriptThreadEventCategory::DevtoolsMsg => ScriptHangAnnotation::DevtoolsMsg,
|
||||
ScriptThreadEventCategory::DocumentEvent => ScriptHangAnnotation::DocumentEvent,
|
||||
ScriptThreadEventCategory::DomEvent => ScriptHangAnnotation::DomEvent,
|
||||
ScriptThreadEventCategory::FileRead => ScriptHangAnnotation::FileRead,
|
||||
ScriptThreadEventCategory::FormPlannedNavigation => {
|
||||
ScriptHangAnnotation::FormPlannedNavigation
|
||||
},
|
||||
ScriptThreadEventCategory::HistoryEvent => ScriptHangAnnotation::HistoryEvent,
|
||||
ScriptThreadEventCategory::ImageCacheMsg => ScriptHangAnnotation::ImageCacheMsg,
|
||||
ScriptThreadEventCategory::InputEvent => ScriptHangAnnotation::InputEvent,
|
||||
ScriptThreadEventCategory::NetworkEvent => ScriptHangAnnotation::NetworkEvent,
|
||||
ScriptThreadEventCategory::Resize => ScriptHangAnnotation::Resize,
|
||||
ScriptThreadEventCategory::ScriptEvent => ScriptHangAnnotation::ScriptEvent,
|
||||
ScriptThreadEventCategory::SetScrollState => ScriptHangAnnotation::SetScrollState,
|
||||
ScriptThreadEventCategory::SetViewport => ScriptHangAnnotation::SetViewport,
|
||||
ScriptThreadEventCategory::StylesheetLoad => ScriptHangAnnotation::StylesheetLoad,
|
||||
ScriptThreadEventCategory::TimerEvent => ScriptHangAnnotation::TimerEvent,
|
||||
ScriptThreadEventCategory::UpdateReplacedElement => {
|
||||
ScriptHangAnnotation::UpdateReplacedElement
|
||||
},
|
||||
ScriptThreadEventCategory::WebSocketEvent => ScriptHangAnnotation::WebSocketEvent,
|
||||
ScriptThreadEventCategory::WorkerEvent => ScriptHangAnnotation::WorkerEvent,
|
||||
ScriptThreadEventCategory::WorkletEvent => ScriptHangAnnotation::WorkletEvent,
|
||||
ScriptThreadEventCategory::ServiceWorkerEvent => {
|
||||
ScriptHangAnnotation::ServiceWorkerEvent
|
||||
},
|
||||
ScriptThreadEventCategory::EnterFullscreen => ScriptHangAnnotation::EnterFullscreen,
|
||||
ScriptThreadEventCategory::ExitFullscreen => ScriptHangAnnotation::ExitFullscreen,
|
||||
ScriptThreadEventCategory::WebVREvent => ScriptHangAnnotation::WebVREvent,
|
||||
ScriptThreadEventCategory::PerformanceTimelineTask => {
|
||||
ScriptHangAnnotation::PerformanceTimelineTask
|
||||
},
|
||||
};
|
||||
self.background_hang_monitor
|
||||
.notify_activity(HangAnnotation::Script(hang_annotation));
|
||||
}
|
||||
|
||||
fn message_to_pipeline(&self, msg: &MixedMessage) -> Option<PipelineId> {
|
||||
use script_traits::ConstellationControlMsg::*;
|
||||
match *msg {
|
||||
|
@ -1399,6 +1465,7 @@ impl ScriptThread {
|
|||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
self.notify_activity_to_hang_monitor(&category);
|
||||
let start = precise_time_ns();
|
||||
let value = if opts::get().profile_script_events {
|
||||
let profiler_cat = match category {
|
||||
|
@ -1855,6 +1922,7 @@ impl ScriptThread {
|
|||
is_parent: false,
|
||||
layout_pair: layout_pair,
|
||||
pipeline_port: pipeline_port,
|
||||
background_hang_monitor_register: self.background_hang_monitor_register.clone(),
|
||||
constellation_chan: self.layout_to_constellation_chan.clone(),
|
||||
script_chan: self.control_chan.clone(),
|
||||
image_cache: self.image_cache.clone(),
|
||||
|
|
|
@ -10,7 +10,7 @@ use euclid::{Point2D, Rect};
|
|||
use gfx_traits::Epoch;
|
||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||
use metrics::PaintTimeMetrics;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use msg::constellation_msg::{BackgroundHangMonitorRegister, PipelineId};
|
||||
use net_traits::image_cache::ImageCache;
|
||||
use profile_traits::mem::ReportsChan;
|
||||
use script_traits::Painter;
|
||||
|
@ -212,6 +212,7 @@ pub struct NewLayoutThreadInfo {
|
|||
pub is_parent: bool,
|
||||
pub layout_pair: (Sender<Msg>, Receiver<Msg>),
|
||||
pub pipeline_port: IpcReceiver<LayoutControlMsg>,
|
||||
pub background_hang_monitor_register: Box<BackgroundHangMonitorRegister>,
|
||||
pub constellation_chan: IpcSender<ConstellationMsg>,
|
||||
pub script_chan: IpcSender<ConstellationControlMsg>,
|
||||
pub image_cache: Arc<dyn ImageCache>,
|
||||
|
|
|
@ -33,6 +33,7 @@ use ipc_channel::Error as IpcError;
|
|||
use keyboard_types::webdriver::Event as WebDriverInputEvent;
|
||||
use keyboard_types::{CompositionEvent, KeyboardEvent};
|
||||
use libc::c_void;
|
||||
use msg::constellation_msg::BackgroundHangMonitorRegister;
|
||||
use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId};
|
||||
use msg::constellation_msg::{PipelineNamespaceId, TopLevelBrowsingContextId, TraversalDirection};
|
||||
use net_traits::image::base::Image;
|
||||
|
@ -546,6 +547,8 @@ pub struct InitialScriptState {
|
|||
pub control_port: IpcReceiver<ConstellationControlMsg>,
|
||||
/// A channel on which messages can be sent to the constellation from script.
|
||||
pub script_to_constellation_chan: ScriptToConstellationChan,
|
||||
/// A handle to register script-(and associated layout-)threads for hang monitoring.
|
||||
pub background_hang_monitor_register: Box<BackgroundHangMonitorRegister>,
|
||||
/// A sender for the layout thread to communicate to the constellation.
|
||||
pub layout_to_constellation_chan: IpcSender<LayoutMsg>,
|
||||
/// A channel to schedule timer events.
|
||||
|
|
|
@ -34,6 +34,7 @@ webgl_backtrace = [
|
|||
]
|
||||
|
||||
[dependencies]
|
||||
background_hang_monitor = {path = "../background_hang_monitor"}
|
||||
bluetooth_traits = {path = "../bluetooth_traits"}
|
||||
bluetooth = {path = "../bluetooth"}
|
||||
canvas = {path = "../canvas"}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub use background_hang_monitor;
|
||||
pub use bluetooth;
|
||||
pub use bluetooth_traits;
|
||||
pub use canvas;
|
||||
|
@ -59,6 +60,7 @@ fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
|
|||
#[cfg(not(feature = "webdriver"))]
|
||||
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) {}
|
||||
|
||||
use background_hang_monitor::HangMonitorRegister;
|
||||
use bluetooth::BluetoothThreadFactory;
|
||||
use bluetooth_traits::BluetoothRequest;
|
||||
use canvas::gl_context::GLContextFactory;
|
||||
|
@ -640,6 +642,12 @@ pub fn run_content_process(token: String) {
|
|||
create_sandbox();
|
||||
}
|
||||
|
||||
let background_hang_monitor_register = HangMonitorRegister::init(
|
||||
unprivileged_content
|
||||
.background_hang_monitor_to_constellation_chan()
|
||||
.clone(),
|
||||
);
|
||||
|
||||
// send the required channels to the service worker manager
|
||||
let sw_senders = unprivileged_content.swmanager_senders();
|
||||
script::init();
|
||||
|
@ -647,7 +655,10 @@ pub fn run_content_process(token: String) {
|
|||
|
||||
unprivileged_content.start_all::<script_layout_interface::message::Msg,
|
||||
layout_thread::LayoutThread,
|
||||
script::script_thread::ScriptThread>(true);
|
||||
script::script_thread::ScriptThread>(
|
||||
true,
|
||||
background_hang_monitor_register
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_os = "windows"), not(target_os = "ios")))]
|
||||
|
|
|
@ -15,7 +15,7 @@ import subprocess
|
|||
import sys
|
||||
|
||||
symbol_regex = re.compile(b"D \*UND\*\t(.*) (.*)$")
|
||||
allowed_symbols = frozenset([b'unshare', b'malloc_usable_size', b'__cxa_type_match'])
|
||||
allowed_symbols = frozenset([b'unshare', b'malloc_usable_size', b'__cxa_type_match', b'signal'])
|
||||
actual_symbols = set()
|
||||
|
||||
objdump_output = subprocess.check_output([
|
||||
|
|
|
@ -192,10 +192,14 @@ class MachCommands(CommandBase):
|
|||
default=None,
|
||||
action='store_true',
|
||||
help='Build the libsimpleservo library instead of the servo executable')
|
||||
@CommandArgument('--with-frame-pointer',
|
||||
default=None,
|
||||
action='store_true',
|
||||
help='Build with frame pointer enabled, used by the background hang monitor.')
|
||||
def build(self, target=None, release=False, dev=False, jobs=None,
|
||||
features=None, android=None, magicleap=None, no_package=False, verbose=False, very_verbose=False,
|
||||
debug_mozjs=False, params=None, with_debug_assertions=False,
|
||||
libsimpleservo=False):
|
||||
libsimpleservo=False, with_frame_pointer=False):
|
||||
|
||||
opts = params or []
|
||||
|
||||
|
@ -288,6 +292,9 @@ class MachCommands(CommandBase):
|
|||
if with_debug_assertions:
|
||||
env['RUSTFLAGS'] = env.get('RUSTFLAGS', "") + " -C debug_assertions"
|
||||
|
||||
if with_frame_pointer:
|
||||
env['RUSTFLAGS'] = env.get('RUSTFLAGS', "") + " -C force-frame-pointers=yes"
|
||||
|
||||
if android:
|
||||
if "ANDROID_NDK" not in env:
|
||||
print("Please set the ANDROID_NDK environment variable.")
|
||||
|
|
|
@ -245,6 +245,7 @@ class MachCommands(CommandBase):
|
|||
test_patterns.append(test)
|
||||
|
||||
self_contained_tests = [
|
||||
"background_hang_monitor",
|
||||
"gfx",
|
||||
"layout",
|
||||
"msg",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue