[CI, scripts, tools] (P7): the pull control strips ctest -V's line prefix, retrace-split pins lavapipe, and the retrace summary reads role logs

This commit is contained in:
2026-09-23 13:23:09 -04:00
parent 2ab757792c
commit b13045918a
5 changed files with 35 additions and 3 deletions
+9 -1
View File
@@ -2542,7 +2542,7 @@ jobs:
# silently is guarded here. trace_cases.py now also rejects an unknown manifest key, which
# was the hole: `"splitt": true` loaded clean and emptied the subset (review N-4).
if [ "$(printf '%s' "${SPLIT_MATRIX}" | python3 -c 'import json,sys; print(len(json.load(sys.stdin)["include"]))')" -lt 1 ]; then
echo "::error::the split retrace subset is EMPTY. `split` is an OPT-OUT as of P6 - a CI case is in this subset unless it carries \"split\": false - so an empty subset means either every case opted out or the CI matrix itself is empty. Either way retrace-split would be skipped with no red, and exit gate E2 names OpenRA."
echo "::error::the split retrace subset is EMPTY. 'split' is an OPT-OUT as of P6 - a CI case is in this subset unless it carries \"split\": false - so an empty subset means either every case opted out or the CI matrix itself is empty. Either way retrace-split would be skipped with no red, and exit gate E2 names OpenRA."
exit 1
fi
echo "split-matrix=${SPLIT_MATRIX}" >> "$GITHUB_OUTPUT"
@@ -2979,6 +2979,14 @@ jobs:
name: retrace split (${{ matrix.backend }} / ${{ matrix.transport }}, ${{ matrix.case }})
runs-on: ubuntu-latest
timeout-minutes: 240
# THE LAVAPIPE ICD PIN the integration lanes get through -DMOBILEGL_ITEST_VK_ICD. Here it
# can be job-wide: add_trace_replay_test's ENVIRONMENT names no VK_ICD_FILENAMES, so nothing
# overrides it. mesa-vulkan-drivers installs every Mesa ICD, and an unpinned loader probes all
# of them inside the spawned server's lazy Magma bring-up - the MakeCurrent that outlasted the
# client's reply bound in runs 35671704873 / 35706183230. ClientSession's cold-start budget
# is the fix; this pin keeps the bring-up on the one device a runner has.
env:
VK_ICD_FILENAMES: /usr/share/vulkan/icd.d/lvp_icd.json
needs:
- build-linux-monolith-control
- build-linux-split
+4
View File
@@ -121,6 +121,10 @@ expect PASSED "run_trace_case.cmake's own sentence, wrapped" -- run_retrace retr
# search. The control accepted only the marker sentence until B3's fix round 2, and was red on
# every real pull-library run (measured on the B3 package tree).
expect PASSED "the no-client-log sentence a pull library gets" -- run_retrace retrace-evidence-nolog
# Both sentences again as `ctest -V` really prints them, with "1: " on every line: folded
# naively that is "never 1: reported resolving it", and the control used to red on it.
expect PASSED "the marker sentence under ctest -V's N: prefix" -- run_retrace retrace-evidence-prefixed
expect PASSED "the no-log sentence under ctest -V's N: prefix" -- run_retrace retrace-evidence-nolog-prefixed
# The pull library replaying green is the failure this control exists to catch.
expect FAILED "a pull library passed the split retrace" -- run_retrace retrace-green
+4 -1
View File
@@ -155,7 +155,10 @@ fi
# width: "never reported resolving it" arrives split over two lines with a two-space continuation
# indent, and a line-oriented grep for the literal finds nothing. That is not hypothetical - it is
# the shape the stub reproduces in scripts/ci/testdata/stub_ctest.sh.
if ! tr -s '[:space:]' ' ' < "${out}" | grep -qF "${EVIDENCE}"; then
# `ctest -V` ALSO PREFIXES EVERY OUTPUT LINE WITH "<test number>: ", continuation lines included,
# so without stripping it first the folded text reads "never 1: reported resolving it" and the
# control reds on the very sentence it is looking for (the *-prefixed stub modes).
if ! sed -E 's/^[0-9]+: //' "${out}" | tr -s '[:space:]' ' ' | grep -qF "${EVIDENCE}"; then
echo "::error::the split retrace went red (ctest exit ${control_rc}) with the pull library in place, but the failure never says the library did not resolve the transport - neither of run_trace_case.cmake's sentences (\"${EVIDENCE//$'\n'/\" / \"}\") is in the output. A loader failure, a missing fixture, a timeout or an SSIM drop all land here, and none of them establishes that the transport-identity assertion is what caught the pull library. Only 'non-zero ctest' used to be checked (ID-46 finding 8b)."
exit 1
fi
Vendored Executable → Regular
+11
View File
@@ -32,6 +32,8 @@
# one match; the run fails one check earlier, with the runner's "wrote no
# <client log> ... no evidence the transport ever resolved" - the shape a
# PULL library produces since P6's per-role log rename
# retrace-evidence-prefixed, retrace-evidence-nolog-prefixed
# the two above with `ctest -V`'s "1: " prefix on every line
# retrace-green one match; the run PASSES
#
# EXIT GATE E2's DRAW-DROP CONTROL (scripts/ci/retrace_drop_draw_control.sh). The library's own
@@ -307,6 +309,15 @@ case "${mode}" in
echo " counted as a split retrace."
exit 8
;;
retrace-evidence-prefixed|retrace-evidence-nolog-prefixed)
# The two modes above as `ctest -V` really prints them: EVERY line of a test's output carries
# a "<test number>: " prefix, continuation lines included, so after whitespace folding the
# wrapped sentence reads "never 1: reported resolving it" / "no evidence the 1: transport ever
# resolved". A short case directory breaks the sentence at exactly those words; CI's long
# paths happened not to (retrace-split, run 35671704873: 5 false reds from this shape).
STUB_MODE="${mode%-prefixed}" bash "$0" "$@" | sed 's/^/1: /'
exit "${PIPESTATUS[0]}"
;;
retrace-green)
echo "100% tests passed, 0 tests failed out of 1"
exit 0
@@ -144,9 +144,15 @@ function parseGpuLabel(logText) {
async function collectGroupLabels(files, defaultGroup) {
const labels = new Map();
// A pull library writes one mobilegl.log. A split library writes one log per ROLE
// (mobilegl.client.log / mobilegl.server.log) and no mobilegl.log, so the Linux split lanes'
// summaries had no GPU label at all; their role logs are read wherever mobilegl.log is absent.
const present = new Set(files.map((file) => normalizeSlashes(file)));
const logFiles = files.filter((file) => {
const name = path.basename(file).toLowerCase();
return name === "mobilegl.log" || name === "logcat.txt";
if (name === "mobilegl.log" || name === "logcat.txt") return true;
if (name !== "mobilegl.client.log" && name !== "mobilegl.server.log") return false;
return !present.has(normalizeSlashes(path.join(path.dirname(file), "mobilegl.log")));
});
for (const logFile of logFiles) {
const group = inferGroup(logFile, defaultGroup);