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