use std::path::{Path, PathBuf};
use std::process::Command;
use supercov_engine::go_instrumenter::{RUNTIME_IMPORT, build_go_obligations, rewrite};
use supercov_engine::go_test_harness::{probe_array_file, synthesized_harness};
mod common;
fn go_binary() -> Option<PathBuf> {
for candidate in ["go", "/opt/homebrew/bin/go", "/usr/local/go/bin/go"] {
let path = PathBuf::from(candidate);
if Command::new(&path)
.arg("version")
.output()
.is_ok_and(|out| out.status.success())
{
return Some(path);
}
}
None
}
fn temporary(label: &str) -> PathBuf {
let root = std::env::temp_dir().join(format!(
"supercov-go-overhead-{label}-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
std::fs::create_dir_all(&root).unwrap();
root
}
fn write(root: &Path, relative: &str, contents: &str) {
let path = root.join(relative);
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(path, contents).unwrap();
}
fn bench(go: &Path, root: &Path) -> f64 {
let output = Command::new(go)
.args([
"test",
"-bench=.",
"-benchtime=1s",
"-count=5",
"-run=NONE",
"./...",
])
.current_dir(root)
.output()
.unwrap();
let text = String::from_utf8_lossy(&output.stdout);
assert!(
output.status.success(),
"benchmark failed:\n{text}{}",
String::from_utf8_lossy(&output.stderr)
);
let mut samples = text
.lines()
.filter(|line| line.starts_with("Benchmark"))
.filter_map(|line| {
let fields = line.split_whitespace().collect::<Vec<_>>();
let index = fields.iter().position(|field| *field == "ns/op")?;
fields.get(index - 1)?.replace(',', "").parse::<f64>().ok()
})
.collect::<Vec<_>>();
assert!(!samples.is_empty(), "no benchmark line in:\n{text}");
samples.sort_by(f64::total_cmp);
samples[samples.len() / 2]
}
fn measure(go: &Path, label: &str, source: &str, bench_source: &str) -> (f64, f64, usize) {
let plain = temporary(&format!("{label}-plain"));
write(&plain, "go.mod", "module example.com/plain\n\ngo 1.22\n");
write(&plain, "work.go", source);
write(&plain, "bench_test.go", bench_source);
let before = bench(go, &plain);
let instrumented = temporary(&format!("{label}-instrumented"));
let runtime =
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../runtime/go/supercov/supercov.go");
write(
&instrumented,
"go.mod",
"module example.com/probe\n\ngo 1.22\n",
);
write(
&instrumented,
"supercov/supercov.go",
&std::fs::read_to_string(runtime).expect("runtime"),
);
let mut next = 0;
let mut decisions = 0;
let obligations =
build_go_obligations("work.go", source, &mut next, &mut decisions).expect("obligations");
let local = "example.com/probe/supercov";
write(
&instrumented,
"work.go",
&rewrite(source, &obligations.edits).replace(RUNTIME_IMPORT, local),
);
write(
&instrumented,
"supercov_probes.go",
&probe_array_file("main", "__supercov", local, obligations.probes.len() + 1),
);
write(&instrumented, "bench_test.go", bench_source);
write(
&instrumented,
"supercov_generated_test.go",
&synthesized_harness(
"main",
"__supercov",
local,
obligations.probes.len() + 1,
&obligations.decision_widths,
"evidence.bin",
false,
),
);
let after = bench(go, &instrumented);
std::fs::remove_dir_all(plain).ok();
std::fs::remove_dir_all(instrumented).ok();
(before, after, obligations.probes.len())
}
const TIGHT: &str = r#"package main
func collatz(n int) int {
steps := 0
for n != 1 {
if n%2 == 0 && n > 0 {
n = n / 2
} else {
n = 3*n + 1
}
steps++
}
return steps
}
func total(limit int) int {
sum := 0
for i := 1; i < limit; i++ {
sum += collatz(i)
}
return sum
}
"#;
const TIGHT_BENCH: &str = r#"package main
import "testing"
func BenchmarkTotal(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = total(20000)
}
}
"#;
const PIPELINE: &str = r#"package main
import (
"fmt"
"sort"
"strings"
)
type Record struct {
Name string
Score int
Tags []string
}
func Normalize(raw []string) []Record {
out := make([]Record, 0, len(raw))
for _, line := range raw {
parts := strings.Split(line, ",")
if len(parts) < 2 || parts[0] == "" {
continue
}
score := 0
for _, c := range parts[1] {
if c >= '0' && c <= '9' {
score = score*10 + int(c-'0')
}
}
tags := []string{}
if len(parts) > 2 && parts[2] != "" {
tags = strings.Split(parts[2], "|")
}
out = append(out, Record{Name: strings.TrimSpace(parts[0]), Score: score, Tags: tags})
}
sort.Slice(out, func(i, j int) bool { return out[i].Score > out[j].Score })
return out
}
func Summarize(records []Record) string {
var b strings.Builder
counts := map[string]int{}
for _, r := range records {
for _, t := range r.Tags {
counts[t]++
}
}
keys := make([]string, 0, len(counts))
for k := range counts {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Fprintf(&b, "%s=%d;", k, counts[k])
}
return b.String()
}
"#;
const PIPELINE_BENCH: &str = r#"package main
import (
"fmt"
"testing"
)
func corpus() []string {
lines := make([]string, 0, 4000)
for i := 0; i < 4000; i++ {
lines = append(lines, fmt.Sprintf("name%d, %d, alpha|beta|gamma%d", i, i%997, i%13))
}
return lines
}
func BenchmarkPipeline(b *testing.B) {
raw := corpus()
for i := 0; i < b.N; i++ {
_ = Summarize(Normalize(raw))
}
}
"#;
#[test]
#[ignore = "a measurement, not a pass/fail; run with --ignored"]
fn instrumentation_overhead() {
let Some(go) = go_binary() else {
common::skip("go", "no Go toolchain on PATH");
return;
};
println!("\n workload plain instrumented overhead probes");
for (label, source, bench_source) in [
("tight numeric loop", TIGHT, TIGHT_BENCH),
("string/map/sort pipeline", PIPELINE, PIPELINE_BENCH),
] {
let (before, after, probes) = measure(&go, label, source, bench_source);
println!(
" {label:<28} {:>9.0}ns {:>13.0}ns {:>9.2}x {:>7}",
before,
after,
after / before,
probes
);
}
println!();
}