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