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