profile-inspect 0.1.2

Analyze V8 CPU and heap profiles from Node.js/Chrome DevTools
Documentation
use std::path::PathBuf;

use bpaf::Bpaf;

use profile_inspect::analysis::ProfileDiffer;
use profile_inspect::classify::FrameClassifier;
use profile_inspect::output::format_time_us;
use profile_inspect::parser::CpuProfileParser;

/// Compare two CPU profiles
#[derive(Debug, Clone, Bpaf)]
#[bpaf(command("diff"))]
pub struct DiffCommand {
    /// Minimum percentage change to report
    #[bpaf(long, fallback(1.0))]
    pub min_delta: f64,

    /// Path to "before" profile
    #[bpaf(positional("BEFORE"))]
    pub before: PathBuf,

    /// Path to "after" profile
    #[bpaf(positional("AFTER"))]
    pub after: PathBuf,
}

impl DiffCommand {
    pub fn run(self) -> Result<(), Box<dyn std::error::Error>> {
        let classifier = FrameClassifier::default();
        let parser = CpuProfileParser::new(classifier);

        let before_profile = parser.parse_file(&self.before)?;

        let classifier2 = FrameClassifier::default();
        let parser2 = CpuProfileParser::new(classifier2);
        let after_profile = parser2.parse_file(&self.after)?;

        let differ = ProfileDiffer::new().min_delta_percent(self.min_delta);
        let diff = differ.diff(&before_profile, &after_profile);

        eprintln!("Profile Comparison");
        eprintln!("==================");
        eprintln!();
        eprintln!("Before: {}", format_time_us(diff.before_total));
        eprintln!("After:  {}", format_time_us(diff.after_total));
        eprintln!("Change: {:+.1}%", diff.overall_delta_percent);
        eprintln!();

        if !diff.regressions.is_empty() {
            eprintln!("Regressions (slower):");
            for reg in diff.regressions.iter().take(10) {
                eprintln!(
                    "  {} - {:+.1}% ({} -> {})",
                    reg.name,
                    reg.delta_percent,
                    format_time_us(reg.before_time),
                    format_time_us(reg.after_time)
                );
            }
            eprintln!();
        }

        if !diff.improvements.is_empty() {
            eprintln!("Improvements (faster):");
            for imp in diff.improvements.iter().take(10) {
                eprintln!(
                    "  {} - {:.1}% ({} -> {})",
                    imp.name,
                    imp.delta_percent,
                    format_time_us(imp.before_time),
                    format_time_us(imp.after_time)
                );
            }
        }

        Ok(())
    }
}