use anyhow::Result;
use ktstr::assert::{AssertDetail, AssertResult, DetailKind};
use ktstr::ktstr_test;
use ktstr::scenario::Ctx;
use ktstr::scenario::ops::{CgroupDef, HoldSpec, Step, execute_steps};
#[ktstr_test(llcs = 1, cores = 2, threads = 1, duration_s = 3)]
fn ctprof_capture_returns_threads_with_nonzero_counters(ctx: &Ctx) -> Result<AssertResult> {
let steps = vec![Step {
setup: vec![CgroupDef::named("cg_0").workers(ctx.workers_per_cgroup)].into(),
ops: vec![],
hold: HoldSpec::FULL,
}];
let workload_result = execute_steps(ctx, steps)?;
let snap = ktstr::ctprof::capture();
if snap.threads.is_empty() {
return Ok(AssertResult::fail(AssertDetail::new(
DetailKind::Other,
"ctprof::capture() returned zero threads — procfs \
walk produced no entries, indicating the capture layer \
is not reading /proc successfully inside the guest",
)));
}
let active_threads = snap
.threads
.iter()
.filter(|t| {
t.run_time_ns.0 > 0
|| t.voluntary_csw.0 > 0
|| t.nonvoluntary_csw.0 > 0
|| t.nr_wakeups.0 > 0
|| t.timeslices.0 > 0
|| t.minflt.0 > 0
})
.count();
if active_threads == 0 {
let any_comm_nonempty = snap.threads.iter().any(|t| !t.comm.is_empty());
let any_tgid_nonzero = snap.threads.iter().any(|t| t.tgid > 0);
return Ok(AssertResult::fail(AssertDetail::new(
DetailKind::Other,
format!(
"ctprof::capture() returned {} threads but NONE \
had a non-zero scheduling or page-fault counter; \
any_comm_nonempty={any_comm_nonempty}, \
any_tgid_nonzero={any_tgid_nonzero}. Suggests the \
capture layer reached procfs but every counter \
read collapsed to Default — likely a
/proc/<tid>/{{sched,stat}} parse regression.",
snap.threads.len(),
),
)));
}
Ok(workload_result)
}