#[cfg(test)]
use crate::assert::AssertResult;
#[cfg(test)]
use crate::scenario::Ctx;
#[cfg(test)]
use anyhow::Result;
pub use crate::scenario::flags::FlagDecl;
mod args;
mod dispatch;
mod entry;
mod eval;
mod metrics;
mod model;
mod output;
mod payload;
mod probe;
mod probe_metrics;
mod profraw;
mod runtime;
mod sidecar;
#[cfg(test)]
pub(crate) mod test_helpers;
mod timefmt;
mod topo;
#[allow(unused_imports)]
pub(crate) use args::{
extract_export_output_arg, extract_export_test_arg, extract_flags_arg, extract_probe_stack_arg,
extract_test_fn_arg, extract_topo_arg, extract_work_type_arg,
};
#[cfg(test)]
pub(crate) use sidecar::enriched_parse_error_message_for_test;
pub(crate) use sidecar::{
SidecarIoError, SidecarParseError, apply_archive_source_override, collect_sidecars,
collect_sidecars_with_errors, format_callback_profile, format_kvm_stats, format_verifier_stats,
is_run_directory, is_sidecar_filename,
};
pub use sidecar::{
SidecarResult, collect_pool, newest_run_dir, repo_is_dirty, runs_root, sidecar_dir,
};
pub use dispatch::{
analyze_sidecars, is_kernel_unavailable, is_resource_contention, ktstr_main,
ktstr_test_early_dispatch, run_ktstr_test, sanitize_kernel_label,
};
pub(crate) use entry::validate_entry_flags;
pub use entry::{
BpfMapWrite, CgroupPath, KTSTR_TESTS, KtstrTestEntry, MemSideCache, NumaDistance, NumaNode,
Scheduler, SchedulerSpec, Sysctl, Topology, TopologyConstraints, find_test,
};
pub use eval::{KernelUnavailable, ResolveSource, resolve_scheduler, resolve_test_kernel};
pub(crate) use eval::{record_skip_sidecar, run_ktstr_test_inner};
pub use metrics::{
MAX_WALK_DEPTH, WALK_TRUNCATION_SENTINEL_NAME, extract_metrics, is_truncation_sentinel_name,
walk_json_leaves,
};
pub use model::{
CleanReport, DEFAULT_MODEL, LLM_DEBUG_RESPONSES_ENV, ModelSpec, ModelStatus, OFFLINE_ENV,
ShaVerdict, clean, ensure, status,
};
pub(crate) use output::extract_panic_message;
pub use payload::{
Metric, MetricBounds, MetricCheck, MetricHint, MetricSource, MetricStream, OutputFormat,
Payload, PayloadKind, PayloadMetrics, Polarity,
};
pub(crate) use payload::{RawPayloadOutput, WireMetricHint};
pub(crate) use probe::maybe_dispatch_vm_test;
pub(crate) use probe::{
maybe_dispatch_vm_test_with_args, maybe_dispatch_vm_test_with_phase_a,
propagate_rust_env_from_cmdline, start_probe_phase_a,
};
pub use probe_metrics::{
MAX_SCAN_INDEX, ThreadLookup, count_indexed_metrics, find_metric, find_metric_u64,
flat_metrics_dump, has_metric, lookup_thread, snapshot_count, snapshot_worker_allocated,
thread_count,
};
pub use profraw::target_dir as profraw_target_dir;
pub(crate) use profraw::try_flush_profraw;
pub(crate) use timefmt::now_iso8601;
pub(crate) use topo::{TopoOverride, parse_topo_string};
#[cfg(test)]
#[allow(dead_code)] pub(crate) fn require_kernel() -> std::path::PathBuf {
match crate::find_kernel() {
Ok(Some(p)) => p,
Ok(None) => panic!(
"ktstr_test: test requires a kernel but none was found. {}",
crate::KTSTR_KERNEL_HINT
),
Err(e) => panic!("ktstr_test: kernel resolution failed: {e:#}"),
}
}
#[cfg(test)]
#[allow(dead_code)] pub(crate) fn require_vmlinux(kernel_path: &std::path::Path) -> std::path::PathBuf {
crate::vmm::find_vmlinux(kernel_path).unwrap_or_else(|| {
panic!(
"ktstr_test: no vmlinux found alongside {}. The cache entry or \
kernel build is incomplete. Rebuild with `cargo ktstr kernel \
build --force`; the specified kernel must include `vmlinux` \
alongside the boot image. {}",
kernel_path.display(),
crate::KTSTR_KERNEL_HINT,
)
})
}
#[cfg(test)]
pub(crate) fn require_binary(package: &str) -> std::path::PathBuf {
crate::build_and_find_binary(package).unwrap_or_else(|e| {
panic!(
"ktstr_test: build of `{package}` failed: {e:#}. \
Run `cargo build -p {package}` to reproduce and diagnose."
)
})
}
#[cfg(test)]
#[allow(dead_code)] pub(crate) fn require_kernel_symbols(
vmlinux_path: &std::path::Path,
) -> crate::monitor::symbols::KernelSymbols {
crate::monitor::symbols::KernelSymbols::from_vmlinux(vmlinux_path).unwrap_or_else(|e| {
panic!(
"ktstr_test: kernel symbol resolution from {} failed: {e:#}",
vmlinux_path.display(),
)
})
}
#[cfg(test)]
pub(crate) fn require_kernel_offsets(
vmlinux_path: &std::path::Path,
) -> crate::monitor::btf_offsets::KernelOffsets {
crate::monitor::btf_offsets::KernelOffsets::from_vmlinux(vmlinux_path).unwrap_or_else(|e| {
panic!(
"ktstr_test: kernel BTF resolution from {} failed: {e:#}. \
The kernel must be built with CONFIG_DEBUG_INFO_BTF=y; \
rebuild with `cargo ktstr kernel build --force` if the \
cache entry was produced without BTF.",
vmlinux_path.display(),
)
})
}
#[cfg(test)]
pub(crate) fn require_bpf_map_offsets(
vmlinux_path: &std::path::Path,
) -> crate::monitor::btf_offsets::BpfMapOffsets {
crate::monitor::btf_offsets::BpfMapOffsets::from_vmlinux(vmlinux_path).unwrap_or_else(|e| {
panic!(
"ktstr_test: BpfMapOffsets resolution from {} failed: {e:#}. \
The kernel must be built with CONFIG_DEBUG_INFO_BTF=y; \
rebuild with `cargo ktstr kernel build --force` if the \
cache entry was produced without BTF.",
vmlinux_path.display(),
)
})
}
#[cfg(test)]
pub(crate) fn require_bpf_prog_offsets(
vmlinux_path: &std::path::Path,
) -> crate::monitor::btf_offsets::BpfProgOffsets {
crate::monitor::btf_offsets::BpfProgOffsets::from_vmlinux(vmlinux_path).unwrap_or_else(|e| {
panic!(
"ktstr_test: BpfProgOffsets resolution from {} failed: {e:#}. \
The kernel must be built with CONFIG_DEBUG_INFO_BTF=y; \
rebuild with `cargo ktstr kernel build --force` if the \
cache entry was produced without BTF.",
vmlinux_path.display(),
)
})
}
#[cfg(test)]
mod tests {
use super::*;
use linkme::distributed_slice;
fn __ktstr_inner_unit_test_dummy(_ctx: &Ctx) -> Result<AssertResult> {
Ok(AssertResult::pass())
}
#[distributed_slice(KTSTR_TESTS)]
static __KTSTR_ENTRY_UNIT_TEST_DUMMY: KtstrTestEntry = KtstrTestEntry {
name: "__unit_test_dummy__",
func: __ktstr_inner_unit_test_dummy,
..KtstrTestEntry::DEFAULT
};
#[test]
fn find_test_registered_entry() {
let entry = find_test("__unit_test_dummy__");
assert!(entry.is_some(), "registered entry should be found");
let entry = entry.unwrap();
assert_eq!(entry.name, "__unit_test_dummy__");
assert_eq!(entry.topology.llcs, 1);
assert_eq!(entry.topology.cores_per_llc, 2);
}
#[test]
fn find_test_nonexistent() {
assert!(find_test("__nonexistent_test_xyz__").is_none());
}
#[test]
fn find_test_from_distributed_slice() {
assert!(!KTSTR_TESTS.is_empty());
}
}