From f87ec9ec672610fbef63fd59b13bf197cc677261 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sat, 17 Oct 2026 12:36:04 -0400 Subject: [PATCH] [Chore] (.github/workflows, docs/ci, scripts/ci): delete dead CI control code - Strict split lane: drop the kMGPipeP5eRunAheadReady grep and its red-by-design pre-flip arm; the constant is true, so the arm never ran - Dual-block census lane: drop the "green with rows outstanding" rule, inert while dualblock-expected-fatals.txt is empty; the empty-census rule and expect-fatal stay - delete scripts/ci/tcp_log_forwarding_control.py, which nothing calls --- .github/workflows/test.yml | 39 +----- docs/ci/ci-cleanup-log.md | 2 + scripts/ci/tcp_log_forwarding_control.py | 158 ----------------------- 3 files changed, 6 insertions(+), 193 deletions(-) delete mode 100644 scripts/ci/tcp_log_forwarding_control.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 89074422..4d37c95d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1679,11 +1679,8 @@ jobs: # ---- Strict split lane: residual pulls under MOBILEGL_IPC_STRICT_ERRORS=1 ---- # # An unbarriered pull is always fatal, and strict also stops any barriered pull that is not - # admitted (ARCHITECTURE.md §9.2.1). The arm comes from kMGPipeP5eRunAheadReady in - # MG_Backend/Init.cpp, so the expectation changes in the commit that changes the runtime. When - # true (run-ahead), the lane must be green and its markers must match the admitted list and - # strict-expected-markers.txt; when false, every record is barriered, the lane must be red - # (strict aborts every drawing entry), and only the marker census is checked. + # admitted (ARCHITECTURE.md §9.2.1). The lane must be green, and its markers must match the + # admitted list and strict-expected-markers.txt in both directions. - name: Strict split lane working-directory: build-split env: @@ -1692,8 +1689,6 @@ jobs: MOBILEGL_TRANSPORT: "inproc" run: | ulimit -c unlimited - ready=$(grep -oE 'kMGPipeP5eRunAheadReady = (true|false)' "${GITHUB_WORKSPACE}/MobileGL/MG_Backend/Init.cpp" | head -1 | awk '{print $3}') - echo "kMGPipeP5eRunAheadReady = ${ready}" helper="${GITHUB_WORKSPACE}/MobileGL/MG_IntegrationTest/Harness/split_log_paths.py" expected="${GITHUB_WORKSPACE}/MobileGL/MG_IntegrationTest/Harness/strict-expected-markers.txt" @@ -1731,24 +1726,7 @@ jobs: > "${RUNNER_TEMP}/allowed.txt" echo "§7 allowlist (derived): $(wc -l < "${RUNNER_TEMP}/allowed.txt") admitted pair(s)" - if [ "${ready}" != "true" ]; then - # ---- the pre-flip arm ---- - if [ "${rc}" -eq 0 ]; then - echo "::error::integration-split under MOBILEGL_IPC_STRICT_ERRORS=1 went GREEN while kMGPipeP5eRunAheadReady is false - the object-class residual pulls (P3b/P4b/P7) are still pinned, so strict must abort every drawing scenario. If P5e has just landed, flip that constant in the same commit." - exit 1 - fi - # The census, and the ADMITTED half of the ratchet, run in this arm too: the Fatal - # half is expected here (it is what "red by design" MEANS), but a debt that appeared - # or vanished on the admitted side is news whichever arm the tree is in. - python3 "${helper}" markers "${RUNNER_TEMP}/lane.json" '.' \ - "${RUNNER_TEMP}/allowed.txt" "${expected}" - echo "integration-split under strict is red by design, and its marker census above is the evidence" - exit 0 - fi - - # ---- the post-flip arm: HARD GREEN, two-sided ---- - # - # EVERY ENTRY MUST COMPLETE, and the census is what says why when one does not: an abort + # Every entry must complete, and the census is what says why when one does not: an abort # is either an unbarriered pull (§3.3's unconditional Fatal, a missed migration) or a # barriered pull nothing admits, and both come out of the census by field and verb. if [ "${rc}" -ne 0 ]; then @@ -1806,12 +1784,7 @@ jobs: # machine-checked list of what still depends on the two roles sharing one object, and it # is ratcheted two-sided against Harness/dualblock-expected-fatals.txt: a new pair is a # debt somebody now owes, a vanished pair must be removed in the commit that retired it. - # - # THE STEP ALSO FAILS WHEN THE LANE IS GREEN while the expected file still has rows - - # "green" would mean the knob stopped arming, which is exactly the decay ID-122 assigns - # to a control rather than to a lane's good mood. At P5f's exit the file is empty and the - # green arm is the correct one; delete the red expectation in the same commit. - # P5f wave 2: with the census empty, this lane is a hard green gate. + # With the census empty, this lane is a hard green gate. - name: Dual-block census lane working-directory: build-split env: @@ -1853,10 +1826,6 @@ jobs: echo "::error::empty dual-block census requires ctest success, including infrastructure/execution status" exit "${rc}" fi - if [ "${rc}" -eq 0 ] && [ "${rows}" -gt 0 ]; then - echo "::error::the dual-block census lane went GREEN with ${rows} expected pair(s) outstanding - the knob stopped arming, or the fields retired and dualblock-expected-fatals.txt was not emptied in the same commit" - exit 1 - fi python3 "${helper}" expect-fatal "${RUNNER_TEMP}/dualblock-lane.json" \ "${RUNNER_TEMP}/dualblock.xml" "${expected}" diff --git a/docs/ci/ci-cleanup-log.md b/docs/ci/ci-cleanup-log.md index 9bb315bd..56208f28 100644 --- a/docs/ci/ci-cleanup-log.md +++ b/docs/ci/ci-cleanup-log.md @@ -44,6 +44,8 @@ them wrong. | 2026-10-17 | `Negative control B (omitted fill point)` | Test / `integration-verify` | it re-ran `PoisonOmissionScenario.WithoutOmissionCompletes` with `MOBILEGL_PIPE_POISON_OMIT=ReadPixels:GetPixelStoreParameters` and accepted any red | the in-lane `DirectGLES/DirectVulkan.PoisonOmitted.PoisonOmissionScenario.OmittedFieldAbortsOnThatVerb` entries: the same pair, and they also require SIGABRT with the named `Fatal{UnmigratedPipeInput}`; now required to pass by name | | 2026-10-17 | `Verify lane arming check` (grep of `pipe-verify-arming-*.log`) | Test / `integration-verify` | the VerifyArming entries assert the armed line in-test; the grep only added "the entry did not skip" | `Verify lane control entries`: `junit_tally.py --require-entry-passed` on the VerifyArming, VerifyCorrupted and PoisonOmitted entries of both backends (a skip fails) | | 2026-10-17 | `Negative control A (server-side read)` and `Negative control B (omitted stamp)` | Test / `integration-verify-split` | the first re-ran the two VerifySplitReadCorrupted entries the lane had just run and grepped what they already assert in-test (at least two where=read reports in the server half); the second re-ran with the pair the in-lane VerifySplitPoisonOmitted entries already pin, and accepted any red | `Verify-split control entries`: `--require-entry-passed` on VerifySplitArming, VerifySplitCorrupted, VerifySplitReadCorrupted and VerifySplitPoisonOmitted of both backends | +| 2026-10-17 | the pre-flip arm of `Strict split lane` (red-by-design while `kMGPipeP5eRunAheadReady` was false) and the "GREEN with rows outstanding" rule of `Dual-block census lane` | Test / `integration-split-strict` | dead: the constant is true, and `dualblock-expected-fatals.txt` has no rows, so neither branch could run | the hard-green arm and its two-sided marker census, and the empty-census rule (rows == 0 requires ctest success), both kept | +| 2026-10-17 | `scripts/ci/tcp_log_forwarding_control.py` | none | no caller outside the archived planning notes | none needed | ## Kept after investigation diff --git a/scripts/ci/tcp_log_forwarding_control.py b/scripts/ci/tcp_log_forwarding_control.py deleted file mode 100644 index d7d444ce..00000000 --- a/scripts/ci/tcp_log_forwarding_control.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env python3 -# MobileGL - scripts/ci/tcp_log_forwarding_control.py -# Copyright (c) 2025-2026 MobileGL-Dev -# Licensed under the GNU Lesser General Public License v3.0: -# https://www.gnu.org/licenses/gpl-3.0.txt -# https://www.gnu.org/licenses/lgpl-3.0.txt -# SPDX-License-Identifier: LGPL-3.0-only -# End of Source File Header - -"""Run the four real arming scenarios with server log forwarding enabled and disabled.""" -import argparse -import errno -import json -import os -from pathlib import Path -import re -import signal -import socket -import subprocess -import time -import xml.etree.ElementTree as ET - - -CASES = { - 'unlocated': ('DirectGLES', 'UnlocatedIoBlockScenario.TheEmulationIsActuallyArmedWhenTheEnvironmentPinsItOn', - {'MOBILEGL_ESPRYT_UNLOCATED_IO_BLOCKS': '1'}, 'WITHOUT their layout(location) qualifier'), - 'primitives': ('DirectVulkan', 'PrimitivesGeneratedNoXfbScenario.TheRerouteIsActuallyArmedWhenTheEnvironmentPinsItOn', - {'MOBILEGL_MAGMA_PRIMGEN_QUERY_REROUTE': '1'}, 'PRIMITIVES_GENERATED reroute engaged'), - 'point-size': ('DirectGLES', 'PointSizeDemotionScenario.TheDemotionIsActuallyArmedWhenTheEnvironmentPinsItOn', - {'MOBILEGL_POINT_SIZE_DEMOTION': '1'}, 'demoted to an ordinary varying'), - 'verify': ('DirectGLES', 'PipeVerifyArmingScenario.Armed', - {'MOBILEGL_PIPE_VERIFY': '1', 'MGITEST_PIPE_ARMING_LANE': '1'}, 'MGPipe: verify armed'), -} - - -def read(path): - return path.read_text(errors='replace') if path.exists() else '' - - -def run_arm(build, out, case, forward): - backend, test, knobs, marker = CASES[case] - out.mkdir(parents=True, exist_ok=True) - with socket.socket() as reserve: - reserve.bind(('127.0.0.1', 0)) - port = reserve.getsockname()[1] - endpoint = f'tcp://127.0.0.1:{port}' - env = dict(os.environ, MOBILEGL_TRANSPORT='spawn', MOBILEGL_BACKEND_TYPE=backend, - MOBILEGL_IPC_REQUIRE_SAME_BUILD='1', MOBILEGL_IPC_TOKEN='', EGL_PLATFORM='surfaceless', - LIBGL_ALWAYS_SOFTWARE='1', MOBILEGL_ITEST_REQUIRE_GPU='1', **knobs) - for name in ('MOBILEGL_PIPE_VERIFY_CORRUPT', 'MOBILEGL_IPC_CONTROL', 'MOBILEGL_IPC_DIAL'): - env.pop(name, None) - if Path('/usr/share/glvnd/egl_vendor.d/50_mesa.json').exists(): - env['__EGL_VENDOR_LIBRARY_FILENAMES'] = '/usr/share/glvnd/egl_vendor.d/50_mesa.json' - if Path('/usr/share/vulkan/icd.d/lvp_icd.json').exists(): - env.setdefault('VK_ICD_FILENAMES', '/usr/share/vulkan/icd.d/lvp_icd.json') - peer_env = dict(env, MOBILEGL_IPC_ROLE='server', MOBILEGL_IPC_DIAL='no', - MOBILEGL_IPC_LOG_FORWARD=str(int(forward)), MOBILEGL_LOG_FILE_PATH=str(out / 'peer.log')) - for name in ('MOBILEGL_TRANSPORT', 'MOBILEGL_IPC_SERVER_PATH', 'MOBILEGL_IPC_RING_MB', 'MOBILEGL_IPC_STAGE_MB'): - peer_env.pop(name, None) - with (out / 'supervisor.log').open('wb') as output: - server = subprocess.Popen([str(build / 'libMobileGLServer.so'), endpoint, '--serve'], - env=peer_env, stdout=output, stderr=output, start_new_session=True) - try: - for _ in range(100): - if server.poll() is not None: - raise RuntimeError('supervisor exited before listening: ' + read(out / 'supervisor.log')) - with socket.socket() as probe: - probe.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - try: - probe.bind(('127.0.0.1', port)) - except OSError as error: - if error.errno == errno.EADDRINUSE: - break - raise - time.sleep(.05) - else: - raise RuntimeError('supervisor did not listen') - client_env = dict(env, MOBILEGL_IPC_CONTROL=endpoint, MOBILEGL_IPC_DATA='stream', - MOBILEGL_IPC_LOG_FORWARD='1', MOBILEGL_LOG_FILE_PATH=str(out / 'client.log'), - MGITEST_SPLIT_LANE='1', MGITEST_TCP_LANE='1') - result = subprocess.run([str(build / 'MobileGL/MG_IntegrationTest/MobileGLIntegrationTest'), - '--gtest_filter=' + test, '--gtest_output=xml:' + str(out / 'result.xml')], - env=client_env, capture_output=True, text=True, timeout=120) - (out / 'stdout.log').write_text(result.stdout + result.stderr) - finally: - try: - os.killpg(server.pid, signal.SIGTERM) - except ProcessLookupError: - pass - server.wait(timeout=10) - passed = failed = skipped = 0 - marker_assertion_failed = False - if (out / 'result.xml').exists(): - for entry in ET.parse(out / 'result.xml').getroot().iter('testcase'): - if entry.find('skipped') is not None or entry.get('status') == 'notrun': - skipped += 1 - elif entry.find('failure') is not None: - failed += 1 - marker_assertion_failed = any(marker in (failure.get('message', '') + (failure.text or '')) - for failure in entry.findall('failure')) - else: - passed += 1 - client_log = read(out / 'client.client.log') - return {'case': test, 'returncode': result.returncode, 'passed': passed, 'failed': failed, 'skipped': skipped, - 'tcp_armed': bool(re.search(r'control=tcp data=stream server=\S+ pid=[1-9][0-9]*', client_log)), - 'marker_in_client': marker in client_log, - 'marker_in_server': marker in read(out / 'peer.server.log'), - 'marker_forwarded': marker in read(out / 'client.server.log'), - 'marker_assertion_failed': marker_assertion_failed, - 'output': str(out)} - - -def main(): - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('--build-dir', type=Path, required=True) - parser.add_argument('--verify-build-dir', type=Path) - parser.add_argument('--out', type=Path, required=True) - parser.add_argument('--case', action='append', choices=CASES, - help='Explicit subset; the default runs all four and requires a verify build') - args = parser.parse_args() - selected = args.case or list(CASES) - if 'verify' in selected and not args.verify_build_dir: - parser.error('the verify scenario requires --verify-build-dir configured with MOBILEGL_PIPE_VERIFY=ON') - args.out.mkdir(parents=True, exist_ok=True) - results = {} - for case in selected: - build = args.verify_build_dir if case == 'verify' else args.build_dir - arms = {} - for forward in (True, False): - name = 'forward-on' if forward else 'forward-off' - try: - arms[name] = run_arm(build, args.out / case / name, case, forward) - except Exception as error: - arms[name] = {'error': str(error)} - on, off = arms['forward-on'], arms['forward-off'] - if case == 'verify': - # PipeFill owns this marker in the client. Disabling peer forwarding - # must leave it visible; requiring red here would test a false design. - arms['expectation'] = 'client-owned marker remains green with peer forwarding disabled' - arms['proved'] = all(arm.get('passed') == 1 and arm.get('returncode') == 0 - and arm.get('tcp_armed') and arm.get('marker_in_client') - for arm in (on, off)) - else: - arms['expectation'] = 'server-owned marker passes with forwarding and fails without it' - arms['proved'] = bool(on.get('passed') == 1 and on.get('returncode') == 0 - and on.get('tcp_armed') and on.get('marker_in_server') and on.get('marker_forwarded') - and off.get('failed') == 1 and not off.get('skipped') and off.get('tcp_armed') - and off.get('marker_in_server') and not off.get('marker_forwarded') - and off.get('marker_assertion_failed') - and not off.get('marker_in_client')) - results[case] = arms - print(case + ': ' + ('PASS' if arms['proved'] else 'NOT PROVEN'), flush=True) - (args.out / 'summary.json').write_text(json.dumps(results, indent=2)) - return 0 if all(result['proved'] for result in results.values()) else 1 - - -if __name__ == '__main__': - raise SystemExit(main())