profile_traits/
time.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
5use base::cross_process_instant::CrossProcessInstant;
6use ipc_channel::ipc::IpcSender;
7use log::warn;
8use malloc_size_of_derive::MallocSizeOf;
9use serde::{Deserialize, Serialize};
10use time::Duration;
11
12#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
13pub struct TimerMetadata {
14    pub url: String,
15    pub iframe: TimerMetadataFrameType,
16    pub incremental: TimerMetadataReflowType,
17}
18
19#[derive(Clone, Debug, Deserialize, Serialize)]
20pub struct ProfilerChan(pub IpcSender<ProfilerMsg>);
21
22impl ProfilerChan {
23    pub fn send(&self, msg: ProfilerMsg) {
24        if let Err(e) = self.0.send(msg) {
25            warn!("Error communicating with the time profiler thread: {}", e);
26        }
27    }
28}
29
30#[derive(Clone, Debug, Deserialize, Serialize)]
31pub enum ProfilerData {
32    NoRecords,
33    Record(Vec<Duration>),
34}
35
36#[derive(Clone, Debug, Deserialize, Serialize)]
37pub enum ProfilerMsg {
38    /// Normal message used for reporting time
39    Time(
40        (ProfilerCategory, Option<TimerMetadata>),
41        (CrossProcessInstant, CrossProcessInstant),
42    ),
43    /// Message used to get time spend entries for a particular ProfilerBuckets (in nanoseconds)
44    Get(
45        (ProfilerCategory, Option<TimerMetadata>),
46        IpcSender<ProfilerData>,
47    ),
48    /// Message used to force print the profiling metrics
49    Print,
50
51    /// Report a layout query that could not be processed immediately for a particular URL.
52    BlockedLayoutQuery(String),
53
54    /// Tells the profiler to shut down.
55    Exit(IpcSender<()>),
56}
57
58/// Usage sites of variants marked “Rust tracing only” are not visible to rust-analyzer.
59#[repr(u32)]
60#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
61pub enum ProfilerCategory {
62    /// The compositor is rasterising or presenting.
63    ///
64    /// Not associated with a specific URL.
65    Compositing = 0x00,
66
67    /// The script thread is doing layout work.
68    Layout = 0x10,
69
70    ImageSaving = 0x51,
71    ScriptAttachLayout = 0x60,
72    ScriptConstellationMsg = 0x61,
73    ScriptDevtoolsMsg = 0x62,
74    ScriptDocumentEvent = 0x63,
75
76    /// Rust tracing only: the script thread is executing a script.
77    /// This may include time doing layout or parse work initiated by the script.
78    ScriptEvaluate = 0x65,
79
80    ScriptEvent = 0x66,
81    ScriptFileRead = 0x67,
82    ScriptFontLoading = 0x68,
83    ScriptImageCacheMsg = 0x69,
84    ScriptInputEvent = 0x6a,
85    ScriptNetworkEvent = 0x6b,
86
87    /// The script thread is parsing HTML, rather than doing other work like evaluating scripts or doing layout.
88    ScriptParseHTML = 0x6c,
89
90    ScriptPlannedNavigation = 0x6d,
91    ScriptResize = 0x6e,
92    ScriptRendering = 0x6f,
93    ScriptSetScrollState = 0x70,
94    ScriptSetViewport = 0x71,
95    ScriptTimerEvent = 0x72,
96    ScriptStylesheetLoad = 0x73,
97    ScriptUpdateReplacedElement = 0x74,
98    ScriptWebSocketEvent = 0x75,
99    ScriptWorkerEvent = 0x76,
100    ScriptServiceWorkerEvent = 0x77,
101
102    /// The script thread is parsing XML, rather than doing other work like evaluating scripts or doing layout.
103    ScriptParseXML = 0x78,
104
105    ScriptEnterFullscreen = 0x79,
106    ScriptExitFullscreen = 0x7a,
107    ScriptWorkletEvent = 0x7b,
108    ScriptGeolocationEvent = 0x7c,
109    ScriptPerformanceEvent = 0x7d,
110    ScriptHistoryEvent = 0x7e,
111    ScriptPortMessage = 0x7f,
112    ScriptWebGPUMsg = 0x80,
113
114    ScriptDatabaseAccessEvent = 0x81,
115
116    /// Web performance metrics.
117    TimeToFirstPaint = 0x90,
118    TimeToFirstContentfulPaint = 0x91,
119    TimeToInteractive = 0x92,
120    TimeToLargestContentfulPaint = 0x93,
121
122    IpcReceiver = 0x94,
123    IpcBytesReceiver = 0x95,
124}
125
126impl ProfilerCategory {
127    pub const fn variant_name(&self) -> &'static str {
128        match self {
129            ProfilerCategory::Compositing => "Compositing",
130            ProfilerCategory::Layout => "Layout",
131            ProfilerCategory::ImageSaving => "ImageSaving",
132            ProfilerCategory::ScriptAttachLayout => "ScriptAttachLayout",
133            ProfilerCategory::ScriptConstellationMsg => "ScriptConstellationMsg",
134            ProfilerCategory::ScriptDatabaseAccessEvent => "ScriptDatabaseAccessEvent",
135            ProfilerCategory::ScriptDevtoolsMsg => "ScriptDevtoolsMsg",
136            ProfilerCategory::ScriptDocumentEvent => "ScriptDocumentEvent",
137            ProfilerCategory::ScriptEvaluate => "ScriptEvaluate",
138            ProfilerCategory::ScriptEvent => "ScriptEvent",
139            ProfilerCategory::ScriptFileRead => "ScriptFileRead",
140            ProfilerCategory::ScriptFontLoading => "ScriptFontLoading",
141            ProfilerCategory::ScriptGeolocationEvent => "ScriptGeolocationEvent",
142            ProfilerCategory::ScriptImageCacheMsg => "ScriptImageCacheMsg",
143            ProfilerCategory::ScriptInputEvent => "ScriptInputEvent",
144            ProfilerCategory::ScriptNetworkEvent => "ScriptNetworkEvent",
145            ProfilerCategory::ScriptParseHTML => "ScriptParseHTML",
146            ProfilerCategory::ScriptPlannedNavigation => "ScriptPlannedNavigation",
147            ProfilerCategory::ScriptRendering => "ScriptRendering",
148            ProfilerCategory::ScriptResize => "ScriptResize",
149            ProfilerCategory::ScriptSetScrollState => "ScriptSetScrollState",
150            ProfilerCategory::ScriptSetViewport => "ScriptSetViewport",
151            ProfilerCategory::ScriptTimerEvent => "ScriptTimerEvent",
152            ProfilerCategory::ScriptStylesheetLoad => "ScriptStylesheetLoad",
153            ProfilerCategory::ScriptUpdateReplacedElement => "ScriptUpdateReplacedElement",
154            ProfilerCategory::ScriptWebSocketEvent => "ScriptWebSocketEvent",
155            ProfilerCategory::ScriptWorkerEvent => "ScriptWorkerEvent",
156            ProfilerCategory::ScriptServiceWorkerEvent => "ScriptServiceWorkerEvent",
157            ProfilerCategory::ScriptParseXML => "ScriptParseXML",
158            ProfilerCategory::ScriptEnterFullscreen => "ScriptEnterFullscreen",
159            ProfilerCategory::ScriptExitFullscreen => "ScriptExitFullscreen",
160            ProfilerCategory::ScriptWorkletEvent => "ScriptWorkletEvent",
161            ProfilerCategory::ScriptPerformanceEvent => "ScriptPerformanceEvent",
162            ProfilerCategory::ScriptHistoryEvent => "ScriptHistoryEvent",
163            ProfilerCategory::ScriptPortMessage => "ScriptPortMessage",
164            ProfilerCategory::ScriptWebGPUMsg => "ScriptWebGPUMsg",
165            ProfilerCategory::TimeToFirstPaint => "TimeToFirstPaint",
166            ProfilerCategory::TimeToFirstContentfulPaint => "TimeToFirstContentfulPaint",
167            ProfilerCategory::TimeToLargestContentfulPaint => "TimeToLargestContentfulPaint",
168            ProfilerCategory::TimeToInteractive => "TimeToInteractive",
169            ProfilerCategory::IpcReceiver => "IpcReceiver",
170            ProfilerCategory::IpcBytesReceiver => "IpcBytesReceiver",
171        }
172    }
173}
174
175#[derive(Clone, Debug, Deserialize, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize)]
176pub enum TimerMetadataFrameType {
177    RootWindow,
178    IFrame,
179}
180
181#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
182pub enum TimerMetadataReflowType {
183    Incremental,
184    FirstReflow,
185}
186
187#[cfg(feature = "tracing")]
188pub type Span = tracing::Span;
189#[cfg(not(feature = "tracing"))]
190pub type Span = ();
191
192pub fn profile<T, F>(
193    category: ProfilerCategory,
194    meta: Option<TimerMetadata>,
195    profiler_chan: ProfilerChan,
196    #[cfg(feature = "tracing")] span: Span,
197    #[cfg(not(feature = "tracing"))] _span: Span,
198    callback: F,
199) -> T
200where
201    F: FnOnce() -> T,
202{
203    let start_time = CrossProcessInstant::now();
204    let val = {
205        #[cfg(feature = "tracing")]
206        let _enter = span.enter();
207        callback()
208    };
209    let end_time = CrossProcessInstant::now();
210
211    send_profile_data(category, meta, &profiler_chan, start_time, end_time);
212    val
213}
214
215pub fn send_profile_data(
216    category: ProfilerCategory,
217    meta: Option<TimerMetadata>,
218    profiler_chan: &ProfilerChan,
219    start_time: CrossProcessInstant,
220    end_time: CrossProcessInstant,
221) {
222    profiler_chan.send(ProfilerMsg::Time((category, meta), (start_time, end_time)));
223}