blob: e5c5e85470cb54e54662a4849ba2384069af0346 [file] [log] [blame]
//! 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()
}
}