| //! Chrome trace output. |
| |
| use std::borrow::Cow; |
| use std::fs::File; |
| use std::io::{self, BufWriter, Write}; |
| use std::sync::atomic::AtomicBool; |
| use std::time::Instant; |
| |
| use crate::concurrent_linked_list::ConcurrentLinkedList; |
| |
| static TRACE: ConcurrentLinkedList<TraceEvent> = ConcurrentLinkedList::new(); |
| static ENABLED: AtomicBool = AtomicBool::new(false); |
| |
| |
| #[non_exhaustive] // potentially add instant later |
| pub enum TraceEvent { |
| Complete(CompleteEvent), |
| } |
| |
| impl TraceEvent { |
| pub fn write_json(&self, w: &mut dyn std::io::Write, start: Instant) -> io::Result<()> { |
| match self { |
| TraceEvent::Complete(e) => { |
| write!( |
| w, |
| "{{\"pid\":0, \"name\":{:?}, \"ts\":{}, \"tid\": {}, \"ph\":\"X\", \"dur\":{}}}", |
| e.name, |
| e.start.duration_since(start).as_micros(), |
| e.tid, |
| e.end.duration_since(e.start).as_micros() |
| ) |
| }, |
| } |
| } |
| } |
| |
| pub struct CompleteEvent { |
| name: Cow<'static, str>, |
| tid: usize, |
| start: Instant, |
| end: Instant, |
| } |
| |
| impl CompleteEvent { |
| pub fn new(name: &'static str, tid: usize, start: Instant, end: Instant) -> Self { |
| Self { name: Cow::Borrowed(name), tid, start, end } |
| } |
| pub fn new_owned(name: String, tid: usize, start: Instant, end: Instant) -> Self { |
| Self { name: Cow::Owned(name), tid, start, end } |
| } |
| } |
| |
| pub fn enable_tracing() { |
| ENABLED.store(true, std::sync::atomic::Ordering::Relaxed); |
| } |
| |
| pub fn trace(event: TraceEvent) { |
| if is_enabled() { |
| TRACE.prepend(event); |
| } |
| } |
| |
| pub fn write_trace_file(path: &str, start: Instant) -> std::io::Result<()> { |
| if !is_enabled() { |
| return Ok(()) |
| } |
| let mut w = BufWriter::new(File::create(path)?); |
| writeln!(w, "[")?; |
| for (i, event) in TRACE.iter().enumerate() { |
| if i > 0 { |
| write!(w, ",")?; |
| } |
| event.write_json(&mut w, start)?; |
| writeln!(&mut w)?; |
| } |
| writeln!(w, "]")?; |
| Ok(()) |
| } |
| |
| #[inline] |
| pub fn is_enabled() -> bool { |
| ENABLED.load(std::sync::atomic::Ordering::Relaxed) |
| } |
| |
| #[inline] |
| pub fn scope<T>(name: &'static str, f: impl FnOnce() -> T) -> T { |
| if is_enabled() { |
| let start = Instant::now(); |
| let result = f(); |
| let end = Instant::now(); |
| TRACE.prepend(TraceEvent::Complete(CompleteEvent::new(name, 0, start, end))); |
| result |
| } else { |
| f() |
| } |
| } |