#![allow(
missing_docs,
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::type_complexity,
clippy::arithmetic_side_effects,
clippy::indexing_slicing
)]
use std::sync::OnceLock;
use divan::{AllocProfiler, Bencher, black_box};
use gobin::{
GoBinary,
formats::BinaryContext,
structures::{buildinfo, pclntab},
};
#[global_allocator]
static ALLOC: AllocProfiler = AllocProfiler::system();
fn main() {
divan::main();
}
const FIXTURES: &[&str] = &[
"elf126",
"elf127",
"pe127",
"macho127s",
"wasm127",
"types127",
];
fn path_of(name: &str) -> &'static str {
match name {
"elf126" => "tests/samples/basic_go126_linux_amd64",
"elf127" => "tests/samples/basic_go127_linux_amd64",
"pe127" => "tests/samples/basic_go127_windows_amd64.exe",
"macho127s" => "tests/samples/basic_go127_darwin_arm64_stripped",
"wasm127" => "tests/samples/basic_go127_wasip1_wasm",
"types127" => "tests/samples/types_go127_linux_amd64",
other => panic!("unknown fixture {other}"),
}
}
fn fixture(path: &str) -> &'static [u8] {
static CACHE: OnceLock<std::sync::Mutex<Vec<(String, &'static [u8])>>> = OnceLock::new();
let cache = CACHE.get_or_init(|| std::sync::Mutex::new(Vec::new()));
let mut guard = cache.lock().expect("fixture cache poisoned");
if let Some((_, data)) = guard.iter().find(|(p, _)| p == path) {
return data;
}
let data: &'static [u8] = Box::leak(
std::fs::read(path)
.unwrap_or_else(|e| panic!("read {path}: {e}"))
.into_boxed_slice(),
);
guard.push((path.to_string(), data));
data
}
#[divan::bench(args = FIXTURES)]
fn parse(bencher: Bencher, name: &str) {
let data = fixture(path_of(name));
bencher.bench(|| black_box(GoBinary::parse(black_box(data))).is_some());
}
#[divan::bench(args = FIXTURES)]
fn functions(bencher: Bencher, name: &str) {
let data = fixture(path_of(name));
let bin = GoBinary::parse(data).expect("fixture parses");
bencher.bench(|| black_box(bin.functions().count()));
}
#[divan::bench(args = FIXTURES)]
fn types(bencher: Bencher, name: &str) {
let data = fixture(path_of(name));
let bin = GoBinary::parse(data).expect("fixture parses");
bencher.bench(|| black_box(bin.types().count()));
}
#[divan::bench(args = FIXTURES)]
fn all_types(bencher: Bencher, name: &str) {
let data = fixture(path_of(name));
let bin = GoBinary::parse(data).expect("fixture parses");
bencher.bench(|| black_box(bin.all_types().len()));
}
#[divan::bench(args = FIXTURES)]
fn strings(bencher: Bencher, name: &str) {
let data = fixture(path_of(name));
let bin = GoBinary::parse(data).expect("fixture parses");
bencher.bench(|| black_box(bin.strings().count()));
}
#[divan::bench(args = FIXTURES)]
fn itabs(bencher: Bencher, name: &str) {
let data = fixture(path_of(name));
let bin = GoBinary::parse(data).expect("fixture parses");
bencher.bench(|| black_box(bin.itab_pairs().count()));
}
#[divan::bench(args = FIXTURES)]
fn inline_trees(bencher: Bencher, name: &str) {
let data = fixture(path_of(name));
let bin = GoBinary::parse(data).expect("fixture parses");
bencher.bench(|| {
let Some(pcl) = bin.pclntab() else { return 0 };
let mut n = 0usize;
for (_, off) in pcl.func_entries() {
if let Some(fd) = pcl.parse_func(off) {
n += bin.inline_tree(&fd).count();
}
}
black_box(n)
});
}
#[divan::bench(args = FIXTURES)]
fn stage_context(bencher: Bencher, name: &str) {
let data = fixture(path_of(name));
bencher.bench(|| {
black_box(BinaryContext::new(black_box(data)))
.sections()
.has_gopclntab
});
}
#[divan::bench(args = FIXTURES)]
fn stage_buildinfo(bencher: Bencher, name: &str) {
let data = fixture(path_of(name));
let ctx = BinaryContext::new(data);
bencher.bench(|| black_box(buildinfo::extract(black_box(&ctx))).is_some());
}
#[divan::bench(args = FIXTURES)]
fn stage_pclntab(bencher: Bencher, name: &str) {
let data = fixture(path_of(name));
let ctx = BinaryContext::new(data);
bencher.bench(|| black_box(pclntab::parse(black_box(&ctx))).is_some());
}
#[divan::bench(args = FIXTURES)]
fn init_order(bencher: Bencher, name: &str) {
let data = fixture(path_of(name));
let bin = GoBinary::parse(data).expect("fixture parses");
bencher.bench(|| black_box(bin.init_order().len()));
}
#[divan::bench(args = FIXTURES)]
fn embeds(bencher: Bencher, name: &str) {
let data = fixture(path_of(name));
let bin = GoBinary::parse(data).expect("fixture parses");
bencher.bench(|| black_box(bin.embedded_assets().len()));
}
#[divan::bench(args = FIXTURES)]
fn full_sweep(bencher: Bencher, name: &str) {
let data = fixture(path_of(name));
bencher.bench(|| {
let Some(bin) = GoBinary::parse(black_box(data)) else {
return 0usize;
};
let mut n = bin.functions().count();
n += bin.types().count();
n += bin.itab_pairs().count();
n += bin.strings().count();
n += bin.init_order().len();
n += bin.embedded_assets().len();
black_box(n)
});
}