1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//! End-to-end coverage for the pcomm fork-then-thread spawn path
//! via [`WorkSpec::pcomm`].
//!
//! Setting `WorkSpec::pcomm("chrome")` on a single WorkSpec routes
//! it through
//! [`crate::workload::WorkloadHandle::spawn_pcomm_cgroup`]: ONE
//! forked thread-group leader whose `task->comm` is `"chrome"`,
//! hosting every requested worker as a thread under that leader.
//! Each worker thread additionally sets its own `task->comm` from
//! `WorkSpec::comm` — modelling a real workload like `chrome`
//! (pcomm) hosting `ThreadPool` worker threads.
//!
//! The `post_vm` callback gates on the same host-visible signals
//! used in [`worker_properties_e2e`](super::worker_properties_e2e):
//! a regression in the partitioner, container fork, leader's
//! `prctl(PR_SET_NAME)`, per-thread spawn, or bincode report stream
//! collapses one of `timed_out` / `crash_message` / `exit_code` /
//! `success`.
use anyhow::Result;
use ktstr::assert::AssertResult;
use ktstr::ktstr_test;
use ktstr::prelude::{VmResult, WorkSpec};
use ktstr::scenario::Ctx;
use ktstr::scenario::ops::{CgroupDef, HoldSpec, Step, execute_steps};
use ktstr::test_support::{Scheduler, SchedulerSpec};
const KTSTR_SCHED: Scheduler =
Scheduler::new("ktstr_sched").binary(SchedulerSpec::Discover("scx-ktstr"));
/// Host-side gate: a clean run means the in-VM dispatch processed
/// the pcomm-tagged WorkSpec without crashing. Any regression in
/// the pcomm path collapses one of these signals.
fn assert_pcomm_ran_clean(result: &VmResult) -> Result<()> {
anyhow::ensure!(
!result.timed_out,
"guest timed out under the watchdog — the pcomm container \
spawn or hold never completed; the fork-then-thread dispatch \
in `apply_setup` likely faulted before reaching the hold"
);
anyhow::ensure!(
result.crash_message.is_none(),
"guest panicked during the run — `crash_message` = {:?}; a \
regression in `spawn_pcomm_cgroup` (leader fork, \
`prctl(PR_SET_NAME)`, per-thread spawn, or bincode report \
stream) would surface here",
result.crash_message,
);
anyhow::ensure!(
result.exit_code == 0,
"guest exit_code = {} (expected 0) — non-zero typically means \
the in-guest scenario bailed before completing the hold (e.g. \
the pcomm partitioner rejected the WorkSpec slice or the \
container leader's report-collection path returned an error)",
result.exit_code,
);
anyhow::ensure!(
result.success,
"VM run reported success = false (timed_out = {}, exit_code = \
{}, crash_message = {:?}); the pcomm fork-then-thread \
dispatch did not produce a clean run",
result.timed_out,
result.exit_code,
result.crash_message,
);
Ok(())
}
/// Boots a real guest, declares one cgroup with a single
/// [`WorkSpec`] of two workers carrying `pcomm = "chrome"` and per-
/// thread `comm = "ThreadPool"`. With `pcomm` set on the WorkSpec,
/// `apply_setup` routes it through `spawn_pcomm_cgroup`: ONE forked
/// leader whose `task->comm` is `"chrome"`, hosting two worker
/// threads whose own `task->comm` is `"ThreadPool"`. Completion
/// alone — gated by `assert_pcomm_ran_clean` — is the assertion:
/// any regression that crashes, times out, or exits non-zero
/// collapses one of the host-visible signals checked there.
#[ktstr_test(
scheduler = KTSTR_SCHED,
duration_s = 3,
watchdog_timeout_s = 15,
auto_repro = false,
post_vm = assert_pcomm_ran_clean,
)]
fn pcomm_fork_then_thread_e2e(ctx: &Ctx) -> Result<AssertResult> {
let _ = ctx;
let steps = vec![Step {
setup: vec![
CgroupDef::named("cg_chrome").work(
WorkSpec::default()
.workers(2)
.comm("ThreadPool")
.pcomm("chrome"),
),
]
.into(),
ops: vec![],
hold: HoldSpec::FULL,
}];
execute_steps(ctx, steps)
}