use std::path::PathBuf;
use bpaf::Bpaf;
use profile_inspect::analysis::CallerCalleeAnalyzer;
use profile_inspect::classify::FrameClassifier;
use profile_inspect::output::format_time_us;
use profile_inspect::parser::CpuProfileParser;
#[derive(Debug, Clone, Bpaf)]
#[bpaf(command("explain"))]
pub struct ExplainCommand {
#[bpaf(long, argument("NAME"))]
pub function: String,
#[bpaf(positional("INPUT"))]
pub input: PathBuf,
}
impl ExplainCommand {
pub fn run(self) -> Result<(), Box<dyn std::error::Error>> {
let classifier = FrameClassifier::default();
let parser = CpuProfileParser::new(classifier);
let profile = parser.parse_file(&self.input)?;
let frame = CallerCalleeAnalyzer::find_frame_by_name(&profile, &self.function)
.ok_or_else(|| format!("Function not found: {}", self.function))?;
let analyzer = CallerCalleeAnalyzer::new();
let analysis = analyzer
.analyze(&profile, frame.id)
.ok_or("Failed to analyze function")?;
eprintln!("Function Analysis: {}", analysis.target_name);
eprintln!("Location: {}", analysis.target_location);
eprintln!();
eprintln!("Self time: {}", format_time_us(analysis.target_self_time));
eprintln!("Total time: {}", format_time_us(analysis.target_total_time));
eprintln!();
if !analysis.callers.is_empty() {
eprintln!("Top Callers:");
for caller in analysis.callers.iter().take(10) {
eprintln!(
" {} - {} ({} calls)",
caller.name,
format_time_us(caller.time),
caller.call_count
);
}
eprintln!();
}
if !analysis.callees.is_empty() {
eprintln!("Top Callees:");
for callee in analysis.callees.iter().take(10) {
eprintln!(
" {} - {} ({} calls)",
callee.name,
format_time_us(callee.self_time),
callee.call_count
);
}
}
Ok(())
}
}