feat: run CPU isolation and the memory instrument inside the macro-agent sandbox#456
feat: run CPU isolation and the memory instrument inside the macro-agent sandbox#456GuillaumeLagrange wants to merge 2 commits into
Conversation
Replace the runner's built-in CPU-isolation mechanisms with a single,
machine-driven one. The runner previously hard-coded `systemd-run --scope` and a
`CODSPEED_ISOLATION=CGROUP:<dir>` mode with per-spawn cgroup-dir plumbing, split
across a `HookScriptsGuard` (which ran the pre/post-bench hooks) and
`isolation.rs` (which did the wrapping).
Now a single `Isolation` type owns the whole lifecycle: `resolve()` runs the
pre-bench hook, `wrap_bench()` pins the benchmark leaf, and `Drop` runs
post-bench. Cpuset logic lives on the machine behind three hooks
(`codspeed-{pre,wrap,post}-bench`); the runner only invokes them and is otherwise
oblivious to how cores are attributed. Discovery is by hook presence:
- an executable `codspeed-wrap-bench` selects the hook path — unprivileged, and
the benchmark stays a descendant of the profiler so it records without sudo;
- its absence falls back to `systemd-run --scope --slice=codspeed.slice`, so
hosts without the hook keep working unchanged.
The pre-bench hook is invoked with the runner's PID so the machine places the
runner (and the profiler it spawns) onto the system cores; the runner makes no
cgroup writes of its own. The profiler's `wrap_command` flag is renamed
`isolate` -> `requires_sudo`, now true only for the systemd fallback.
Refs COD-3012
Co-Authored-By: Claude <noreply@anthropic.com>
Refs COD-3047
Merging this PR will not alter performance
|
Greptile SummaryThis PR ships two sandbox-enablement features together: hook-based CPU isolation (replacing
Confidence Score: 3/5The CPU isolation refactor is clean and well-tested, but the eBPF namespace PID fix has a gap that silently breaks realloc tracking inside the sandbox. The UPROBE_ARGS_RET macro in memtrack.bpf.c (used exclusively for realloc) still uses global pid = tid >> 32 for its is_tracked guard while every other probe was updated to current_pid(). Inside a PID namespace, namespace-local PIDs stored in tracked_pids never match the global PID the guard looks up, so realloc args are never stored and every call is silently dropped. The flavor_equivalence test does not catch this because it runs without a PID namespace. crates/memtrack/src/ebpf/c/memtrack.bpf.c — the UPROBE_ARGS_RET macro entry probe PID guard on line 319 Important Files Changed
|
| fn has_delegated_bpf_token() -> bool { | ||
| std::env::var_os("LIBBPF_BPF_TOKEN_PATH").is_some_and(|p| !p.is_empty()) | ||
| } |
There was a problem hiding this comment.
has_delegated_bpf_token() is defined differently here than in src/executor/memory/executor.rs. The executor's version additionally verifies the path is a directory (Path::new(&p).is_dir()), guarding against a stale or invalid LIBBPF_BPF_TOKEN_PATH. This function skips that check, so if the variable is set to a non-existent path, the executor correctly falls through to capability checking while this code still selects the token flavor — causing MemtrackBpf::new() to fail with an opaque eBPF load error instead of the clear "no privilege" path.
| fn has_delegated_bpf_token() -> bool { | |
| std::env::var_os("LIBBPF_BPF_TOKEN_PATH").is_some_and(|p| !p.is_empty()) | |
| } | |
| fn has_delegated_bpf_token() -> bool { | |
| std::env::var_os("LIBBPF_BPF_TOKEN_PATH") | |
| .is_some_and(|p| !p.is_empty() && std::path::Path::new(&p).is_dir()) | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/memtrack/src/ebpf/memtrack.rs
Line: 30-32
Comment:
`has_delegated_bpf_token()` is defined differently here than in `src/executor/memory/executor.rs`. The executor's version additionally verifies the path is a directory (`Path::new(&p).is_dir()`), guarding against a stale or invalid `LIBBPF_BPF_TOKEN_PATH`. This function skips that check, so if the variable is set to a non-existent path, the executor correctly falls through to capability checking while this code still selects the token flavor — causing `MemtrackBpf::new()` to fail with an opaque eBPF load error instead of the clear "no privilege" path.
```suggestion
fn has_delegated_bpf_token() -> bool {
std::env::var_os("LIBBPF_BPF_TOKEN_PATH")
.is_some_and(|p| !p.is_empty() && std::path::Path::new(&p).is_dir())
}
```
How can I resolve this? If you propose a fix, please make it concise.
Runner-side changes to make CPU isolation and the memory instrument work
unprivileged inside the macro-agent sandbox. Two issues, combined here because
they share the sandbox's privilege model and the same branch.
CPU isolation (COD-3012). Drop the runner's built-in
systemd-run/CGROUP:isolation for a hook-based one: anIsolationtype invokescodspeed-{pre,wrap,post}-benchand stays oblivious to how cores are attributed.If
codspeed-wrap-benchexists it takes the (unprivileged) hook path; otherwiseit falls back to
systemd-run --scope --slice=codspeed.slice, so non-sandboxhosts are unchanged.
Memory instrument (COD-3047). Let memtrack run off a delegated BPF token
instead of root: attach via
bpf()-native links (uprobe_multi+tp_btf),resolve tracked PIDs in the tracker's PID namespace, and accept
LIBBPF_BPF_TOKEN_PATHas a privilege source.Closes COD-3012
Closes COD-3047