1use 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 Time(
40 (ProfilerCategory, Option<TimerMetadata>),
41 (CrossProcessInstant, CrossProcessInstant),
42 ),
43 Get(
45 (ProfilerCategory, Option<TimerMetadata>),
46 IpcSender<ProfilerData>,
47 ),
48 Print,
50
51 BlockedLayoutQuery(String),
53
54 Exit(IpcSender<()>),
56}
57
58#[repr(u32)]
60#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
61pub enum ProfilerCategory {
62 Compositing = 0x00,
66
67 Layout = 0x10,
69
70 ImageSaving = 0x51,
71 ScriptAttachLayout = 0x60,
72 ScriptConstellationMsg = 0x61,
73 ScriptDevtoolsMsg = 0x62,
74 ScriptDocumentEvent = 0x63,
75
76 ScriptEvaluate = 0x65,
79
80 ScriptEvent = 0x66,
81 ScriptFileRead = 0x67,
82 ScriptFontLoading = 0x68,
83 ScriptImageCacheMsg = 0x69,
84 ScriptInputEvent = 0x6a,
85 ScriptNetworkEvent = 0x6b,
86
87 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 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 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}