# Objective
- fix new clippy lints before they get stable and break CI
## Solution
- run `clippy --fix` to auto-fix machine-applicable lints
- silence `clippy::should_implement_trait` for `fn HandleId::default<T: Asset>`
## Changes
- always prefer `format!("{inline}")` over `format!("{}", not_inline)`
- prefer `Box::default` (or `Box::<T>::default` if necessary) over `Box::new(T::default())`
163 lines
4.5 KiB
Rust
163 lines
4.5 KiB
Rust
//! helper to extract span stats from a chrome trace file
|
|
//! spec: <https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#heading=h.puwqg050lyuy>
|
|
|
|
use std::ops::Div;
|
|
|
|
use clap::Parser;
|
|
use parse::read_trace;
|
|
use regex::Regex;
|
|
use termcolor::{ColorChoice, StandardStream};
|
|
|
|
use crate::pretty::{print_spanstats, set_bold, simplify_name};
|
|
|
|
mod parse;
|
|
mod pretty;
|
|
|
|
#[derive(Parser, Debug)]
|
|
struct Args {
|
|
#[arg(short, long, default_value_t = 0.0)]
|
|
/// Filter spans that have an average shorther than the threshold
|
|
threshold: f32,
|
|
|
|
#[arg(short, long)]
|
|
/// Filter spans by name matching the pattern
|
|
pattern: Option<Regex>,
|
|
|
|
#[arg(short, long)]
|
|
/// Simplify system names
|
|
short: bool,
|
|
|
|
trace: String,
|
|
/// Optional, second trace to compare
|
|
second_trace: Option<String>,
|
|
}
|
|
|
|
fn main() {
|
|
let cli = Args::parse();
|
|
|
|
// Setup stdout to support colors
|
|
let mut stdout = StandardStream::stdout(ColorChoice::Auto);
|
|
|
|
// Read the first trace file
|
|
let reference = read_trace(cli.trace);
|
|
if let Some(comparison) = cli.second_trace {
|
|
// Read the second trace file
|
|
let mut comparison = read_trace(comparison);
|
|
|
|
reference
|
|
.iter()
|
|
.filter(|(_, stats)| filter_by_threshold(stats, cli.threshold))
|
|
.filter(|(name, _)| filter_by_pattern(name, cli.pattern.as_ref()))
|
|
.for_each(|(span, reference)| {
|
|
// for each span in the first trace
|
|
set_bold(&mut stdout, true);
|
|
if cli.short {
|
|
println!("{}", simplify_name(span));
|
|
} else {
|
|
println!("{span}");
|
|
}
|
|
set_bold(&mut stdout, false);
|
|
print!(" ");
|
|
let comparison = comparison.remove(span);
|
|
print_spanstats(&mut stdout, Some(reference), comparison.as_ref(), false);
|
|
});
|
|
comparison
|
|
.iter()
|
|
.filter(|(_, stats)| filter_by_threshold(stats, cli.threshold))
|
|
.filter(|(name, _)| filter_by_pattern(name, cli.pattern.as_ref()))
|
|
.for_each(|(span, comparison)| {
|
|
// print the spans only present in the second trace
|
|
set_bold(&mut stdout, true);
|
|
if cli.short {
|
|
println!("{}", simplify_name(span));
|
|
} else {
|
|
println!("{span}");
|
|
}
|
|
set_bold(&mut stdout, false);
|
|
print!(" ");
|
|
print_spanstats(&mut stdout, None, Some(comparison), false);
|
|
});
|
|
} else {
|
|
// just print stats from the first trace
|
|
reference
|
|
.iter()
|
|
.filter(|(_, stats)| filter_by_threshold(stats, cli.threshold))
|
|
.filter(|(name, _)| filter_by_pattern(name, cli.pattern.as_ref()))
|
|
.for_each(|(span, reference)| {
|
|
set_bold(&mut stdout, true);
|
|
if cli.short {
|
|
println!("{}", simplify_name(span));
|
|
} else {
|
|
println!("{span}");
|
|
}
|
|
set_bold(&mut stdout, false);
|
|
print!(" ");
|
|
print_spanstats(&mut stdout, Some(reference), None, true);
|
|
});
|
|
}
|
|
}
|
|
|
|
fn filter_by_threshold(span_stats: &SpanStats, threshold: f32) -> bool {
|
|
span_stats.avg > threshold
|
|
}
|
|
|
|
fn filter_by_pattern(name: &str, pattern: Option<&Regex>) -> bool {
|
|
if let Some(pattern) = pattern {
|
|
pattern.is_match(name)
|
|
} else {
|
|
true
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct SpanStats {
|
|
pub count: usize,
|
|
pub avg: f32,
|
|
pub min: f32,
|
|
pub max: f32,
|
|
}
|
|
|
|
impl Default for SpanStats {
|
|
fn default() -> Self {
|
|
Self {
|
|
count: 0,
|
|
avg: 0.0,
|
|
min: f32::MAX,
|
|
max: 0.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SpanStats {
|
|
fn add_span(&mut self, duration: f32) {
|
|
if duration < self.min {
|
|
self.min = duration;
|
|
}
|
|
if duration > self.max {
|
|
self.max = duration;
|
|
}
|
|
self.avg = (self.avg * self.count as f32 + duration) / (self.count as f32 + 1.0);
|
|
self.count += 1;
|
|
}
|
|
}
|
|
|
|
pub struct SpanRelative {
|
|
count: f32,
|
|
avg: f32,
|
|
min: f32,
|
|
max: f32,
|
|
}
|
|
|
|
impl Div for &SpanStats {
|
|
type Output = SpanRelative;
|
|
|
|
fn div(self, rhs: Self) -> Self::Output {
|
|
Self::Output {
|
|
count: self.count as f32 / rhs.count as f32,
|
|
avg: self.avg / rhs.avg,
|
|
min: self.min / rhs.min,
|
|
max: self.max / rhs.max,
|
|
}
|
|
}
|
|
}
|