| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ |
| |
| #![deny(unsafe_code)] |
| |
| //! An API interface to the BackgroundHangMonitor. |
| |
| use std::time::Duration; |
| use std::{fmt, mem}; |
| |
| use base::id::PipelineId; |
| use serde::{Deserialize, Serialize}; |
| |
| #[derive(Clone, Copy, Debug, Deserialize, Serialize)] |
| /// The equivalent of script::script_runtime::ScriptEventCategory |
| pub enum ScriptHangAnnotation { |
| AttachLayout, |
| ConstellationMsg, |
| DatabaseAccessEvent, |
| DevtoolsMsg, |
| DocumentEvent, |
| FileRead, |
| FontLoading, |
| FormPlannedNavigation, |
| ImageCacheMsg, |
| InputEvent, |
| HistoryEvent, |
| NetworkEvent, |
| Rendering, |
| Resize, |
| ScriptEvent, |
| SetScrollState, |
| SetViewport, |
| StylesheetLoad, |
| TimerEvent, |
| UpdateReplacedElement, |
| WebSocketEvent, |
| WorkerEvent, |
| WorkletEvent, |
| ServiceWorkerEvent, |
| EnterFullscreen, |
| ExitFullscreen, |
| WebVREvent, |
| PerformanceTimelineTask, |
| PortMessage, |
| WebGPUMsg, |
| } |
| |
| #[derive(Clone, Copy, Debug, Deserialize, Serialize)] |
| pub enum HangAnnotation { |
| Script(ScriptHangAnnotation), |
| } |
| |
| /// Hang-alerts are sent by the monitor to the constellation. |
| #[derive(Deserialize, Serialize)] |
| pub enum HangMonitorAlert { |
| /// A component hang has been detected. |
| Hang(HangAlert), |
| /// Report a completed sampled profile. |
| Profile(Vec<u8>), |
| } |
| |
| impl fmt::Debug for HangMonitorAlert { |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| match *self { |
| HangMonitorAlert::Hang(..) => write!(fmt, "Hang"), |
| HangMonitorAlert::Profile(..) => write!(fmt, "Profile"), |
| } |
| } |
| } |
| |
| /// 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, None) |
| }, |
| HangAlert::Permanent(component_id, annotation, profile) => { |
| write!( |
| fmt, |
| "\n The following component is experiencing a permanent hang: \n {:?}", |
| component_id |
| )?; |
| (*annotation, 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.is_empty() { |
| 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 { |
| 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, |
| exit_signal: Box<dyn BackgroundHangMonitorExitSignal>, |
| ) -> Box<dyn BackgroundHangMonitor>; |
| } |
| |
| impl Clone for Box<dyn BackgroundHangMonitorRegister> { |
| fn clone(&self) -> Box<dyn BackgroundHangMonitorRegister> { |
| self.clone_box() |
| } |
| } |
| |
| pub trait BackgroundHangMonitorClone { |
| fn clone_box(&self) -> Box<dyn 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); |
| /// Unregister the component from monitor. |
| fn unregister(&self); |
| } |
| |
| /// A means for the BHM to signal a monitored component to exit. |
| /// Useful when the component is hanging, and cannot be notified via the usual way. |
| /// The component should implement this in a way allowing for the signal to be received when hanging, |
| /// if at all. |
| pub trait BackgroundHangMonitorExitSignal: Send { |
| /// Called by the BHM, to notify the monitored component to exit. |
| fn signal_to_exit(&self); |
| } |
| |
| /// Messages to control the sampling profiler. |
| #[derive(Deserialize, Serialize)] |
| pub enum BackgroundHangMonitorControlMsg { |
| /// Toggle the sampler, with a given sampling rate and max total sampling duration. |
| ToggleSampler(Duration, Duration), |
| /// Propagate exit signal to monitored components, and shutdown when they have. |
| Exit, |
| } |