Skip to main content

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 serde::{Deserialize, Serialize};
13use servo_base::id::ScriptEventLoopId;
14
15#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
16/// The equivalent of script::script_runtime::ScriptEventCategory
17pub enum ScriptHangAnnotation {
18    SpawnPipeline,
19    ConstellationMsg,
20    DatabaseAccessEvent,
21    DevtoolsMsg,
22    DocumentEvent,
23    FileRead,
24    FontLoading,
25    FormPlannedNavigation,
26    GeolocationEvent,
27    ImageCacheMsg,
28    InputEvent,
29    NavigationAndTraversalEvent,
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 HangAlert {
59    /// Report a transient hang.
60    Transient(MonitoredComponentId, HangAnnotation),
61    /// Report a permanent hang.
62    Permanent(MonitoredComponentId, HangAnnotation, Option<HangProfile>),
63}
64
65impl fmt::Debug for HangAlert {
66    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
67        let (annotation, profile) = match self {
68            HangAlert::Transient(component_id, annotation) => {
69                write!(
70                    fmt,
71                    "\n The following component is experiencing a transient hang: \n {:?}",
72                    component_id
73                )?;
74                (*annotation, None)
75            },
76            HangAlert::Permanent(component_id, annotation, profile) => {
77                write!(
78                    fmt,
79                    "\n The following component is experiencing a permanent hang: \n {:?}",
80                    component_id
81                )?;
82                (*annotation, profile.clone())
83            },
84        };
85
86        write!(fmt, "\n Annotation for the hang:\n{:?}", annotation)?;
87        if let Some(profile) = profile {
88            write!(fmt, "\n {:?}", profile)?;
89        }
90
91        Ok(())
92    }
93}
94
95#[derive(Clone, Deserialize, Serialize)]
96pub struct HangProfileSymbol {
97    pub name: Option<String>,
98    pub filename: Option<String>,
99    pub lineno: Option<u32>,
100}
101
102#[derive(Clone, Deserialize, Serialize)]
103/// Info related to the activity of an hanging component.
104pub struct HangProfile {
105    pub backtrace: Vec<HangProfileSymbol>,
106}
107
108impl fmt::Debug for HangProfile {
109    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
110        let hex_width = mem::size_of::<usize>() * 2 + 2;
111
112        write!(fmt, "HangProfile backtrace:")?;
113
114        if self.backtrace.is_empty() {
115            write!(fmt, "backtrace failed to resolve")?;
116            return Ok(());
117        }
118
119        for symbol in self.backtrace.iter() {
120            write!(fmt, "\n      {:1$}", "", hex_width)?;
121
122            if let Some(ref name) = symbol.name {
123                write!(fmt, " - {}", name)?;
124            } else {
125                write!(fmt, " - <unknown>")?;
126            }
127
128            if let (Some(ref file), Some(ref line)) = (symbol.filename.as_ref(), symbol.lineno) {
129                write!(fmt, "\n      {:3$}at {}:{}", "", file, line, hex_width)?;
130            }
131        }
132
133        Ok(())
134    }
135}
136
137#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
138pub enum MonitoredComponentType {
139    Script,
140}
141
142#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
143pub struct MonitoredComponentId(pub ScriptEventLoopId, pub MonitoredComponentType);
144
145/// A handle to register components for hang monitoring,
146/// and to receive a means to communicate with the underlying hang monitor worker.
147pub trait BackgroundHangMonitorRegister: BackgroundHangMonitorClone + Send {
148    /// Register a component for hang monitoring:
149    /// to be called from within the thread to be monitored for hangs.
150    fn register_component(
151        &self,
152        component: MonitoredComponentId,
153        transient_hang_timeout: Duration,
154        permanent_hang_timeout: Duration,
155        exit_signal: Box<dyn BackgroundHangMonitorExitSignal>,
156    ) -> Box<dyn BackgroundHangMonitor>;
157}
158
159impl Clone for Box<dyn BackgroundHangMonitorRegister> {
160    fn clone(&self) -> Box<dyn BackgroundHangMonitorRegister> {
161        self.clone_box()
162    }
163}
164
165pub trait BackgroundHangMonitorClone {
166    fn clone_box(&self) -> Box<dyn BackgroundHangMonitorRegister>;
167}
168
169/// Proxy methods to communicate with the background hang monitor
170pub trait BackgroundHangMonitor {
171    /// Notify the start of handling an event.
172    fn notify_activity(&self, annotation: HangAnnotation);
173    /// Notify the start of waiting for a new event to come in.
174    fn notify_wait(&self);
175    /// Unregister the component from monitor.
176    fn unregister(&self);
177}
178
179/// A means for the BHM to signal a monitored component to exit.
180/// Useful when the component is hanging, and cannot be notified via the usual way.
181/// The component should implement this in a way allowing for the signal to be received when hanging,
182/// if at all.
183pub trait BackgroundHangMonitorExitSignal: Send {
184    /// Called by the BHM, to notify the monitored component to exit.
185    fn signal_to_exit(&self);
186}
187
188/// Messages to control the sampling profiler.
189#[derive(Clone, Debug, Deserialize, Serialize)]
190pub enum BackgroundHangMonitorControlMsg {
191    /// Propagate exit signal to monitored components, and shutdown when they have.
192    Exit,
193}