[MG_Util, MG_IntegrationTest, CI] (P6): the readers the role split left behind

The per-role log split renamed BOTH halves so that an un-updated reader would
fail loudly instead of silently reading half a session. It worked: CI found
four readers this way. This is them.

- MG_Util/Debug/Log.h: the role vocabulary now exists in the PULL build too,
  header-only. It was wholly inside #if MOBILEGL_BUILD_DISAGGREGATED, so
  PipeInputsTest - which builds in the verify flavour - could not name
  ReadRoleLogs and took the whole build-linux-verify job down at compile time.
  Inline rather than out-of-line because of G1: nothing in the library calls
  these, so an inline definition emits code into the readers and NOTHING into
  libMobileGL.so. Verified: pull symbols 30570 -> 30570, 0 added / 0 removed,
  .text still 0xa52203, and zero role-log symbols in the pull library.

- PipeStatsWindow.h: the harness copy of the naming rule is now flavour-aware.
  It derived `.client` in BOTH flavours, so in the verify build - one role,
  MOBILEGL_LOG_FILE_PATH written unchanged - it opened a file that never
  existed. The copy has to stay a copy: MG_Util's RoleLogPath is `t` (local)
  in the shipping .so this module links, so calling it does not link.
  Added LogMark/MarkLaneLog/ReadLaneLogSince: one offset PER ROLE, because a
  single scalar over the concatenation slides by whatever the other role wrote.

- The four arming scenarios: they asserted on lines the BACKEND emits, and
  under inproc the backend runs on the apply thread - the server role - so the
  line lands in the server's log. Reading only the client's half reported the
  emulation unarmed, i.e. accused the product of a defect the reader invented.
  Four copies of the same private helper pair now delegate to the harness.

- scripts/ci/magma_runahead_checks.py: it unlinked and read the base name as
  if it were a file, so the negative arm saw an empty log and could not prove
  the knob took. It now imports split_log_paths' derivation - the authoritative
  one - rather than becoming a third copy.

Local verification (WSL, lk-split + c6-pull):
  - the 4 CI-red entries: 10/10 PASS under inproc, 10/10 under monolith
  - unit role-log readers: 510/510 PASS
  - magma_runahead_checks negative: run_ahead_0 EXPECTED_RED, restored GREEN
  - the 5 affected TUs compile clean with MOBILEGL_BUILD_DISAGGREGATED=0
  - G1: 0/0/0/0, .text 0xa52203

Red-once is the CI run itself (35677991453), which failed for each of these
reasons before the change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-22 00:37:39 -04:00
co-authored by Claude Opus 5
parent 98d0b96cbe
commit 71aa995128
7 changed files with 191 additions and 73 deletions
+19 -3
View File
@@ -19,6 +19,15 @@ import sys
import time
import xml.etree.ElementTree as ET
# P6: MOBILEGL_LOG_FILE_PATH IS A BASE NAME and the library writes one file per ROLE, so no file
# of the literal name exists. The derivation is IMPORTED, not copied: a third copy of the rule is
# a third thing that can fall behind, and the one that does reads an empty log and reports "the
# runtime knob did not take" - a product failure that never happened. (It did happen: this check
# went red on the role split for exactly that reason.)
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "MobileGL" / "MG_IntegrationTest"
/ "Harness"))
from split_log_paths import any_role_file, read_role_logs, role_paths # noqa: E402
CASES = (
"QueuedBufferVersionsAndDeletedNameReuseKeepTheirPixels",
"QueuedProgramRebindsKeepEachUniformSnapshot",
@@ -98,7 +107,10 @@ def run_case(test, build, directory, overrides):
private = (cwd / private).resolve() if not private.is_absolute() else private.resolve()
if not private.is_relative_to(build):
raise RuntimeError(f"refusing to reset a private log outside the selected build: {private}")
private.unlink(missing_ok=True)
# Every role's file, because a stale server half would otherwise survive into this arm and
# its lines would read as evidence about the knob this run set.
for role_path in role_paths(private):
Path(role_path).unlink(missing_ok=True)
xml = directory / "gtest.xml"
command = [arg for arg in command if not arg.startswith("--gtest_output=")]
command.append(f"--gtest_output=xml:{xml}")
@@ -119,10 +131,14 @@ def run_case(test, build, directory, overrides):
except subprocess.TimeoutExpired:
rc = 124
output.write("\nRUNNER_TIMEOUT: process killed after discovered CTest timeout\n")
private_text = private.read_text(encoding="utf-8", errors="replace") if private.is_file() else ""
# BOTH ROLES, concatenated into the one evidence file this run archives. `Config: IPC` is the
# client's line and the queue-lead diagnostics are the applier's, so either half alone would
# answer one of the two questions the negative arm asks and be silent on the other.
private_text = read_role_logs(private)
(directory / "private.log").write_text(private_text, encoding="utf-8")
result = {"ctest_name": test["name"], "returncode": rc,
"seconds": round(time.monotonic()-started, 3), "private_log_present": private.is_file()}
"seconds": round(time.monotonic()-started, 3),
"private_log_present": any_role_file(private)}
write_json(directory / "process.json", result)
return result