1#![expect(dead_code)]
6
7use std::sync::{Arc, Mutex};
8use std::thread;
9use std::time::Duration;
10
11use atomic_refcell::AtomicRefCell;
12use devtools_traits::DevtoolScriptControlMsg::{DropTimelineMarkers, SetTimelineMarkers};
13use devtools_traits::{DevtoolScriptControlMsg, TimelineMarker, TimelineMarkerType};
14use malloc_size_of_derive::MallocSizeOf;
15use serde::{Serialize, Serializer};
16use serde_json::{Map, Value};
17use servo_base::cross_process_instant::CrossProcessInstant;
18use servo_base::generic_channel::{self, GenericReceiver, GenericSender};
19use servo_base::id::PipelineId;
20
21use crate::StreamId;
22use crate::actor::{Actor, ActorError, ActorRegistry};
23use crate::actors::framerate::FramerateActor;
24use crate::actors::memory::{MemoryActor, TimelineMemoryReply};
25use crate::protocol::{ClientRequest, DevtoolsConnection, JsonPacketStream};
26
27#[derive(MallocSizeOf)]
28pub(crate) struct TimelineActor {
29 name: String,
30 script_sender: GenericSender<DevtoolScriptControlMsg>,
31 marker_types: Vec<TimelineMarkerType>,
32 pipeline_id: PipelineId,
33 #[conditional_malloc_size_of]
34 is_recording: Arc<Mutex<bool>>,
35 framerate_actor: AtomicRefCell<Option<String>>,
36 memory_actor: AtomicRefCell<Option<String>>,
37 #[conditional_malloc_size_of]
38 registry: Arc<Mutex<ActorRegistry>>,
39 start_stamp: CrossProcessInstant,
40}
41
42struct Emitter {
43 from: String,
44 stream: DevtoolsConnection,
45 registry: Arc<Mutex<ActorRegistry>>,
46 start_stamp: CrossProcessInstant,
47
48 framerate_actor: Option<String>,
49 memory_actor: Option<String>,
50}
51
52#[derive(Serialize)]
53struct IsRecordingReply {
54 from: String,
55 value: bool,
56}
57
58#[derive(Serialize)]
59struct StartReply {
60 from: String,
61 value: HighResolutionStamp,
62}
63
64#[derive(Serialize)]
65struct StopReply {
66 from: String,
67 value: HighResolutionStamp,
68}
69
70#[derive(Serialize)]
71#[serde(rename_all = "camelCase")]
72struct TimelineMarkerReply {
73 name: String,
74 start: HighResolutionStamp,
75 end: HighResolutionStamp,
76 stack: Option<Vec<()>>,
77 end_stack: Option<Vec<()>>,
78}
79
80#[derive(Serialize)]
81#[serde(rename_all = "camelCase")]
82struct MarkersEmitterReply {
83 #[serde(rename = "type")]
84 type_: String,
85 markers: Vec<TimelineMarkerReply>,
86 from: String,
87 end_time: HighResolutionStamp,
88}
89
90#[derive(Serialize)]
91struct MemoryEmitterReply {
92 #[serde(rename = "type")]
93 type_: String,
94 from: String,
95 delta: HighResolutionStamp,
96 measurement: TimelineMemoryReply,
97}
98
99#[derive(Serialize)]
100struct FramerateEmitterReply {
101 #[serde(rename = "type")]
102 type_: String,
103 from: String,
104 delta: HighResolutionStamp,
105 timestamps: Vec<HighResolutionStamp>,
106}
107
108#[derive(MallocSizeOf)]
113pub(crate) struct HighResolutionStamp(f64);
114
115impl HighResolutionStamp {
116 pub fn new(start_stamp: CrossProcessInstant, time: CrossProcessInstant) -> HighResolutionStamp {
117 let duration = (time - start_stamp).whole_microseconds();
118 HighResolutionStamp(duration as f64 / 1000_f64)
119 }
120
121 pub fn wrap(time: f64) -> HighResolutionStamp {
122 HighResolutionStamp(time)
123 }
124}
125
126impl Serialize for HighResolutionStamp {
127 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
128 self.0.serialize(s)
129 }
130}
131
132static DEFAULT_TIMELINE_DATA_PULL_TIMEOUT: u64 = 200; impl TimelineActor {
135 pub fn new(
136 name: String,
137 pipeline_id: PipelineId,
138 script_sender: GenericSender<DevtoolScriptControlMsg>,
139 registry: Arc<Mutex<ActorRegistry>>,
140 ) -> TimelineActor {
141 let marker_types = vec![TimelineMarkerType::Reflow, TimelineMarkerType::DOMEvent];
142
143 TimelineActor {
144 name,
145 pipeline_id,
146 marker_types,
147 script_sender,
148 is_recording: Arc::new(Mutex::new(false)),
149 framerate_actor: AtomicRefCell::new(None),
150 memory_actor: AtomicRefCell::new(None),
151 start_stamp: CrossProcessInstant::now(),
152 registry,
153 }
154 }
155
156 fn pull_timeline_data(
157 &self,
158 receiver: GenericReceiver<Option<TimelineMarker>>,
159 mut emitter: Emitter,
160 ) {
161 let is_recording = self.is_recording.clone();
162
163 if !*is_recording.lock().unwrap() {
164 return;
165 }
166
167 thread::Builder::new()
168 .name("PullTimelineData".to_owned())
169 .spawn(move || {
170 loop {
171 if !*is_recording.lock().unwrap() {
172 break;
173 }
174
175 let mut markers = vec![];
176 while let Ok(Some(marker)) = receiver.try_recv() {
177 markers.push(emitter.marker(marker));
178 }
179 if emitter.send(markers).is_err() {
180 break;
181 }
182
183 thread::sleep(Duration::from_millis(DEFAULT_TIMELINE_DATA_PULL_TIMEOUT));
184 }
185 })
186 .expect("Thread spawning failed");
187 }
188}
189
190impl Actor for TimelineActor {
191 fn name(&self) -> String {
192 self.name.clone()
193 }
194
195 fn handle_message(
196 &self,
197 request: ClientRequest,
198 registry: &ActorRegistry,
199 msg_type: &str,
200 msg: &Map<String, Value>,
201 _id: StreamId,
202 ) -> Result<(), ActorError> {
203 match msg_type {
204 "start" => {
205 **self.is_recording.lock().as_mut().unwrap() = true;
206
207 let (tx, rx) = generic_channel::channel::<Option<TimelineMarker>>().unwrap();
208 self.script_sender
209 .send(SetTimelineMarkers(
210 self.pipeline_id,
211 self.marker_types.clone(),
212 tx,
213 ))
214 .unwrap();
215
216 if let Some(with_memory) = msg.get("withMemory") {
218 if let Some(true) = with_memory.as_bool() {
219 *self.memory_actor.borrow_mut() = Some(MemoryActor::create(registry));
220 }
221 }
222
223 if let Some(with_ticks) = msg.get("withTicks") {
225 if let Some(true) = with_ticks.as_bool() {
226 let framerate_actor = Some(FramerateActor::create(
227 registry,
228 self.pipeline_id,
229 self.script_sender.clone(),
230 ));
231 *self.framerate_actor.borrow_mut() = framerate_actor;
232 }
233 }
234
235 let emitter = Emitter::new(
236 self.name(),
237 self.registry.clone(),
238 self.start_stamp,
239 request.stream(),
240 self.memory_actor.borrow().clone(),
241 self.framerate_actor.borrow().clone(),
242 );
243
244 self.pull_timeline_data(rx, emitter);
245
246 let msg = StartReply {
247 from: self.name(),
248 value: HighResolutionStamp::new(self.start_stamp, CrossProcessInstant::now()),
249 };
250 request.reply_final(&msg)?
251 },
252
253 "stop" => {
254 let msg = StopReply {
255 from: self.name(),
256 value: HighResolutionStamp::new(self.start_stamp, CrossProcessInstant::now()),
257 };
258
259 self.script_sender
260 .send(DropTimelineMarkers(
261 self.pipeline_id,
262 self.marker_types.clone(),
263 ))
264 .unwrap();
265
266 if let Some(ref actor_name) = *self.framerate_actor.borrow() {
268 registry.remove(actor_name.clone());
269 }
270
271 if let Some(ref actor_name) = *self.memory_actor.borrow() {
272 registry.remove(actor_name.clone());
273 }
274
275 **self.is_recording.lock().as_mut().unwrap() = false;
276 request.reply_final(&msg)?
277 },
278
279 "isRecording" => {
280 let msg = IsRecordingReply {
281 from: self.name(),
282 value: *self.is_recording.lock().unwrap(),
283 };
284
285 request.reply_final(&msg)?
286 },
287
288 _ => return Err(ActorError::UnrecognizedPacketType),
289 };
290 Ok(())
291 }
292}
293
294impl Emitter {
295 pub fn new(
296 name: String,
297 registry: Arc<Mutex<ActorRegistry>>,
298 start_stamp: CrossProcessInstant,
299 stream: DevtoolsConnection,
300 memory_actor_name: Option<String>,
301 framerate_actor_name: Option<String>,
302 ) -> Emitter {
303 Emitter {
304 from: name,
305 stream,
306 registry,
307 start_stamp,
308
309 framerate_actor: framerate_actor_name,
310 memory_actor: memory_actor_name,
311 }
312 }
313
314 fn marker(&self, payload: TimelineMarker) -> TimelineMarkerReply {
315 TimelineMarkerReply {
316 name: payload.name,
317 start: HighResolutionStamp::new(self.start_stamp, payload.start_time),
318 end: HighResolutionStamp::new(self.start_stamp, payload.end_time),
319 stack: payload.start_stack,
320 end_stack: payload.end_stack,
321 }
322 }
323
324 fn send(&mut self, markers: Vec<TimelineMarkerReply>) -> Result<(), ActorError> {
325 let end_time = CrossProcessInstant::now();
326 let reply = MarkersEmitterReply {
327 type_: "markers".to_owned(),
328 markers,
329 from: self.from.clone(),
330 end_time: HighResolutionStamp::new(self.start_stamp, end_time),
331 };
332 self.stream.write_json_packet(&reply)?;
333
334 if let Some(ref actor_name) = self.framerate_actor {
335 let mut lock = self.registry.lock();
336 let registry = lock.as_mut().unwrap();
337 let framerate_actor = registry.find::<FramerateActor>(actor_name);
338 let framerate_reply = FramerateEmitterReply {
339 type_: "framerate".to_owned(),
340 from: framerate_actor.name(),
341 delta: HighResolutionStamp::new(self.start_stamp, end_time),
342 timestamps: framerate_actor.take_pending_ticks(),
343 };
344 self.stream.write_json_packet(&framerate_reply)?;
345 }
346
347 if let Some(ref actor_name) = self.memory_actor {
348 let registry = self.registry.lock().unwrap();
349 let memory_actor = registry.find::<MemoryActor>(actor_name);
350 let memory_reply = MemoryEmitterReply {
351 type_: "memory".to_owned(),
352 from: memory_actor.name(),
353 delta: HighResolutionStamp::new(self.start_stamp, end_time),
354 measurement: memory_actor.measure(),
355 };
356 self.stream.write_json_packet(&memory_reply)?;
357 }
358
359 Ok(())
360 }
361}