background_hang_monitor_api/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5#![deny(unsafe_code)]
6
7//! An API interface to the BackgroundHangMonitor.
8
9use std::time::Duration;
10use std::{fmt, mem};
11
12use base::id::PipelineId;
13use serde::{Deserialize, Serialize};
14
15#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
16/// The equivalent of script::script_runtime::ScriptEventCategory
17pub enum ScriptHangAnnotation {
18    AttachLayout,
19    ConstellationMsg,
20    DatabaseAccessEvent,
21    DevtoolsMsg,
22    DocumentEvent,
23    FileRead,
24    FontLoading,
25    FormPlannedNavigation,
26    GeolocationEvent,
27    ImageCacheMsg,
28    InputEvent,
29    HistoryEvent,
30    NetworkEvent,
31    Rendering,
32    Resize,
33    ScriptEvent,
34    SetScrollState,
35    SetViewport,
36    StylesheetLoad,
37    TimerEvent,
38    UpdateReplacedElement,
39    WebSocketEvent,
40    WorkerEvent,
41    WorkletEvent,
42    ServiceWorkerEvent,
43    EnterFullscreen,
44    ExitFullscreen,
45    WebVREvent,
46    PerformanceTimelineTask,
47    PortMessage,
48    WebGPUMsg,
49}
50
51#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
52pub enum HangAnnotation {
53    Script(ScriptHangAnnotation),
54}
55
56/// Hang-alerts are sent by the monitor to the constellation.
57#[derive(Deserialize, Serialize)]
58pub enum HangMonitorAlert {
59    /// A component hang has been detected.
60    Hang(HangAlert),
61    /// Report a completed sampled profile.
62    Profile(Vec<u8>),
63}
64
65impl fmt::Debug for HangMonitorAlert {
66    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
67        match *self {
68            HangMonitorAlert::Hang(..) => write!(fmt, "Hang"),
69            HangMonitorAlert::Profile(..) => write!(fmt, "Profile"),
70        }
71    }
72}
73
74/// Hang-alerts are sent by the monitor to the constellation.
75#[derive(Deserialize, Serialize)]
76pub enum HangAlert {
77    /// Report a transient hang.
78    Transient(MonitoredComponentId, HangAnnotation),
79    /// Report a permanent hang.
80    Permanent(MonitoredComponentId, HangAnnotation, Option<HangProfile>),
81}
82
83impl fmt::Debug for HangAlert {
84    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
85        let (annotation, profile) = match self {
86            HangAlert::Transient(component_id, annotation) => {
87                write!(
88                    fmt,
89                    "\n The following component is experiencing a transient hang: \n {:?}",
90                    component_id
91                )?;
92                (*annotation, None)
93            },
94            HangAlert::Permanent(component_id, annotation, profile) => {
95                write!(
96                    fmt,
97                    "\n The following component is experiencing a permanent hang: \n {:?}",
98                    component_id
99                )?;
100                (*annotation, profile.clone())
101            },
102        };
103
104        write!(fmt, "\n Annotation for the hang:\n{:?}", annotation)?;
105        if let Some(profile) = profile {
106            write!(fmt, "\n {:?}", profile)?;
107        }
108
109        Ok(())
110    }
111}
112
113#[derive(Clone, Deserialize, Serialize)]
114pub struct HangProfileSymbol {
115    pub name: Option<String>,
116    pub filename: Option<String>,
117    pub lineno: Option<u32>,
118}
119
120#[derive(Clone, Deserialize, Serialize)]
121/// Info related to the activity of an hanging component.
122pub struct HangProfile {
123    pub backtrace: Vec<HangProfileSymbol>,
124}
125
126impl fmt::Debug for HangProfile {
127    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
128        let hex_width = mem::size_of::<usize>() * 2 + 2;
129
130        write!(fmt, "HangProfile backtrace:")?;
131
132        if self.backtrace.is_empty() {
133            write!(fmt, "backtrace failed to resolve")?;
134            return Ok(());
135        }
136
137        for symbol in self.backtrace.iter() {
138            write!(fmt, "\n      {:1$}", "", hex_width)?;
139
140            if let Some(ref name) = symbol.name {
141                write!(fmt, " - {}", name)?;
142            } else {
143                write!(fmt, " - <unknown>")?;
144            }
145
146            if let (Some(ref file), Some(ref line)) = (symbol.filename.as_ref(), symbol.lineno) {
147                write!(fmt, "\n      {:3$}at {}:{}", "", file, line, hex_width)?;
148            }
149        }
150
151        Ok(())
152    }
153}
154
155#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
156pub enum MonitoredComponentType {
157    Script,
158}
159
160#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
161pub struct MonitoredComponentId(pub PipelineId, pub MonitoredComponentType);
162
163/// A handle to register components for hang monitoring,
164/// and to receive a means to communicate with the underlying hang monitor worker.
165pub trait BackgroundHangMonitorRegister: BackgroundHangMonitorClone + Send {
166    /// Register a component for hang monitoring:
167    /// to be called from within the thread to be monitored for hangs.
168    fn register_component(
169        &self,
170        component: MonitoredComponentId,
171        transient_hang_timeout: Duration,
172        permanent_hang_timeout: Duration,
173        exit_signal: Box<dyn BackgroundHangMonitorExitSignal>,
174    ) -> Box<dyn BackgroundHangMonitor>;
175}
176
177impl Clone for Box<dyn BackgroundHangMonitorRegister> {
178    fn clone(&self) -> Box<dyn BackgroundHangMonitorRegister> {
179        self.clone_box()
180    }
181}
182
183pub trait BackgroundHangMonitorClone {
184    fn clone_box(&self) -> Box<dyn BackgroundHangMonitorRegister>;
185}
186
187/// Proxy methods to communicate with the background hang monitor
188pub trait BackgroundHangMonitor {
189    /// Notify the start of handling an event.
190    fn notify_activity(&self, annotation: HangAnnotation);
191    /// Notify the start of waiting for a new event to come in.
192    fn notify_wait(&self);
193    /// Unregister the component from monitor.
194    fn unregister(&self);
195}
196
197/// A means for the BHM to signal a monitored component to exit.
198/// Useful when the component is hanging, and cannot be notified via the usual way.
199/// The component should implement this in a way allowing for the signal to be received when hanging,
200/// if at all.
201pub trait BackgroundHangMonitorExitSignal: Send {
202    /// Called by the BHM, to notify the monitored component to exit.
203    fn signal_to_exit(&self);
204}
205
206/// Messages to control the sampling profiler.
207#[derive(Deserialize, Serialize)]
208pub enum BackgroundHangMonitorControlMsg {
209    /// Toggle the sampler, with a given sampling rate and max total sampling duration.
210    ToggleSampler(Duration, Duration),
211    /// Propagate exit signal to monitored components, and shutdown when they have.
212    Exit,
213}