diff --git a/tracing-trace/src/bin/trace-to-callstats.rs b/tracing-trace/src/bin/trace-to-callstats.rs new file mode 100644 index 000000000..3644b7bff --- /dev/null +++ b/tracing-trace/src/bin/trace-to-callstats.rs @@ -0,0 +1,16 @@ +use std::ffi::OsString; +use std::io::Write; + +fn main() { + let input_file = std::env::args_os().nth(1).expect("missing file"); + let input = + std::io::BufReader::new(std::fs::File::open(&input_file).expect("could not open ")); + let trace = tracing_trace::TraceReader::new(input); + let profile = tracing_trace::processor::span_stats::to_call_stats(trace).unwrap(); + let mut output_file = OsString::new(); + output_file.push("callstats-"); + output_file.push(input_file); + let mut output_file = std::io::BufWriter::new(std::fs::File::create(output_file).unwrap()); + serde_json::to_writer(&mut output_file, &profile).unwrap(); + output_file.flush().unwrap(); +} diff --git a/tracing-trace/src/processor/mod.rs b/tracing-trace/src/processor/mod.rs index a84cb3b63..ea445b0a5 100644 --- a/tracing-trace/src/processor/mod.rs +++ b/tracing-trace/src/processor/mod.rs @@ -1,2 +1,3 @@ pub mod firefox_profiler; pub mod fmt; +pub mod span_stats; diff --git a/tracing-trace/src/processor/span_stats.rs b/tracing-trace/src/processor/span_stats.rs new file mode 100644 index 000000000..63b6ae5c1 --- /dev/null +++ b/tracing-trace/src/processor/span_stats.rs @@ -0,0 +1,79 @@ +use std::collections::{BTreeMap, HashMap}; +use std::time::Duration; + +use serde::Serialize; + +use crate::entry::{Entry, NewCallsite, SpanClose, SpanEnter, SpanExit}; +use crate::{Error, TraceReader}; + +#[derive(Debug, Clone, Copy)] +enum SpanStatus { + Outside, + Inside(std::time::Duration), +} + +#[derive(Serialize)] +pub struct CallStats { + nb: usize, + ns: u64, +} + +pub fn to_call_stats( + trace: TraceReader, +) -> Result, Error> { + let mut calls = HashMap::new(); + let mut spans = HashMap::new(); + for entry in trace { + let entry = entry?; + match entry { + Entry::NewCallsite(callsite) => { + calls.insert(callsite.call_id, (callsite, vec![])); + } + Entry::NewThread(_) => {} + Entry::NewSpan(span) => { + spans.insert(span.id, (span, SpanStatus::Outside)); + } + Entry::SpanEnter(SpanEnter { id, time, memory: _ }) => { + let (_, status) = spans.get_mut(&id).unwrap(); + + let SpanStatus::Outside = status else { + continue; + }; + + *status = SpanStatus::Inside(time); + } + Entry::SpanExit(SpanExit { id, time: end, memory: _ }) => { + let (span, status) = spans.get_mut(&id).unwrap(); + + let SpanStatus::Inside(begin) = status else { + continue; + }; + let begin = *begin; + + *status = SpanStatus::Outside; + + let span = *span; + let (_, call_list) = calls.get_mut(&span.call_id).unwrap(); + call_list.push(end - begin); + } + Entry::SpanClose(SpanClose { id, time: _ }) => { + spans.remove(&id); + } + Entry::Event(_) => {} + } + } + + Ok(calls + .into_iter() + .map(|(_, (call_site, calls))| (site_to_string(call_site), calls_to_stats(calls))) + .collect()) +} + +fn site_to_string(call_site: NewCallsite) -> String { + format!("{}::{}", call_site.target, call_site.name) +} +fn calls_to_stats(calls: Vec) -> CallStats { + let nb = calls.len(); + let sum: Duration = calls.iter().sum(); + CallStats { nb, ns: sum.as_nanos() as u64 } +}