1use ipc_channel::ipc::IpcSender;
6use log::warn;
7use malloc_size_of_derive::MallocSizeOf;
8use serde::{Deserialize, Serialize};
9use servo_base::cross_process_instant::CrossProcessInstant;
10use servo_base::generic_channel::GenericSender;
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 Time(
43 (ProfilerCategory, Option<TimerMetadata>),
44 (CrossProcessInstant, CrossProcessInstant),
45 ),
46 Get(
48 (ProfilerCategory, Option<TimerMetadata>),
49 IpcSender<ProfilerData>,
50 ),
51 Print,
53
54 BlockedLayoutQuery(String),
56
57 Exit(IpcSender<()>),
59}
60
61#[repr(u32)]
63#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
64pub enum ProfilerCategory {
65 Painting = 0x00,
69
70 Layout = 0x10,
72
73 ImageSaving = 0x51,
74 ScriptSpawnPipeline = 0x60,
75 ScriptConstellationMsg = 0x61,
76 ScriptDevtoolsMsg = 0x62,
77 ScriptDocumentEvent = 0x63,
78
79 ScriptEvaluate = 0x65,
82
83 ScriptEvent = 0x66,
84 ScriptFileRead = 0x67,
85 ScriptFontLoading = 0x68,
86 ScriptImageCacheMsg = 0x69,
87 ScriptInputEvent = 0x6a,
88 ScriptNetworkEvent = 0x6b,
89
90 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 ScriptParseXML = 0x78,
107
108 ScriptEnterFullscreen = 0x79,
109 ScriptExitFullscreen = 0x7a,
110 ScriptWorkletEvent = 0x7b,
111 ScriptGeolocationEvent = 0x7c,
112 ScriptPerformanceEvent = 0x7d,
113 ScriptNavigationAndTraversalEvent = 0x7e,
114 ScriptPortMessage = 0x7f,
115 ScriptWebGPUMsg = 0x80,
116
117 ScriptDatabaseAccessEvent = 0x81,
118
119 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::ScriptNavigationAndTraversalEvent => {
166 "ScriptNavigationAndTraversalEvent"
167 },
168 ProfilerCategory::ScriptPortMessage => "ScriptPortMessage",
169 ProfilerCategory::ScriptWebGPUMsg => "ScriptWebGPUMsg",
170 ProfilerCategory::TimeToFirstPaint => "TimeToFirstPaint",
171 ProfilerCategory::TimeToFirstContentfulPaint => "TimeToFirstContentfulPaint",
172 ProfilerCategory::TimeToLargestContentfulPaint => "TimeToLargestContentfulPaint",
173 ProfilerCategory::TimeToInteractive => "TimeToInteractive",
174 ProfilerCategory::IpcReceiver => "IpcReceiver",
175 ProfilerCategory::IpcBytesReceiver => "IpcBytesReceiver",
176 }
177 }
178}
179
180#[derive(Clone, Debug, Deserialize, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize)]
181pub enum TimerMetadataFrameType {
182 RootWindow,
183 IFrame,
184}
185
186#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
187pub enum TimerMetadataReflowType {
188 Incremental,
189 FirstReflow,
190}
191
192#[cfg(feature = "tracing")]
193pub type Span = tracing::Span;
194#[cfg(not(feature = "tracing"))]
195pub type Span = ();
196
197pub fn profile<T, F>(
198 category: ProfilerCategory,
199 meta: Option<TimerMetadata>,
200 profiler_chan: ProfilerChan,
201 #[cfg(feature = "tracing")] span: Span,
202 #[cfg(not(feature = "tracing"))] _span: Span,
203 callback: F,
204) -> T
205where
206 F: FnOnce() -> T,
207{
208 let start_time = CrossProcessInstant::now();
209 let val = {
210 #[cfg(feature = "tracing")]
211 let _enter = span.enter();
212 callback()
213 };
214 let end_time = CrossProcessInstant::now();
215
216 send_profile_data(category, meta, &profiler_chan, start_time, end_time);
217 val
218}
219
220pub fn send_profile_data(
221 category: ProfilerCategory,
222 meta: Option<TimerMetadata>,
223 profiler_chan: &ProfilerChan,
224 start_time: CrossProcessInstant,
225 end_time: CrossProcessInstant,
226) {
227 profiler_chan.send(ProfilerMsg::Time((category, meta), (start_time, end_time)));
228}