mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 20:28:32 +09:00
[CI] (Pipe): add FlushPendingRangesNow's three-tier drain to G5's byte-identical set
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
# G5's gate: "pool 与延迟释放原样搬" is LITERAL - nine functions in
|
||||
# G5's gate: "pool 与延迟释放原样搬" is LITERAL - TEN functions in
|
||||
# MobileGL/MG_Backend/DirectGLES/Managers.cpp are byte-identical after P3a.
|
||||
#
|
||||
# WHAT G5 CLAIMS, and why a diff of the file cannot say it. ARCHITECTURE.md:316 puts the three
|
||||
# persistently mapped rings and the buffer pool in the do-not-touch list, and :515 says what
|
||||
# protects them: `present` and eglSwapBuffers are strictly 1:1, so the rings' and the pool's retire
|
||||
# only ever happens inside Present, and a batching change would starve them. P3a rewrites the file
|
||||
# those nine functions live in - Ops_* becomes handle-shaped, the twin's gate is re-keyed - so the
|
||||
# those ten functions live in - Ops_* becomes handle-shaped, the twin's gate is re-keyed - so the
|
||||
# file's diff is large by design and says nothing about whether the pool moved. This gate extracts
|
||||
# the nine BODIES and compares them on their own.
|
||||
# the ten BODIES and compares them on their own.
|
||||
#
|
||||
# The nine, and what each one is (BRIEF-P3A.md D-F):
|
||||
# The ten, and what each one is (BRIEF-P3A.md D-F for the first nine, D-C and E's risk row for the
|
||||
# tenth):
|
||||
#
|
||||
# IsPoolable takes the server-side resource, never the frontend object
|
||||
# EnrollIntoPool the retireSerial = CurrentFrameSerial() + 1 stamp is load-bearing
|
||||
@@ -21,6 +22,23 @@
|
||||
# CreateRingStorage glBufferStorageEXT + persistent|coherent, retires at serial + 1
|
||||
# RingAvailable self-heals a stale context generation
|
||||
# RingAllocate the fast path on the hot upload route
|
||||
# FlushPendingRangesNow the three-tier drain: tier 1 is an INVALIDATE_BUFFER map + memcpy
|
||||
# for a whole-buffer flush or a range >= 128 KiB, tier 2 the upload
|
||||
# ring + glCopyBufferSubData, tier 3 UploadRangeNow
|
||||
#
|
||||
# THE TENTH IS HERE BY INTEGRATOR DECISION ID-11, resolving a contradiction inside the brief.
|
||||
# D-F's "Decision: nine functions" table omits FlushPendingRangesNow, but BRIEF-P3A.md:420 calls it
|
||||
# "a G5-protected function" and E's risk row at :1708 makes this gate the WHOLE mitigation for
|
||||
# "the three-tier drain silently changes tier because its caller now supplies bytes differently":
|
||||
# dropping the hot path from tier 1 into tier 3 costs the Mali WAR stall back, which is
|
||||
# MEASUREMENTS.md:87's p99 163 -> 21 ms and ROADMAP.md:19's headline number, and under ID-6
|
||||
# performance is RECORDED rather than gated, so no other gate stops it. A risk row whose named
|
||||
# mitigation does not exist is worse than an unmitigated one. It is appended LAST rather than
|
||||
# inserted, so the first nine lines of the D.0 baseline capture keep their positions.
|
||||
#
|
||||
# What "byte-identical" means for it, and it is the point of the row: the P3a handle arm must CALL
|
||||
# this function, not carry a copy of the ladder. A push arm that re-spells the three tiers beside
|
||||
# an untouched pull arm satisfies G1 and defeats this gate's reason to exist - two ladders drift.
|
||||
#
|
||||
# HOW A BODY IS EXTRACTED. The file is masked first - comments, string, char and raw-string
|
||||
# literals are replaced by spaces of the same length, so a brace or a parenthesis inside one can
|
||||
@@ -37,8 +55,8 @@
|
||||
# silent pass: a rename that this gate could not follow must not read as "nothing moved".
|
||||
#
|
||||
# Usage:
|
||||
# scripts/p3a_untouched_regions.sh <ref-a> <ref-b> compare the nine bodies at two git refs
|
||||
# scripts/p3a_untouched_regions.sh <ref> print the nine shas at one ref (the D.0
|
||||
# scripts/p3a_untouched_regions.sh <ref-a> <ref-b> compare the ten bodies at two git refs
|
||||
# scripts/p3a_untouched_regions.sh <ref> print the ten shas at one ref (the D.0
|
||||
# baseline capture: ... > p3a-before-untouched.sha)
|
||||
# scripts/p3a_untouched_regions.sh --self-test prove the comparison can go red
|
||||
#
|
||||
@@ -48,18 +66,22 @@
|
||||
# Both arguments are GIT REFS: the gate is about what landed, so an uncommitted edit is invisible
|
||||
# by design. Use HEAD after committing, which is what D.1 and the CI row do.
|
||||
#
|
||||
# Exit codes: 0 the nine bodies are identical at both refs (or a single ref was listed);
|
||||
# Exit codes: 0 the ten bodies are identical at both refs (or a single ref was listed);
|
||||
# 1 at least one moved - the first one in the fixed order is named on stderr;
|
||||
# 2 the gate could not run: a bad ref, a missing file, a name that is not defined
|
||||
# exactly once, or a self-test whose control failed to trip.
|
||||
set -u -o pipefail
|
||||
|
||||
SOURCE_PATH=MobileGL/MG_Backend/DirectGLES/Managers.cpp
|
||||
FUNCTIONS="IsPoolable EnrollIntoPool AcquireFromPool TrimBufferPool ClearBufferPool ProcessDeferredBufferReleases CreateRingStorage RingAvailable RingAllocate"
|
||||
# The function the self-test perturbs. Any of the nine would do; this one is small, has no
|
||||
# forward declaration and no overload, so a failure to trip is about the comparison rather than
|
||||
# about the extraction.
|
||||
SELF_TEST_FUNCTION=ClearBufferPool
|
||||
FUNCTIONS="IsPoolable EnrollIntoPool AcquireFromPool TrimBufferPool ClearBufferPool ProcessDeferredBufferReleases CreateRingStorage RingAvailable RingAllocate FlushPendingRangesNow"
|
||||
EXPECTED_FUNCTION_COUNT=10
|
||||
# The functions the self-test perturbs, one control each. ClearBufferPool is small, has no forward
|
||||
# declaration and no overload, so a failure to trip there is about the COMPARISON rather than about
|
||||
# the extraction. FlushPendingRangesNow is the opposite shape on purpose - the longest body in the
|
||||
# set, three nested tiers, its own early returns - and it is the ID-11 addition, so a control that
|
||||
# only ever perturbed the easy one would leave the tenth entry unproven: an entry that silently
|
||||
# extracted the wrong extent would compare equal forever.
|
||||
SELF_TEST_FUNCTIONS="ClearBufferPool FlushPendingRangesNow"
|
||||
|
||||
say() { echo "[p3a-untouched] $*" >&2; }
|
||||
|
||||
@@ -112,6 +134,15 @@ def mask(text):
|
||||
if text[j] != '\n':
|
||||
out[j] = ' '
|
||||
i = end
|
||||
elif c == "'" and i > 0 and (text[i - 1].isdigit() or
|
||||
(text[i - 1] in 'abcdefABCDEF' and i > 1 and
|
||||
text[i - 2] in "0123456789abcdefABCDEFxX")):
|
||||
# A C++14 DIGIT SEPARATOR (16'777'216, 0xff'ff), not a char literal. Treating it as one
|
||||
# would blank forward to the next apostrophe - which can be a whole function away, in a
|
||||
# comment - and silently swallow a brace, shifting a body's extent with no diagnostic.
|
||||
# There is no digit separator in Managers.cpp today; this is here so that adding one
|
||||
# cannot quietly turn this gate into a vacuous green.
|
||||
i += 1
|
||||
elif c in '"\'':
|
||||
quote = c
|
||||
out[i] = ' '
|
||||
@@ -174,6 +205,18 @@ def extract(path, names):
|
||||
hits = find_definition(text, masked, name)
|
||||
if len(hits) != 1:
|
||||
problems.append('%s: expected exactly one definition, found %d' % (name, len(hits)))
|
||||
if len(hits) > 1:
|
||||
# The expected shape of this failure, and it is a FINDING rather than a limitation:
|
||||
# a `#if MOBILEGL_PIPE_PUSH` arm that re-spells one of these bodies beside an
|
||||
# untouched `#else` copy satisfies G1 (the pull build's text did not move) and
|
||||
# defeats G5 (the push build compiles a second copy that can drift). The gate
|
||||
# cannot say which of the two is "the" body, and must not pick one.
|
||||
for begin, end in hits:
|
||||
problems.append(' ...definition at line %d, %d lines'
|
||||
% (text[:begin].count('\n') + 1,
|
||||
text[:end].count('\n') - text[:begin].count('\n') + 1))
|
||||
problems.append(' the handle arm must CALL the untouched %s, not carry a copy of '
|
||||
'it: two ladders drift (BRIEF-P3A.md:1708, ID-11)' % name)
|
||||
continue
|
||||
begin, end = hits[0]
|
||||
body = text[begin:end]
|
||||
@@ -189,7 +232,11 @@ def perturb(src, dst, names, target):
|
||||
sys.stderr.write('[p3a-untouched] cannot perturb %s: %d definitions\n' % (target, len(hits)))
|
||||
return 2
|
||||
begin, end = hits[0]
|
||||
brace = text.index('{', begin)
|
||||
# The opening brace is located in the MASKED text and then used as an offset into the original:
|
||||
# a brace inside a comment or a string on the signature line would otherwise send the
|
||||
# perturbation somewhere that is not the body, and the control would be proving the wrong
|
||||
# thing. Offsets are identical between the two by construction (mask() preserves length).
|
||||
brace = masked.index('{', begin)
|
||||
patched = (text[:brace + 1] +
|
||||
'\n // p3a_untouched_regions.sh --self-test: a body that MOVED.\n' +
|
||||
text[brace + 1:])
|
||||
@@ -239,9 +286,12 @@ compare_lists() {
|
||||
say "FIRST FUNCTION THAT MOVED: $name"
|
||||
say " $labelA $shaA"
|
||||
say " $labelB ${shaB:-<not found>}"
|
||||
say " G5 (ARCHITECTURE.md:316, :515) says the buffer pool, the deferred-release drain and"
|
||||
say " the three rings move VERBATIM. If this change is intended, it is not a P3a change and"
|
||||
say " it needs its own commit and its own reason; if it is not, revert the body."
|
||||
say " G5 (ARCHITECTURE.md:316, :515) says the buffer pool, the deferred-release drain, the"
|
||||
say " three rings and FlushPendingRangesNow's three-tier drain (BRIEF-P3A.md:420, :1708)"
|
||||
say " move VERBATIM. If this change is intended, it is not a P3a change and it needs its"
|
||||
say " own commit and its own reason; if it is not, revert the body. A push arm that carries"
|
||||
say " its own COPY of one of these bodies beside an untouched pull arm is the same finding:"
|
||||
say " the handle arm must CALL the untouched function, not re-spell it."
|
||||
else
|
||||
say "also moved: $name"
|
||||
fi
|
||||
@@ -262,15 +312,15 @@ if [ "${1:-}" = "--self-test" ]; then
|
||||
|
||||
cp -f "$SOURCE_PATH" "$WORK_DIR/pristine.cpp" || exit 2
|
||||
if ! python3 "$PY" extract "$WORK_DIR/pristine.cpp" "$FUNCTIONS" > "$WORK_DIR/pristine.sha"; then
|
||||
say "the extractor could not read the nine bodies out of the working tree's $SOURCE_PATH"
|
||||
say "the extractor could not read the ten bodies out of the working tree's $SOURCE_PATH"
|
||||
exit 2
|
||||
fi
|
||||
found=$(wc -l < "$WORK_DIR/pristine.sha")
|
||||
if [ "$found" -ne 9 ]; then
|
||||
say "extracted $found bodies, expected 9"
|
||||
if [ "$found" -ne "$EXPECTED_FUNCTION_COUNT" ]; then
|
||||
say "extracted $found bodies, expected $EXPECTED_FUNCTION_COUNT"
|
||||
exit 2
|
||||
fi
|
||||
say "positive control: 9 bodies extracted from the working tree"
|
||||
say "positive control: $EXPECTED_FUNCTION_COUNT bodies extracted from the working tree"
|
||||
|
||||
cp -f "$WORK_DIR/pristine.cpp" "$WORK_DIR/copy.cpp"
|
||||
python3 "$PY" extract "$WORK_DIR/copy.cpp" "$FUNCTIONS" > "$WORK_DIR/copy.sha" || exit 2
|
||||
@@ -284,36 +334,42 @@ if [ "${1:-}" = "--self-test" ]; then
|
||||
# The second positive control, and it is the one that matters for P3a: the rest of this file
|
||||
# IS going to be rewritten (the Ops_* become handle-shaped, the twin's gate is re-keyed), so a
|
||||
# gate that fired on any edit to Managers.cpp would have to be switched off in the same week it
|
||||
# landed. An edit outside the nine bodies must be invisible here.
|
||||
{ echo "// p3a_untouched_regions.sh --self-test: an edit OUTSIDE the nine bodies."; \
|
||||
# landed. An edit outside the ten bodies must be invisible here.
|
||||
{ echo "// p3a_untouched_regions.sh --self-test: an edit OUTSIDE the ten bodies."; \
|
||||
cat "$WORK_DIR/pristine.cpp"; } > "$WORK_DIR/outside.cpp"
|
||||
python3 "$PY" extract "$WORK_DIR/outside.cpp" "$FUNCTIONS" > "$WORK_DIR/outside.sha" || exit 2
|
||||
if ! compare_lists "$WORK_DIR/pristine.sha" "$WORK_DIR/outside.sha" "pristine" "outside" \
|
||||
2>/dev/null; then
|
||||
say "POSITIVE CONTROL FAILED: an edit OUTSIDE the nine bodies was reported as one of them"
|
||||
say "POSITIVE CONTROL FAILED: an edit OUTSIDE the ten bodies was reported as one of them"
|
||||
say "moving. This gate would fire on every P3a commit to Managers.cpp and would have to be"
|
||||
say "silenced, which is the same as not having it."
|
||||
exit 2
|
||||
fi
|
||||
say "positive control: an edit outside the nine bodies is invisible"
|
||||
say "positive control: an edit outside the ten bodies is invisible"
|
||||
|
||||
python3 "$PY" perturb "$WORK_DIR/pristine.cpp" "$WORK_DIR/perturbed.cpp" \
|
||||
"$SELF_TEST_FUNCTION" "$FUNCTIONS" || exit 2
|
||||
python3 "$PY" extract "$WORK_DIR/perturbed.cpp" "$FUNCTIONS" > "$WORK_DIR/perturbed.sha" || exit 2
|
||||
if compare_lists "$WORK_DIR/pristine.sha" "$WORK_DIR/perturbed.sha" "pristine" "perturbed" \
|
||||
2> "$WORK_DIR/perturbed.err"; then
|
||||
say "NEGATIVE CONTROL DID NOT TRIP: $SELF_TEST_FUNCTION's body was changed and the comparison"
|
||||
say "still reported every function as identical. This gate cannot go red for the reason it"
|
||||
say "exists, so every green it has ever printed means nothing."
|
||||
exit 2
|
||||
fi
|
||||
if ! grep -q "FIRST FUNCTION THAT MOVED: $SELF_TEST_FUNCTION" "$WORK_DIR/perturbed.err"; then
|
||||
say "NEGATIVE CONTROL TRIPPED FOR THE WRONG REASON: the comparison went red but did not name"
|
||||
say "$SELF_TEST_FUNCTION as the first function that moved. It said:"
|
||||
sed 's/^/[p3a-untouched] /' "$WORK_DIR/perturbed.err" >&2
|
||||
exit 2
|
||||
fi
|
||||
say "negative control: a perturbed $SELF_TEST_FUNCTION body is reported, and named"
|
||||
# One negative control per SELF_TEST_FUNCTIONS entry. Each is run on its own, from the pristine
|
||||
# copy, so the message it produces has to NAME that function - a control that only proved "some
|
||||
# body moved" would not distinguish "the tenth entry is compared" from "the tenth entry is
|
||||
# extracted as an empty range and every comparison of it is vacuous".
|
||||
for target in $SELF_TEST_FUNCTIONS; do
|
||||
python3 "$PY" perturb "$WORK_DIR/pristine.cpp" "$WORK_DIR/perturbed.cpp" \
|
||||
"$target" "$FUNCTIONS" || exit 2
|
||||
python3 "$PY" extract "$WORK_DIR/perturbed.cpp" "$FUNCTIONS" > "$WORK_DIR/perturbed.sha" || exit 2
|
||||
if compare_lists "$WORK_DIR/pristine.sha" "$WORK_DIR/perturbed.sha" "pristine" "perturbed" \
|
||||
2> "$WORK_DIR/perturbed.err"; then
|
||||
say "NEGATIVE CONTROL DID NOT TRIP: $target's body was changed and the comparison"
|
||||
say "still reported every function as identical. This gate cannot go red for the reason it"
|
||||
say "exists, so every green it has ever printed means nothing."
|
||||
exit 2
|
||||
fi
|
||||
if ! grep -q "FIRST FUNCTION THAT MOVED: $target" "$WORK_DIR/perturbed.err"; then
|
||||
say "NEGATIVE CONTROL TRIPPED FOR THE WRONG REASON: the comparison went red but did not name"
|
||||
say "$target as the first function that moved. It said:"
|
||||
sed 's/^/[p3a-untouched] /' "$WORK_DIR/perturbed.err" >&2
|
||||
exit 2
|
||||
fi
|
||||
say "negative control: a perturbed $target body is reported, and named"
|
||||
done
|
||||
say "self-test passed"
|
||||
exit 0
|
||||
fi
|
||||
@@ -323,7 +379,7 @@ case $# in
|
||||
1)
|
||||
extract_ref "$1" one || exit 2
|
||||
cat "$WORK_DIR/one.sha"
|
||||
say "listed the nine bodies at $1"
|
||||
say "listed the $EXPECTED_FUNCTION_COUNT bodies at $1"
|
||||
exit 0
|
||||
;;
|
||||
2) ;;
|
||||
@@ -338,7 +394,8 @@ extract_ref "$2" b || exit 2
|
||||
cat "$WORK_DIR/b.sha"
|
||||
|
||||
if compare_lists "$WORK_DIR/a.sha" "$WORK_DIR/b.sha" "$1" "$2"; then
|
||||
say "the nine pool / deferred-release / ring functions are byte-identical between $1 and $2"
|
||||
say "the $EXPECTED_FUNCTION_COUNT pool / deferred-release / ring / flush-drain functions are"
|
||||
say "byte-identical between $1 and $2"
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
|
||||
Reference in New Issue
Block a user