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