use std::fs::File;
use std::io::BufWriter;
use std::path::PathBuf;
use bpaf::Bpaf;
use profile_inspect::analysis::HeapAnalyzer;
use profile_inspect::classify::FrameClassifier;
use profile_inspect::output::{OutputFormat, get_formatter};
use profile_inspect::parser::HeapProfileParser;
use super::common::{AnalysisOptions, OutputOptions};
use super::common::{analysis_options, output_options};
#[derive(Debug, Clone, Bpaf)]
#[bpaf(command("heap"))]
pub struct HeapCommand {
#[bpaf(external(output_options))]
pub output_options: OutputOptions,
#[bpaf(external(analysis_options))]
pub analysis_options: AnalysisOptions,
#[bpaf(positional("INPUT"))]
pub input: PathBuf,
}
impl HeapCommand {
pub fn run(self) -> Result<(), Box<dyn std::error::Error>> {
let classifier = FrameClassifier::default();
let parser = HeapProfileParser::new(classifier);
let profile = parser.parse_file(&self.input)?;
let analyzer = HeapAnalyzer::new()
.include_internals(self.analysis_options.include_internals)
.min_percent(self.analysis_options.min_percent)
.top_n(self.analysis_options.top);
let analysis = analyzer.analyze(&profile);
for fmt_str in &self.output_options.formats() {
let format = OutputFormat::from_str(fmt_str)
.ok_or_else(|| format!("Unknown format: {fmt_str}"))?;
let formatter = get_formatter(format);
let output_path = if let Some(dir) = &self.output_options.output {
std::fs::create_dir_all(dir)?;
let filename = match format {
OutputFormat::Markdown => "heap-analysis.md",
OutputFormat::Text => "heap-analysis.txt",
OutputFormat::Json => "heap-analysis.json",
OutputFormat::Speedscope => "heap.speedscope.json",
OutputFormat::Collapsed => "heap.collapsed.txt",
};
Some(dir.join(filename))
} else {
None
};
if let Some(path) = output_path {
let file = File::create(&path)?;
let mut writer = BufWriter::new(file);
formatter.write_heap_analysis(&profile, &analysis, &mut writer)?;
eprintln!("Wrote: {}", path.display());
} else {
let stdout = std::io::stdout();
let mut writer = stdout.lock();
formatter.write_heap_analysis(&profile, &analysis, &mut writer)?;
}
}
Ok(())
}
}