crates/mimir-binary/src/main.rs
//! `mimir-binary` — join one linked artifact onto a mimir-driver dump.
//!
//! ```text
//! mimir-binary --bin PATH --dump DIR [--extra-dump DIR]… [--deps-dir DIR] [--manifest-path P]
//! [--sysroot DIR | --no-sysroot] [--ignore-driver-symbols]
//! [--out DIR] [--sha SHA]
//! ```
//!
//! `mimir-binary --cat FILE.arrow` prints any of the IPC files as TSV.
//!
//! Writes `<out>/<bin>.binary.{nodes,edges,symbols}.arrow` and
//! `<out>/<bin>.binary.ledger.json`, prints the ledger summary.
use std::path::PathBuf;
use std::process::ExitCode;
const USAGE: &str = "usage: mimir-binary --bin PATH --dump DIR [--extra-dump DIR]... [--deps-dir DIR] [--manifest-path P] [--sysroot DIR | --no-sysroot] [--ignore-driver-symbols] [--out DIR] [--sha SHA]";
fn main() -> ExitCode {
let mut opts = mimir_binary::Options::default();
let mut out: Option<PathBuf> = None;
let mut no_sysroot = false;
let mut it = std::env::args().skip(1);
while let Some(a) = it.next() {
match a.as_str() {
"--bin" => opts.binary = it.next().map(PathBuf::from).unwrap_or_default(),
"--dump" => opts.dump = it.next().map(PathBuf::from).unwrap_or_default(),
"--extra-dump" => opts.extra_dumps.extend(it.next().map(PathBuf::from)),
"--deps-dir" => opts.deps_dir = it.next().map(PathBuf::from),
"--manifest-path" => opts.manifest = it.next().map(PathBuf::from),
"--out" => out = it.next().map(PathBuf::from),
"--sha" => opts.sha = it.next(),
"--sysroot" => opts.sysroot = it.next().map(PathBuf::from),
"--no-sysroot" => no_sysroot = true,
"--ignore-driver-symbols" => opts.ignore_driver_symbols = true,
"--cat" => {
// print one Arrow IPC file as TSV
let Some(f) = it.next() else { return ExitCode::from(2) };
return match mimir_binary::read_table(std::path::Path::new(&f)) {
Ok((cols, rows)) => {
println!("{}", cols.join("\t"));
for r in rows {
println!("{}", r.join("\t"));
}
ExitCode::SUCCESS
}
Err(e) => {
eprintln!("{e}");
ExitCode::FAILURE
}
};
}
"-h" | "--help" => {
eprintln!("{USAGE}");
return ExitCode::SUCCESS;
}
other => {
eprintln!("mimir-binary: unknown argument {other}\n{USAGE}");
return ExitCode::from(2);
}
}
}
if opts.binary.as_os_str().is_empty() || opts.dump.as_os_str().is_empty() {
eprintln!("{USAGE}");
return ExitCode::from(2);
}
// default: the sysroot of the `rustc` on PATH (the toolchain that most
// likely linked the artifact; a wrong one simply yields no id hits)
if opts.sysroot.is_none() && !no_sysroot {
opts.sysroot = std::process::Command::new("rustc")
.args(["--print", "sysroot"])
.output()
.ok()
.filter(|o| o.status.success())
.map(|o| PathBuf::from(String::from_utf8_lossy(&o.stdout).trim()));
}
let o = match mimir_binary::run(&opts) {
Ok(o) => o,
Err(e) => {
eprintln!("mimir-binary: {e}");
return ExitCode::FAILURE;
}
};
let out = out.unwrap_or_else(|| opts.dump.clone());
let stem = opts.binary.file_name().map(|s| s.to_string_lossy().into_owned()).unwrap_or_else(|| "artifact".into());
if let Err(e) = mimir_binary::write(&out, &stem, &o) {
eprintln!("mimir-binary: write: {e}");
return ExitCode::FAILURE;
}
let l = &o.ledger;
eprintln!("── mimir-binary ───────────────────────────────────────");
eprintln!("artifact {} ({} bytes, .text {}, dwarf {}, {})", l.binary, l.file_size, l.text_size, l.has_dwarf, l.symbol_table);
eprintln!("crate ids known {:?} (cu crates {}, deps rlibs {}, sysroot rlibs {}, conflicts {})", l.crate_ids_known, l.cu_crates, l.deps_rlibs, l.sysroot_rlibs, l.crate_id_conflicts.len());
eprintln!("symbols defined {} ({} bytes), undefined {}", l.defined, l.defined_bytes, l.undefined);
eprintln!("mangling {:?}", l.by_mangling);
eprintln!("crate via {:?}", l.crate_via);
eprintln!("ambiguous crates {:?}", l.ambiguous_crates);
eprintln!("unknown crates {:?}", l.unknown_crates);
eprintln!("matched {:?} (ambiguous exact {}, loose {})", l.matched, l.ambiguous_exact, l.ambiguous_loose);
eprintln!("roles {:?}", l.roles);
for (o, c) in &l.by_origin {
eprintln!("origin {o:<9} {} symbols, {} bytes, matched to source {} ({:.1}%)", c.symbols, c.bytes, c.matched, if c.symbols == 0 { 0.0 } else { c.matched as f64 * 100.0 / c.symbols as f64 });
}
eprintln!("imports {:?}", l.imports);
eprintln!("decode errors {:?}", l.decode_errors);
eprintln!("nodes both {} binary_only {} (mono_instance {})", l.nodes_both, l.nodes_binary_only, l.nodes_mono_instance);
eprintln!("INSTANCE_OF {} (dangling {})", l.edges_instance_of, l.instance_of_dangling);
eprintln!("moniker coverage {}% of defined, {}% of Rust; source match {}% of Rust", l.pct_moniker_of_defined, l.pct_moniker_of_rust, l.pct_source_match_of_rust);
eprintln!("source_only {} of {} symbol-bearing items in linked crates", l.source_only_items, l.source_items_in_linked_crates);
eprintln!("bytes by crate (top 15):");
for (k, b, n) in l.bytes_by_crate.iter().take(15) {
eprintln!(" {k:<48} {b:>10} {n:>6} syms");
}
eprintln!("time read {} ms, join {} ms", l.ms_read, l.ms_join);
eprintln!("out {}", out.display());
ExitCode::SUCCESS
}