pub fn run(target: Option<&str>, output: Option<&str>) -> anyhow::Result<()> {
let out = output.unwrap_or("profile_output");
println!("rok profile — profiling integration");
println!("{}", "─".repeat(50));
let flamegraph = has_tool("flamegraph");
let perf = has_tool("perf");
let instruments = has_tool("instruments");
let heaptrack = has_tool("heaptrack");
let valgrind = has_tool("valgrind");
if !flamegraph && !perf && !instruments && !heaptrack && !valgrind {
println!(" No profiler found. Install one of:");
println!(" CPU profiling:");
println!(" cargo flamegraph cargo install flamegraph");
println!(" perf (Linux, via package manager)");
println!(" Instruments (macOS, via Xcode)");
println!();
println!(" Memory profiling:");
println!(" heaptrack (Linux, via package manager)");
println!(" valgrind (Linux/macOS, via package manager)");
return Ok(());
}
println!(" Available profilers:");
if flamegraph { println!(" ✓ cargo-flamegraph (CPU)"); }
if perf { println!(" ✓ perf (CPU, Linux)"); }
if instruments{ println!(" ✓ Instruments (CPU/memory, macOS)"); }
if heaptrack { println!(" ✓ heaptrack (memory, Linux)"); }
if valgrind { println!(" ✓ valgrind/massif (memory)"); }
println!();
if let Some(t) = target {
if flamegraph {
println!(" Running: cargo flamegraph --bin {t} -o {out}.svg");
let status = std::process::Command::new("cargo")
.args(["flamegraph", "--bin", t, "-o", &format!("{out}.svg")])
.status()?;
if status.success() {
println!(" ✓ flamegraph saved to {out}.svg");
} else {
println!(" ✗ flamegraph failed (exit {status})");
}
} else {
println!(" Hint: cargo install flamegraph then rok profile --target {t}");
}
} else {
println!(" Usage: rok profile --target <binary-name> [--output <file>]");
println!(" Example: rok profile --target axum_app");
}
Ok(())
}
fn has_tool(name: &str) -> bool {
let cmd = if name == "flamegraph" {
std::process::Command::new("cargo").args(["flamegraph", "--help"]).output()
} else {
std::process::Command::new(name).arg("--version").output()
};
cmd.map(|o| o.status.success() || !o.stderr.is_empty()).unwrap_or(false)
}