use anyhow::Result;
use ktstr::assert::{AssertDetail, AssertResult, DetailKind};
use ktstr::ktstr_test;
use ktstr::prelude::{Backdrop, CgroupDef, Payload, Scheduler, SchedulerSpec};
use ktstr::scenario::Ctx;
use ktstr::scenario::ops::{HoldSpec, Op, Step, execute_scenario};
use std::time::Duration;
const KTSTR_SCHED: Scheduler =
Scheduler::named("ktstr_sched").binary(SchedulerSpec::Discover("scx-ktstr"));
const SHELL_PROBE: Payload = Payload::binary("cgroup_placement_probe", "sh");
const CGROUP_SNAPSHOT_PATH: &str = "/tmp/ktstr-placement-check-cgroup";
const PID_SNAPSHOT_PATH: &str = "/tmp/ktstr-placement-check-pid";
#[ktstr_test(
scheduler = KTSTR_SCHED,
workload_root_cgroup = "/ktstr-placement-pre-exec-e2e",
extra_include_files = ["/bin/sh", "/bin/cat"],
llcs = 1,
cores = 2,
threads = 1,
memory_mib = 256,
duration_s = 3,
watchdog_timeout_s = 30,
auto_repro = false,
)]
fn op_runpayload_places_child_in_cgroup_before_exec(ctx: &Ctx) -> Result<AssertResult> {
let backdrop = Backdrop::new().push_op(Op::add_cgroup("cg_dst"));
let probe_script = format!(
"echo $$ > {pid_path} && cat /proc/self/cgroup > {cgroup_path}",
pid_path = PID_SNAPSHOT_PATH,
cgroup_path = CGROUP_SNAPSHOT_PATH,
);
let steps = vec![Step {
setup: Vec::<CgroupDef>::new().into(),
ops: vec![
Op::run_payload_in_cgroup(&SHELL_PROBE, vec!["-c".to_string(), probe_script], "cg_dst"),
Op::wait_payload(SHELL_PROBE.name),
],
hold: HoldSpec::fixed(Duration::ZERO),
}];
let _ = execute_scenario(ctx, backdrop, steps)?;
let snapshot = match std::fs::read_to_string(CGROUP_SNAPSHOT_PATH) {
Ok(s) => s,
Err(e) => {
return Ok(AssertResult::fail(AssertDetail::new(
DetailKind::Other,
format!(
"read {CGROUP_SNAPSHOT_PATH}: {e}. The shell probe \
payload was expected to write its `/proc/self/cgroup` \
contents to this path before exiting; a missing file \
means either the payload never ran (spawn failure) or \
the `Op::wait_payload` returned before the shell's \
redirect completed."
),
)));
}
};
let line = snapshot.trim();
let relative = ctx
.cgroups
.parent_path()
.strip_prefix("/sys/fs/cgroup")
.expect(
"workload_root_cgroup parent_path lives under /sys/fs/cgroup in \
the guest-VM context this test runs in (Mode A — no cgroup-v2 \
delegation walk_root override)",
);
let expected_line = format!("0::/{}/cg_dst", relative.display());
if line != expected_line {
return Ok(AssertResult::fail(AssertDetail::new(
DetailKind::Other,
format!(
"{CGROUP_SNAPSHOT_PATH} contents = {line:?}; expected \
exactly {expected_line:?} (proving the payload's first \
userspace read of `/proc/self/cgroup` observed it inside \
cg_dst). A different line means \
`CgroupOps::place_task_during_handshake` either did not \
fire or fired after the child's `execve` — the \
placement-before-exec invariant has regressed and the \
spawn is no longer placement-before-exec."
),
)));
}
Ok(AssertResult::pass())
}