1#![feature(thread_id_value)]
14
15use std::borrow::Cow;
16use std::marker::PhantomData;
17use std::str::FromStr;
18use std::sync::{Arc, Mutex};
19use std::{collections::HashMap, io, time::SystemTime};
20
21use crossbeam_queue::ArrayQueue;
22use derivative::Derivative;
23use derive_builder::Builder;
24use serde::{Deserialize, Serialize};
25use strum::AsRefStr;
26use strum_macros::EnumString;
27use tracing::Subscriber;
28use tracing::{span, Event};
29use tracing_subscriber::{fmt::MakeWriter, layer::Context, registry::LookupSpan, Layer};
30
31#[derive(Debug, Copy, Clone, Default, EnumString, AsRefStr, Serialize, Deserialize, PartialEq)]
32pub enum EventType {
33 #[serde(rename = "B")]
34 DurationBegin,
35 #[serde(rename = "E")]
36 DurationEnd,
37 #[serde(rename = "X")]
38 Complete,
39 #[default]
40 #[serde(rename = "i")]
41 Instant,
42 #[serde(rename = "C")]
43 Counter,
44 #[serde(rename = "b")]
45 AsyncStart,
46 #[serde(rename = "n")]
47 AsyncInstant,
48 #[serde(rename = "e")]
49 AsyncEnd,
50 #[serde(rename = "s")]
51 FlowStart,
52 #[serde(rename = "t")]
53 FlowStep,
54 #[serde(rename = "f")]
55 FlowEnd,
56 #[serde(rename = "p")]
57 Sample,
58 #[serde(rename = "N")]
59 ObjectCreated,
60 #[serde(rename = "O")]
61 ObjectSnapshot,
62 #[serde(rename = "D")]
63 ObjectDestroyed,
64 #[serde(rename = "M")]
65 Metadata,
66 #[serde(rename = "V")]
67 MemoryDumpGlobal,
68 #[serde(rename = "v")]
69 MemoryDumpProcess,
70 #[serde(rename = "R")]
71 Mark,
72 #[serde(rename = "c")]
73 ClockSync,
74 #[serde(rename = "(")]
75 ContextBegin,
76 #[serde(rename = ")")]
77 ContextEnd,
78}
79
80#[derive(Derivative, Serialize, Deserialize, Builder, Debug)]
81#[derivative(PartialEq)]
82#[builder(custom_constructor)]
83#[builder(derive(Debug))]
84pub struct ChromeEvent {
85 #[builder(setter(custom))]
86 #[serde(default = "SystemTime::now")]
87 #[serde(skip)]
88 #[allow(unused)]
89 #[derivative(PartialEq = "ignore")]
90 start: SystemTime,
91 #[builder(default)]
92 #[builder(setter(into))]
93 pub name: Cow<'static, str>,
94 #[builder(default)]
95 #[builder(setter(into))]
96 pub cat: Cow<'static, str>,
97 #[builder(default)]
98 pub ph: EventType,
99 #[builder(
100 default = "SystemTime::now().duration_since(self.start.unwrap()).unwrap().as_nanos() as f64 / 1000.0"
101 )]
102 pub ts: f64,
103 #[builder(default)]
104 #[serde(skip_serializing_if = "Option::is_none")]
105 pub dur: Option<f64>,
106 #[serde(skip_serializing_if = "Option::is_none")]
107 #[builder(default)]
108 pub tts: Option<f64>,
109 #[builder(default)]
110 #[builder(setter(into))]
111 #[serde(default, skip_serializing_if = "str::is_empty")]
112 pub id: Cow<'static, str>,
113 #[builder(default = "std::process::id().into()")]
114 pub pid: u64,
115 #[builder(default = "std::thread::current().id().as_u64().into()")]
116 pub tid: u64,
117 #[builder(default, setter(each = "arg"))]
118 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
119 pub args: HashMap<String, String>,
120}
121
122impl ChromeEvent {
123 pub fn builder(start: SystemTime) -> ChromeEventBuilder {
124 ChromeEventBuilder {
125 start: Some(start),
126 ..ChromeEventBuilder::create_empty()
127 }
128 }
129}
130
131#[derive(Debug)]
132pub struct ChromeLayer<S, W = fn() -> std::io::Stdout> {
133 pub start: SystemTime,
134 make_writer: W,
135 events: Arc<Mutex<ArrayQueue<String>>>,
136 _inner: PhantomData<S>,
137}
138
139#[derive(Clone, Debug)]
140pub struct ChromeWriter<W>
141where
142 W: Clone + for<'writer> MakeWriter<'writer> + 'static,
143{
144 make_writer: W,
145}
146
147impl<W> ChromeWriter<W>
148where
149 W: Clone + for<'writer> MakeWriter<'writer> + 'static,
150{
151 fn new(make_writer: W, events: Arc<Mutex<ArrayQueue<String>>>) -> (Self, ChromeWriterGuard<W>) {
152 (
153 Self {
154 make_writer: make_writer.clone(),
155 },
156 ChromeWriterGuard::new(make_writer, events),
157 )
158 }
159}
160
161impl<W> io::Write for ChromeWriter<W>
162where
163 W: Clone + for<'writer> MakeWriter<'writer> + 'static,
164{
165 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
166 let mut writer = self.make_writer.make_writer();
167 io::Write::write(&mut writer, buf)
168 }
169
170 fn flush(&mut self) -> io::Result<()> {
171 Ok(())
172 }
173}
174
175impl<'a, W> MakeWriter<'a> for ChromeWriter<W>
176where
177 W: Clone + for<'writer> MakeWriter<'writer> + 'static,
178{
179 type Writer = ChromeWriter<W>;
180
181 fn make_writer(&'a self) -> Self::Writer {
182 self.clone()
183 }
184}
185
186pub struct ChromeWriterGuard<W>
187where
188 W: Clone + for<'writer> MakeWriter<'writer> + 'static,
189{
190 make_writer: W,
191 events: Arc<Mutex<ArrayQueue<String>>>,
192}
193
194impl<W> ChromeWriterGuard<W>
195where
196 W: Clone + for<'writer> MakeWriter<'writer> + 'static,
197{
198 fn new(make_writer: W, events: Arc<Mutex<ArrayQueue<String>>>) -> Self {
199 io::Write::write_all(&mut make_writer.make_writer(), b"[\n").unwrap();
201
202 Self {
203 make_writer,
204 events,
205 }
206 }
207}
208
209impl<W> Drop for ChromeWriterGuard<W>
210where
211 W: Clone + for<'writer> MakeWriter<'writer> + 'static,
212{
213 fn drop(&mut self) {
214 let mut writer = self.make_writer.make_writer();
215
216 let mut write = |event: String, is_last: bool| {
217 let mut buf = String::with_capacity(event.len() + 1 + 1 );
218 buf.push_str(&event);
219 if !is_last {
220 buf.push(',');
221 }
222 buf.push('\n');
223
224 io::Write::write_all(&mut writer, buf.as_bytes()).unwrap();
225 };
226
227 if let Ok(lock) = self.events.lock() {
228 let events = &*lock;
229 while events.len() > 1 {
231 write(events.pop().unwrap(), false);
232 }
233 if let Some(event) = events.pop() {
235 write(event, true);
236 }
237 }
238
239 io::Write::write_all(&mut writer, b"]\n").unwrap();
241 }
242}
243
244impl<S, W> ChromeLayer<S, W>
245where
246 W: Clone + for<'writer> MakeWriter<'writer> + 'static,
247{
248 pub fn with_writer(make_writer: W) -> (ChromeLayer<S, ChromeWriter<W>>, ChromeWriterGuard<W>) {
249 let events = Arc::new(Mutex::new(ArrayQueue::new(1)));
250 let (make_writer, guard) = ChromeWriter::new(make_writer, events.clone());
251 (
252 ChromeLayer {
253 start: SystemTime::now(),
254 make_writer,
255 events,
256 _inner: PhantomData,
257 },
258 guard,
259 )
260 }
261
262 fn write(&self, writer: &mut dyn io::Write, event: ChromeEvent) -> io::Result<()> {
263 let current = serde_json::to_string(&event).unwrap();
264
265 if let Some(event) = self.events.lock().unwrap().force_push(current) {
267 let mut buf = String::with_capacity(
268 event.len() + 1 + 1 + 1, );
270 buf.push_str(&event);
271 buf.push(',');
272 buf.push('\n');
273 io::Write::write_all(writer, buf.as_bytes())
274 } else {
275 Ok(())
276 }
277 }
278}
279
280struct ChromeEventVisitor {
281 builder: ChromeEventBuilder,
282 event: Option<String>,
283}
284
285impl tracing_subscriber::field::Visit for ChromeEventVisitor {
286 fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
287 let value = format!("{value:?}").trim_matches('"').to_string();
288 let name = field.name();
289
290 match name {
291 "name" => {
292 self.builder.name(value);
293 }
294 "cat" => {
295 self.builder.cat(value);
296 }
297 "id" => {
298 self.builder.id(value);
299 }
300 "ph" => {
301 self.builder.ph(EventType::from_str(&value)
302 .unwrap_or_else(|_| panic!("Invalid EventType: {}", value)));
303 }
304 "ts" => {
305 self.builder.ts(value.parse().expect("Invalid timestamp"));
306 }
307 "dur" => {
308 self.builder
309 .dur(Some(value.parse().expect("Invalid timestamp")));
310 }
311 "tts" => {
312 self.builder
313 .tts(Some(value.parse().expect("Invalid timestamp")));
314 }
315 "pid" => {
316 self.builder.pid(value.parse().unwrap());
317 }
318 "tid" => {
319 self.builder.tid(value.parse().unwrap());
320 }
321 "event" => {
322 self.event = Some(value);
324 }
325 arg => {
326 self.builder.arg((arg.to_string(), value));
327 }
328 }
329 }
330}
331
332struct AsyncEntered(bool);
333
334impl<S, W> Layer<S> for ChromeLayer<S, W>
335where
336 S: Subscriber + for<'span> LookupSpan<'span>,
337 W: Clone + for<'writer> MakeWriter<'writer> + 'static,
338{
339 fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
340 let span = ctx.span(id).expect("Span not found, this is a bug");
341
342 let mut visitor = ChromeEventVisitor {
343 builder: ChromeEvent::builder(self.start),
344 event: None,
345 };
346 attrs.record(&mut visitor);
347
348 span.extensions_mut().insert(visitor);
349 }
350
351 fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
352 let mut visitor = ChromeEventVisitor {
353 builder: ChromeEvent::builder(self.start),
354 event: None,
355 };
356
357 visitor.builder.ph(EventType::Instant);
359
360 event.record(&mut visitor);
361
362 self.write(
363 &mut self.make_writer.make_writer(),
364 visitor.builder.build().unwrap(),
365 )
366 .expect("Failed to write event in tracing-chrometrace");
367 }
368
369 fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
370 let span = ctx.span(id).expect("Span not found, this is a bug");
371
372 let mut extensions = span.extensions_mut();
373
374 if extensions.get_mut::<AsyncEntered>().is_some() {
375 return;
377 } else {
378 extensions.insert(AsyncEntered(true));
379 }
380
381 if let Some(visitor) = extensions.get_mut::<ChromeEventVisitor>() {
382 if visitor
384 .event
385 .as_ref()
386 .map(|event| event == "async")
387 .unwrap_or(false)
388 {
389 visitor.builder.ph(EventType::AsyncStart);
390 } else {
391 visitor.builder.ph(EventType::DurationBegin);
392 }
393
394 self.write(
395 &mut self.make_writer.make_writer(),
396 visitor.builder.build().unwrap(),
397 )
398 .expect("Failed to write event in tracing-chrometrace");
399 }
400 }
401
402 fn on_exit(&self, _id: &span::Id, _ctx: Context<'_, S>) {}
403
404 fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
405 let span = ctx.span(&id).expect("Span not found, this is a bug");
406
407 let mut extensions = span.extensions_mut();
408
409 if let Some(visitor) = extensions.get_mut::<ChromeEventVisitor>() {
410 if visitor
411 .event
412 .as_ref()
413 .map(|event| event == "async")
414 .unwrap_or(false)
415 {
416 visitor.builder.ph(EventType::AsyncEnd);
417 } else {
418 visitor.builder.ph(EventType::DurationEnd);
419 }
420
421 self.write(
422 &mut self.make_writer.make_writer(),
423 visitor.builder.build().unwrap(),
424 )
425 .expect("Failed to write event in tracing-chrometrace");
426 }
427 }
428}
429
430#[cfg(test)]
431mod tests {
432 use super::*;
433
434 #[test]
435 fn event_stringify() {
436 let event = EventType::from_str("DurationBegin").unwrap();
437 matches!(event, EventType::DurationBegin);
438 }
439
440 #[test]
441 fn test_serde() {
442 let event = ChromeEvent::builder(SystemTime::now())
443 .arg(("a".to_string(), "a".to_string()))
444 .ts(1.0)
445 .build()
446 .unwrap();
447
448 let serialized = serde_json::to_string(&event).unwrap();
449
450 let deserialized: ChromeEvent = serde_json::from_str(&serialized).unwrap();
451
452 assert_eq!(event, deserialized);
453 }
454}