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