crates/dwarves-tune-graph/src/main.rs
//! `dwarves-tune-graph --graph <graph.json> --profile <probe.json|oci.json> [--json]`
//!
//! The cross-check of a Graph JSON file (the POSSIBLE syscalls, written by the
//! owner's engine) with what a Heimdall probe SAW. Text by default, the
//! report document with `--json`. Exit 0 = a report, 2 = a usage or input
//! error. It guides review and tests only; it never widens a filter.
use std::path::PathBuf;
use std::process::ExitCode;
const USAGE: &str =
"usage: dwarves-tune-graph --graph <graph.json> --profile <probe.json|oci.json> [--json]";
fn fail(msg: impl std::fmt::Display) -> ExitCode {
eprintln!("dwarves-tune-graph: {msg}");
ExitCode::from(2)
}
fn main() -> ExitCode {
let mut graph: Option<PathBuf> = None;
let mut profile: Option<PathBuf> = None;
let mut json = false;
let mut binary: Option<String> = None;
let mut args = std::env::args().skip(1);
while let Some(a) = args.next() {
match a.as_str() {
"--graph" => graph = args.next().map(PathBuf::from),
"--profile" => profile = args.next().map(PathBuf::from),
"--json" => json = true,
"-h" | "--help" => {
println!("{USAGE}");
return ExitCode::SUCCESS;
}
s if s.starts_with('-') => return fail(format!("unknown flag {s}\n{USAGE}")),
s => binary = Some(s.to_string()),
}
}
let Some(graph) = graph else {
return fail(format!(
"{}: the possible syscalls come from a Graph JSON file (the owner's engine): pass --graph <graph.json>\n{USAGE}",
binary.as_deref().unwrap_or("no graph")
));
};
let Some(profile) = profile else {
return fail(format!("name the probe file: --profile <probe.json|oci.json>\n{USAGE}"));
};
let g = match dwarves_tune_graph::read_graph(&graph) {
Ok(g) => g,
Err(e) => return fail(e),
};
let s = match dwarves_tune_graph::read_seen(&profile) {
Ok(s) => s,
Err(e) => return fail(e),
};
let r = dwarves_tune_graph::cross_check(&g, &s);
if json {
println!("{}", serde_json::to_string_pretty(&r).expect("a report serializes"));
} else {
print!("{}", dwarves_tune_graph::render_text(&r));
}
ExitCode::SUCCESS
}