Skip to content

fix(ci): read the process table without forking ps in the exec tracker (D-1008) - #295

Merged
acamarata merged 2 commits into
mainfrom
fix/D-1008-exec-tracker-snapshot
Oct 19, 2026
Merged

acamarata merged 2 commits into
mainfrom
fix/D-1008-exec-tracker-snapshot

Conversation

@acamarata

@acamarata acamarata commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

What

D-1008: the exec funnel tracker forked ps -A up to three times per snapshot. It now reads the process table from /proc (Linux) or sysctl kern.proc.all (darwin) and falls back to ps only if that fails. Per-job process identity is the fork-free st:<start> form (Linux adds the boot id); legacy ps lstart markers are still compared.

Review fixes on top of the Codex branch

  • KillAttemptGroup compared only the new identity form, so a marker written by an older build looked like a reused PID: its group was left running and the marker removed. Both kill paths now use one sameProcess helper that reads the live identity in the marker's own form; an unreadable one is "unknown", not "different".
  • The snapshot re-scans (max 3 passes) while a pass finds new processes, as the ps loop did, so forks made while the table is read are still caught.
  • Linux start ticks are tied to the boot id, so a marker kept across a reboot cannot match a new process with the same pid and ticks.
  • Tests: legacy marker killed, reused PID spared (native and legacy forms), sweep refuses a reused PID, nested descendants tracked, stable per-process start id.

Verified

go vet, go test ./internal/exec/... ./internal/hygiene/ pass on darwin (local, mm6) and linux (ps1). Existing cancel/timeout/detached-grandchild teardown tests unchanged and green.

Still open

planRevision still makes about 9 funnel git calls, not merged into one invocation, and the cibench ratio still misses (below), so D-1008 stays open.

cibench (V15 / v1.4)

Measured after merge on ps1 (linux/amd64, 16 vCPU), cibench -runs 20 -warmup 2, harness from p7/P7-CI-46 (#282) merged onto main at 8c7550c1 in a scratch worktree, load average 4.4 at start. Numbers are from the perfbench/v1 result.

Metric before (evidence CI-46, 3 runs) after
ci.exec_overhead p50 31.1 to 34.4 ms 9.6 ms
ci.e2e_tiny.v14 p50 287 to 294 ms 297.4 ms
ci.e2e_tiny.v15 p50 1317 to 1337 ms 2195.3 ms (p95 2259.8)
V15 / v1.4 tiny 4.5 to 4.6 7.4
ci.e2e_medium.v14 / v15 p50 686 to 694 / 1464 to 1491 ms 699.7 / 2424.3 ms

A first run under load average 34 gave the same tiny medians (v1.4 295.0, V15 2206.2 ms), so the figure is not load noise.

Result: the per-job funnel overhead dropped about 3x and an strace of one tiny V15 run shows zero ps execs, but the end-to-end V15 time did not improve and is higher than the CI-46 baseline (main has moved since revision 8ecef39e). The ratio check still fails (ci.e2e_tiny.v15 limit 577 ms), so P7-CI-46 cannot drop its ratio exception on this PR alone and D-1008 stays open. The remaining cost is not the tracker: an strace of one tiny run shows 26 git, 28 sh, repeated docker info (plus buildx/compose plugin metadata execs, 5 times) and 3 go version execs besides the checks themselves.

…ing ps (D-1008)

The exec funnel's descendant tracker forked ps -A up to three times per
snapshot, and the process identity marker and Linux group probe forked ps
again. A no-op funnel job cost about 31 ms; it now costs about 2.4 ms.
Snapshots read /proc on Linux and sysctl kern.proc.all on darwin, one read
per call; ps stays as the fallback. Markers written by older builds (ps
lstart text) are still compared through ps.
…l stable, bind start ticks to boot

KillAttemptGroup compared only the new st: identity, so a marker written
by an older build (ps lstart text) looked like a reused PID and its group
was left running. Both kill paths now share sameProcess, which reads the
live identity in the marker's own form and treats an unreadable one as
unknown, not different.

The tracker snapshot repeats (at most three passes) while a pass finds new
processes, as the ps version did, so forks during the read are caught.
Linux start ids carry the boot id so ticks cannot match across a reboot.
Adds identity, legacy-marker, reused-PID and nested-descendant tests.
@acamarata
acamarata merged commit 8c7550c into main Oct 19, 2026
38 of 39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant