crates/nornir-build-thing/tests/kernel_run_live.rs
//! **HEAVY, LIVE RUN — what a workload actually EXECUTED, `#[ignore]`d.**
//!
//! Design §4's loop. LTO and dead-code elimination are STATIC: they drop what
//! can never be called and know nothing about what ran. ftrace's function
//! profiler is the only instrument that does, and this drives it — boot a kernel
//! built with `profile_run = true` in QEMU inside the toolchain container, let
//! PID 1 profile a small appliance-shaped workload, and read the counters back
//! off the serial console.
//!
//! ```bash
//! export KERNEL_RUN_ROOT=/path/holding/.kernel-out
//! export KERNEL_RUN_NAME=e2e-slim-profiled
//! export KERNEL_RUN_TOOLCHAIN=localhost/nordisk-kernel-toolchain:latest
//! cargo test -p nornir-build-thing --features kernel-build \
//! --test kernel_run_live -- --ignored --nocapture
//! ```
#![cfg(feature = "kernel-build")]
use nornir_build_thing::kernel::{run_kernel_with, DraupnirKernelRunner, KernelBuild};
#[test]
#[ignore = "HEAVY: boots a profiling kernel under QEMU inside a container. Needs \
KERNEL_RUN_ROOT + KERNEL_RUN_NAME and a podman/Docker socket."]
fn a_profiled_kernel_reports_what_the_workload_executed() {
let (Ok(root), Ok(name)) =
(std::env::var("KERNEL_RUN_ROOT"), std::env::var("KERNEL_RUN_NAME"))
else {
eprintln!(
"SKIP (not a pass): set KERNEL_RUN_ROOT to the repo_root a PROFILED kernel was \
built under and KERNEL_RUN_NAME to its variant name."
);
return;
};
let mut k = KernelBuild {
name,
version: "0.0.0".into(),
tarball_sha256: "0".repeat(64),
profile_run: true,
..KernelBuild::default()
};
if let Ok(img) = std::env::var("KERNEL_RUN_TOOLCHAIN") {
k.toolchain_image = img;
k.toolchain_packages = Vec::new();
}
k.validate().expect("the run declaration");
let run = run_kernel_with(&DraupnirKernelRunner::new(), &k, std::path::Path::new(&root))
.expect("the profile run");
eprintln!(
"kernel_run_live: {} · tracing={} · {} executed of {} traceable · {} NEVER entered",
k.name,
run.tracing_path,
run.executed(),
run.traceable,
run.never_executed()
.map(|n| n.to_string())
.unwrap_or_else(|| "<unknown: no available_filter_functions>".into()),
);
for (f, h) in run.hits.iter().filter(|(_, h)| *h > 0).take(15) {
eprintln!(" {h:>8} {f}");
}
assert!(run.complete, "the counters were not read to the end");
assert!(!run.tracing_path.is_empty(), "the guest must say WHERE it read them");
// A profiled boot touches thousands of functions. A handful would mean the
// profiler was on but nothing was traced, which is a different failure from
// a small workload and must not pass as one.
// The drop candidates are the point of the loop: a run that could not say
// how many functions COULD have been traced has measured a numerator with
// no denominator.
assert!(run.traceable > run.executed() as u64, "traceable={} executed={}", run.traceable, run.executed());
// THE REPORT: which CONFIG symbols nothing under them executed.
let (never, touched) = run.unused_configs();
if run.mapping.is_empty() {
eprintln!(" (no build tree in this workdir — no symbol report)");
} else {
eprintln!(
" mapping: {} function rows · {} CONFIG symbols touched · {} NEVER touched",
run.mapping.len(),
touched,
never.len()
);
for c in never.iter().take(12) {
eprintln!(" drop candidate: {c}");
}
assert!(touched > 0, "no symbol was touched at all — the join is broken, not the kernel");
assert!(!never.is_empty(), "every claimed symbol ran, which is not credible");
}
assert!(
run.executed() > 100,
"only {} functions executed — the profiler reported almost nothing, which is not a \
small workload, it is a broken measurement",
run.executed()
);
}