diff --git a/docs/Disaggregated/notes/p7/device-window-2/README.md b/docs/Disaggregated/notes/p7/device-window-2/README.md index 60b0f5b3..b1dc7b1a 100755 --- a/docs/Disaggregated/notes/p7/device-window-2/README.md +++ b/docs/Disaggregated/notes/p7/device-window-2/README.md @@ -7,6 +7,12 @@ - 集成者裁定(2026-09-23)已落进脚本与 RUNBOOK §1/§3:CTS rate = Pass/(Pass+Fail)、五块缺一即 INCOMPLETE、UBO 记 unrun; 门 3 单一 boot 会话(否则 INVALID-SESSION)、臂内逐位相同是门而跨臂同图只 WARN、monolith 臂身份核验;`.done` 只在产物齐全时写; restore 把 `$BASE` 库推回 `mgcts`。 +- 评审后的加固(同日):reducer 不再从「无」得出 PASS——caselist 读不到 / 为空的块记 MISSING(输出目录另存 caselist 副本, + 构建树删了也能判),AFTER 无一个 Pass/Fail 的块 FAIL,算不出 delta 的块不会 PASS;两边 rate 同口径(只算两边都有结果的用例, + `$BASE` 缺的与 NS 变化打 WARN),旧口径的两个 rate 逐块列出;只有 36/36、reboot-clean 的门 3 与五块完整的 CTS 才退出 0 + (否则 `PASS-SUBSET` / `PASS-NOT-REBOOT-CLEAN`);`50-cts-after.sh` 用与判读同一段代码(`60-reduce.py --check-block`)判块完整后才写 `.done`; + 窗口从 preflight 到 restore 持有本波次共享设备锁 `/home/swung/w7/locks/2f7cbe2e.lock`(`flock -o`,不把锁带给残留子进程)。 + 自检 81/81(含只由 rate 定的块:dsa −0.541 pp FAIL、−1.081 pp FAIL);cfd3dde9 的 reducer 在同一套检查上 62/81。 - `$BASE` 的机器可读 JSON 此前缺失,已由 `cts_multi_report.py --adopt-legacy` 从 `~/w7/logs/cts-a64/base/runs` 重生成: `tools/cts/baselines/base-DirectVulkan-monolith-p7w1-9be62cbc.{json,md}`(逐块数字与 `../device-window-1/CTS-base/README.md` 一致)。 @@ -18,6 +24,6 @@ | `dry-run/dry-p7w6-b-bsl/` | 修 supervisor 探测与采样器预停后重跑:bsl inproc + spawn 的 maps / RSS 峰值与 `wbuf[]` | | `dry-run/dry-p7w6-c-cts-ssbo-dsa/` | CTS 计时样本:p7w6 inproc 全 ssbo + dsa | | `dry-run/build-p7w7dry-14e1c8b9/` | `10-build.sh 14e1c8b9 p7w7dry-14e1c8b9`:APK 131 s、主机制品 223 s;**未安装** | -| `dry-run/pre-14e1c8b9-cts/` | pipe 头 lib 的 CTS 五块预读(inproc)与红例的 monolith / inproc 逐例归属(`attribution.txt`)——门 5 在该头上会红,见 RUNBOOK §6;`verdict.txt` 是旧口径读数,`verdict-contract.txt` 是合同口径(dsa −0.272 pp,红在新 crash) | +| `dry-run/pre-14e1c8b9-cts/` | pipe 头 lib 的 CTS 五块预读(inproc)与红例的 monolith / inproc 逐例归属(`attribution.txt`)——门 5 在该头上会红,见 RUNBOOK §6;`verdict.txt` 是旧口径读数,`verdict-contract.txt` 是现行 reducer 的合同口径(dsa −0.272 pp,红在新 crash;每块 `info` 行列旧口径的两个 rate) | 原始归档(actual PNG、角色日志、qpa)在 `~/w7/logs/devprep/w2//`。 diff --git a/docs/Disaggregated/notes/p7/device-window-2/tools/50-cts-after.sh b/docs/Disaggregated/notes/p7/device-window-2/tools/50-cts-after.sh index 37ffe69e..53f3d01b 100755 --- a/docs/Disaggregated/notes/p7/device-window-2/tools/50-cts-after.sh +++ b/docs/Disaggregated/notes/p7/device-window-2/tools/50-cts-after.sh @@ -13,11 +13,16 @@ # # Arm proof before any block: mgprobe (the SKILL step-5 preflight) and one glcts case, each under # the AFTER environment, must log ConfigLoader's "Config: MOBILEGL_TRANSPORT=inproc - the MGPipe -# record stream" and a "Config: IPC" line with strict=1 role-split-state=1 run-ahead=1. -# RESUMABLE per block (/cts/runs//.done), and a block's .done is written ONLY when its -# report-.json exists and holds results: a block without one is logged INCOMPLETE and redone -# from scratch by the next run (exit 3 = the loop finished with such blocks; window.sh carries on -# and leaves the step for the next run). 60-reduce.py requires all five blocks. The UBO block +# record stream" and a "Config: IPC" line with strict=1 role-split-state=1 run-ahead=1; the proof +# case's StatusCode is recorded in arm-proof.txt (a WARN when it is neither Pass nor Fail). +# RESUMABLE per block (/cts/runs//.done), and a block's .done is written ONLY when the +# block is a complete reading: run_cts.py exited 0 and 60-reduce.py --check-block (the very test the +# verdict applies) finds report-.json with a result for every non-skipped case of the +# caselist and unrun.txt empty. Otherwise the block is logged INCOMPLETE (runs//complete.txt +# says why) and redone from scratch by the next run (exit 3 = the loop finished with such blocks; +# window.sh carries on and leaves the step for the next run). Each block keeps a copy of the +# caselist it ran (runs//caselist.txt) beside its path, so the window's output reduces on its +# own after the build tree is gone. 60-reduce.py requires all five blocks. The UBO block # (GTF-GL46 uniform_buffer_object) is not run: this glcts has no GTF module (ID-P7-16). # --limit N runs the first N cases of each block (the dry run). --restore-base-lib re-pushes the # $BASE library at once; 90-restore.sh does it at the end of every window anyway. @@ -93,6 +98,14 @@ if [ ! -f "$OUT/preflight.done" ]; then Ash "cd $W2_CTS_DEV && echo $first > p7w7-proof.txt && $DEV_ENV LD_LIBRARY_PATH=. ./glcts --deqp-caselist-file=p7w7-proof.txt --deqp-surface-type=fbo --deqp-surface-width=256 --deqp-surface-height=256 --deqp-gl-config-name=rgba8888d24s8 --deqp-terminate-on-device-lost=disable --deqp-log-images=disable --deqp-log-shader-sources=disable --deqp-log-filename=p7w7-proof.qpa > /dev/null 2>&1; echo RC=\$?; grep -o 'StatusCode=\"[A-Za-z]*\"' p7w7-proof.qpa; rm -f p7w7-proof.txt p7w7-proof.qpa" > "$OUT/proof-glcts.txt" 2>&1 A logcat -d > "$OUT/proof-glcts-logcat.txt" 2>&1 arm_proof "glcts $first" "$OUT/proof-glcts-logcat.txt" | tee -a "$OUT/arm-proof.txt" || die "glcts did not prove the inproc arm (see $OUT/proof-glcts-logcat.txt)" + # Information: a proof case that is NotSupported (or crashed) says the AFTER library may not + # reach a GL 4.6 core context at all; 60-reduce.py FAILs a block without a single Pass/Fail. + proof_status=$(sed -n 's/.*StatusCode="\([A-Za-z]*\)".*/\1/p' "$OUT/proof-glcts.txt" | sed -n 1p) + echo "glcts $first: StatusCode=${proof_status:-none}" | tee -a "$OUT/arm-proof.txt" + case "$proof_status" in + Pass|Fail) ;; + *) log "WARN: the proof case $first gave StatusCode=${proof_status:-none} under the AFTER environment (see $OUT/proof-glcts.txt)";; + esac touch "$OUT/preflight.done" fi @@ -110,6 +123,7 @@ for b in $BLOCKS; do caselist="$OUT/caselists/p7-$b-gl46.first$LIMIT.txt" fi echo "$caselist" > "$run/caselist.path" + cp "$caselist" "$run/caselist.txt" pin=$(w2_pin_check "$W2_TOOLS" "$OUT/pin-before-$b.txt") w2_wake log "block $b start ($(grep -vc '^#' "$caselist") cases, pin $pin)" @@ -127,13 +141,17 @@ for b in $BLOCKS; do for f in unrun hung crashed; do [ -s "$run/$f.txt" ] && log " [$b] $f.txt: $(wc -l < "$run/$f.txt") lines"; done grep -E 'Pass|Fail|NotSupported|Crash|rate' "$OUT/report-$b.txt" | head -8 | sed "s/^/ [$b] /" w2_timing "$(w2_out "$STAMP")" "cts-$b" "$t0" "$t1" "limit=$LIMIT" - if [ -s "$OUT/report-$b.json" ] && python3 -c 'import json, sys; sys.exit(0 if json.load(open(sys.argv[1]))["results"] else 1)' \ - "$OUT/report-$b.json" 2> /dev/null; then + # The verdict's own completeness test (60-reduce.py block_scope), so a block the reducer would + # call INCOMPLETE is never frozen by a .done that a resume then skips. + complete=$(python3 "$W2_HERE/60-reduce.py" --check-block "$run" "$OUT/report-$b.json" 2>&1); crc=$? + [ $rc -eq 0 ] || { complete="run_cts.py rc=$rc; $complete"; crc=3; } + echo "$complete" > "$run/complete.txt" + if [ $crc -eq 0 ]; then printf 'rc=%s seconds=%s pin_after=%s finished=%s\n' "$rc" "$((t1 - t0))" "$pin" "$(date -Is)" > "$run/.done" - log "block $b rc=$rc $((t1 - t0))s" + log "block $b rc=$rc $((t1 - t0))s: $complete" else INCOMPLETE=$((INCOMPLETE + 1)) - log "block $b rc=$rc $((t1 - t0))s INCOMPLETE: no report-$b.json with results (qpa_report rc=$qrc, see $OUT/report-$b.txt); no .done, the next run redoes the block" + log "block $b rc=$rc $((t1 - t0))s INCOMPLETE: $complete (qpa_report rc=$qrc, see $OUT/report-$b.txt); no .done, the next run redoes the block" fi done @@ -154,7 +172,7 @@ if [ $RESTORE_BASE -eq 1 ]; then fi w2_timing "$(w2_out "$STAMP")" cts "$t_all" "$(date +%s)" "blocks=[$BLOCKS] limit=$LIMIT incomplete=$INCOMPLETE" if [ $INCOMPLETE -gt 0 ]; then - log "=== CTS AFTER INCOMPLETE $STAMP: $INCOMPLETE block(s) without a report; run the same command again to redo them ===" + log "=== CTS AFTER INCOMPLETE $STAMP: $INCOMPLETE block(s) not a complete reading (runs//complete.txt); run the same command again to redo them ===" exit 3 fi log "=== CTS AFTER DONE $STAMP ===" diff --git a/docs/Disaggregated/notes/p7/device-window-2/tools/RUNBOOK.md b/docs/Disaggregated/notes/p7/device-window-2/tools/RUNBOOK.md index ac30803e..8650feba 100644 --- a/docs/Disaggregated/notes/p7/device-window-2/tools/RUNBOOK.md +++ b/docs/Disaggregated/notes/p7/device-window-2/tools/RUNBOOK.md @@ -17,8 +17,12 @@ M2/M3 的 bsl-esc-menu 设备读数(`MOBILEGL_PIPE_STATS` 下的 `/proc// 2. Windows 侧 adb server 在跑并看得见 `2f7cbe2e`。WSL 的 `/usr/sbin/adb` 经 mirrored networking 连的就是它; server 没了在 **Windows** 上 `adb start-server`,不要在 WSL 里起(会抢 5037 而看不见 USB)。 3. 手机 `/data/local/tmp/mgcts` 有 `glcts`、`gl_cts/`、`mgprobe`(窗口 1 部署,现仍在;缺则 `sh ~/w7/logs/cts-a64/deploy/deploy.sh 2f7cbe2e`)。 -4. 窗口期间没有别的 agent 用手机、主机不并行跑 gate(超时是墙钟)。手机若有锁屏密码,reboot 后要手动解锁一次 - (preflight 等 `sys.user.0.ce_available=true` 最多 10 min)。 +4. 窗口期间没有别的 agent 用手机、主机不并行跑 gate(超时是墙钟)。「没有别的 agent」由锁保证:本波次所有 agent 都以 + `flock -w 3600 /home/swung/w7/locks/2f7cbe2e.lock ` 用手机;`window.sh` 在 build 之后、preflight 之前拿**同一把锁** + (`lib.sh` 的 `W2_SHARED_LOCK`),一直持有到 restore 结束——两步之间别的 agent 插不进来;单独起的某一步自己拿、自己放。 + 拿锁是 `flock -o`:脚本以同样的参数在 flock 进程下重跑,锁 fd 不传给子进程(步骤留下的 adb server / supervisor 启动器不会把锁 + 带出窗口)。等锁最多 `W2_SHARED_LOCK_WAIT`(默认 3600 s),等不到退出 75、不碰手机。窗口自己的步骤锁 `W2_LOCK` 照旧。 + 手机若有锁屏密码,reboot 后要手动解锁一次(preflight 等 `sys.user.0.ce_available=true` 最多 10 min)。 ## 1. 一条命令 @@ -27,11 +31,14 @@ bash ~/w7/notes/p7/window2/detach.sh ~/w7/logs/p7w7/window-.log ~/w7/notes tail -f ~/w7/logs/p7w7/window-.log # 结束行 "=== WINDOW 2 DONE ",其后打印 verdict ``` -`detach.sh` = `setsid nohup … < /dev/null &`,与终端 / 工具 10 min 上限脱钩。**中断后原命令重跑即续**: +`detach.sh` = `setsid nohup … < /dev/null &`,与终端 / 工具 10 min 上限脱钩;它打印的 pid 是新会话的进程组长, +要停整个窗口用 `kill -- -`(只杀 pid 本身会留下正在跑的那一步,且放掉共享锁)。**中断后原命令重跑即续**: 每步完成写 `/steps/.done`;门 3 逐 (arm, case) 写 `gate3/state//.done`——**只在该对要求的每一遍都留下 `result.json` 与 actual PNG 时才写**,否则日志记 `INCOMPLETE`、`gate3/progress.tsv` 第 6 列 `INCOMPLETE`、不写 `.done`,下次整对重跑 -(残档先删;遍数按首跑冻结在 `gate3/arms.txt` 的 `repeat=`);CTS 逐块写 `cts/runs//.done`——**只在 `cts/report-.json` -存在且有结果时才写**。循环跑完仍有 INCOMPLETE 时 `30-gate3.sh` / `50-cts-after.sh` 退出 3:`window.sh` 不给该步写 `.done`、 +(残档先删;遍数按首跑冻结在 `gate3/arms.txt` 的 `repeat=`);CTS 逐块写 `cts/runs//.done`——**只在该块是完整读数时才写**: +`run_cts.py` 退出 0,且 `60-reduce.py --check-block`(与判读**同一段代码**)确认 `cts/report-.json` 对 caselist 里每个未 skip 的用例 +都有结果、`unrun.txt` 为空;否则不写,原因进 `cts/runs//complete.txt`。每块另存所跑 caselist 的副本 `cts/runs//caselist.txt` +(`caselist.path` 指向的构建树删掉之后,输出目录仍能单独判读)。循环跑完仍有 INCOMPLETE 时 `30-gate3.sh` / `50-cts-after.sh` 退出 3:`window.sh` 不给该步写 `.done`、 照走后面各步(reduce 与 restore 每次都跑),结尾打 `WINDOW 2 INCOMPLETE … left open`;原命令再跑一次只补缺的——若上一遍已 restore, 它先以 `21-preflight.sh `(不重启)回到**同一** boot 会话(重新钉频、stay-on;boot_id 变了就停;`reboot-clean.txt` 不改写, `session/resumed.txt` 记一笔)。 @@ -61,8 +68,8 @@ boot_id,与它不同立即停;每对的 `.done` 与每遍归档的 `repeat-N | 3 | `20-install.sh ` | `adb install -r`;**设备上 `base.apk` 的 sha256 必须等于签名 APK**,versionName 带 ``;等 dex2oat 空闲 | ~1 min | | 4 | `30-gate3.sh ` | 36 例(`--matrix` 的 DirectVulkan 集减 `create-indirect` / `1.21.11-main-menu` / `photon-v1.3b`,冻结在 `gate3/cases.txt`)× 四臂;iterationrp 带 `apk.yml:460-462` 三个 Magma 旋钮;每遍 result.json / actual PNG / 双角色日志 / transport-proof / logcat 归档;每臂前后 pin check;每对前后核 boot_id(§1「会话」),`.done` 只在每遍都有 result.json + actual PNG 时写,否则退出 3 留待续跑 | 55–65 min | | 5 | `40-bsl-stats.sh ` | bsl-esc-menu × {inproc, spawn} 各 1 遍,`MOBILEGL_PIPE_STATS=1 PERIOD=1`;先 force-stop 再起设备端 root 采样器(0.25 s:maps 行数、VmRSS、VmHWM;排除 argv 带 `tcp://` 的 supervisor server);`wbuf[]` 从归档日志取 | ~1 min | -| 6 | `50-cts-after.sh ` | APK 的 arm64 `libMobileGL.so` 推到 `mgcts` 并在设备上核 sha256;AFTER 环境 = `$BASE` 的 flag + `MOBILEGL_TRANSPORT=inproc` + `ROLE_SPLIT_STATE=1 STRICT_ERRORS=1 RUN_AHEAD=1`;臂证明(mgprobe PASS 且 logcat 有 inproc 解析句与 `Config: IPC … strict=1 role-split-state=1 run-ahead=1`、0 Fatal;再用一个 glcts 用例证一次);五块依次跑(顺序同 `$BASE`),每块 `qpa_report --json`(块的 `.done` 只在 `report-.json` 有结果时写,否则退出 3 留待续跑),最后 `cts_multi_report` 出与 `$BASE` 同形的 JSON;UBO(GTF)块不跑:本 glcts 无 GTF 模块(ID-P7-16) | 6–7 min(pipe 头 lib inproc 实测 358 s;`$BASE` monolith 372 s) | -| 7 | `60-reduce.py ` | 写 `verdict.txt` / `verdict.json`(见 §3);每遍都跑。自检:`python3 60-reduce-selftest.py`(主机,不占设备) | <10 s | +| 6 | `50-cts-after.sh ` | APK 的 arm64 `libMobileGL.so` 推到 `mgcts` 并在设备上核 sha256;AFTER 环境 = `$BASE` 的 flag + `MOBILEGL_TRANSPORT=inproc` + `ROLE_SPLIT_STATE=1 STRICT_ERRORS=1 RUN_AHEAD=1`;臂证明(mgprobe PASS 且 logcat 有 inproc 解析句与 `Config: IPC … strict=1 role-split-state=1 run-ahead=1`、0 Fatal;再用一个 glcts 用例证一次,其 StatusCode 记进 `arm-proof.txt`,不是 Pass / Fail 时打 WARN);五块依次跑(顺序同 `$BASE`),每块 `qpa_report --json`(块的 `.done` 只在 `run_cts` 退出 0 且 `60-reduce.py --check-block` 判完整时写,否则退出 3 留待续跑),最后 `cts_multi_report` 出与 `$BASE` 同形的 JSON;UBO(GTF)块不跑:本 glcts 无 GTF 模块(ID-P7-16) | 6–7 min(pipe 头 lib inproc 实测 358 s;`$BASE` monolith 372 s) | +| 7 | `60-reduce.py ` | 写 `verdict.txt` / `verdict.json`(见 §3);每遍都跑。自检:`python3 60-reduce-selftest.py`(主机,不占设备,~4 s) | <10 s | | 8 | `90-restore.sh ` | Home;`am force-stop` 包(回放后缓存的 app 进程与 spawn 回放留下的 `libMobileGLServer.so @mgl-…` 子进程会一直占着几百 MB);**把 `$BASE` 库推回 `/data/local/tmp/mgcts`**(设备 sha256 ≠ `$BASE` = `IDENTITY.txt` 的 `7f58aa0b…` 时才推,推后在设备上核 sha,结果行进 `restored.txt`);supervisor 若开窗时在跑则以 `tcp_device_server.py start --allow-idle`(同 listen / token / Doze 状态文件)在**新 APK**上重启并核实在听;解钉频(MIUI 启动 boost 会让 `check` 短暂读成 DRIFT,重读至多 20 s);stay-on 复原;每遍都跑(幂等) | ~15 s | 合计:构建 ~6 min + 设备 ~65–80 min ≈ **1.3–1.5 h**。外推依据见 §5。 @@ -71,7 +78,8 @@ boot_id,与它不同立即停;每对的 `.done` 与每遍归档的 `repeat-N - **门 3**,先看 `-- session` 行:`session/boot-id.txt` 与四臂的每条记录(`gate3/state//.done` 的 `boot_id` / `boot_id_before`、 每遍 `repeat-NN/boot_id.txt`)必须是**同一个** boot_id;有第二个值、有记录不带 boot_id(含本规则之前的旧 `.done`)、或缺 `session/boot-id.txt`, - 整门判 **`GATE3 INVALID-SESSION`**,与图无关。会话不是 reboot-clean(`session/reboot-clean.txt`)时末行注明「不是判据」。 + 整门判 **`GATE3 INVALID-SESSION`**,与图无关。全部通过但不是门本身的读数时不记 `PASS`:分母不是 36 记 **`PASS-SUBSET`**, + 会话不是 reboot-clean(`session/reboot-clean.txt`)记 **`PASS-NOT-REBOOT-CLEAN`**,末行注明「不是判据」,退出码非 0。 逐例 `PASS / FAIL / INCOMPLETE` + 原因:monolith 本身通过且**确是 monolith**(该遍 `mobilegl.log`(及 client / server 日志)没有 `Config: IPC` 也没有 `MOBILEGL_TRANSPORT=` 行;没有日志即「未证」);inproc 与 spawn 各 3 遍齐全、该对有 `.done`、每遍臂证明 (`transport-proof.json` passed)通过、每遍 ssim ≥ 阈值、**同一臂内三遍 actual PNG SHA-256 相同**、每遍 `|ssim − monolith| ≤ 0.0005`; @@ -84,25 +92,37 @@ boot_id,与它不同立即停;每对的 `.done` 与每遍归档的 `repeat-N 不同的例各打一行 `WARN cross-arm : monolith | inproc | spawn `;WARN 不改变该例与门 3 的判定 (跨臂的门是每遍 `|ssim − monolith| ≤ 0.0005`)。 - **BSL**:每臂每进程(app / server)的 maps 行数峰值、VmRSS / VmHWM 峰值、`wbuf[wbufs wlivepk wdefpk wdefsync]`。 -- **CTS**:五块(shader-image / ssbo / dsa / texture / packed-pixels)**缺一不可**:每块要有 `cts/runs//.done` 与非空、有结果的 - `cts/report-.json`,否则该块记 `MISSING`、整体 `CTS INCOMPLETE`、退出码 ≠ 0。UBO 块(`GTF-GL46.gtf31.GL3Tests.uniform_buffer_object*`, +- **CTS**:五块(shader-image / ssbo / dsa / texture / packed-pixels)**缺一不可**:每块要有 `cts/runs//.done`、非空且有结果的 + `cts/report-.json`,以及读得到、非空的 caselist(先用输出目录里的副本 `runs//caselist.txt`,没有才用 `caselist.path` + 所指的文件;指向的文件没了、是空的、或两者都没有,**不**拿报告自己的用例顶替),否则该块记 `MISSING`、整体 `CTS INCOMPLETE`、退出码 ≠ 0—— + 0 例的块永远不是读数。UBO 块(`GTF-GL46.gtf31.GL3Tests.uniform_buffer_object*`, `p7-ubo-gl46.txt` 90 例)显式记 `unrun (not in this glcts)`(设备 glcts 无 GTF 模块,ID-P7-16),不入判据。 每块 BASE 与 AFTER 的 `P/F/NS/W/X`(W = CompatibilityWarning / QualityWarning 等,X = Crash / Timeout / InternalError / ResourceError / DeviceHang / Incomplete), **rate = Pass / (Pass + Fail)**(§7.3 原文:NS 不入分母;warning 与 crash 也不在分母里),delta pp;块判据 delta ≥ −0.5 pp、 **新增 crash = 0 单独硬红**(AFTER 为 X 而 BASE 不是——Fail→Crash 也算——另加 hung.txt;X 也是非 Pass,进 Pass→非 Pass 名单)、`unrun.txt` 为空。 - `(info)` 列是旧口径 Pass / (结果数 − NS) 的 delta,只供对照,**不判**。另列 Pass→非 Pass 与非 Pass→Pass 的逐例名单;AFTER 高出 0.5 pp 以上只标注不判红。 -- 末行 `WINDOW2 GATE3 | CTS -> exit `:两门都 PASS 才是 0;门 3 没跑(无 `gate3/cases.txt`)记 `NOT-RUN`,同样非 0。 + 两边的 rate 只在**两边都有结果的用例**上算(同口径);`$BASE` 没有的用例打 `WARN : N case(s) have no $BASE result`,不进两边的 rate, + 其中的 crash 仍算新增。AFTER 一个 Pass / Fail 都没有而 `$BASE` 有(例如全变 NotSupported:rate 无从算起,`$BASE` 的 Pass 全丢)→ 该块 **FAIL**; + `$BASE` 自己没有 Pass / Fail → 该块 INCOMPLETE——算不出 delta 的块永远不是 PASS。NotSupported 数与 `$BASE` 不同时打 + `WARN : NotSupported …`(只作信息:合同口径 NS 不入分母;变成 NS 的 Pass 在 Pass→非 Pass 名单里)。 + `(info)` 列与每块的 `info` 行是旧口径 Pass / (结果数 − NS):BASE、AFTER 两个 rate 与 delta,只供对照,**不判**。另列 Pass→非 Pass 与非 Pass→Pass 的逐例名单; + AFTER 高出 0.5 pp 以上只标注不判红。某块用的不是工具树里完整的 `p7--gl46.txt`(`--limit` 子集)时,全部通过也只记 **`CTS PASS-SUBSET`**。 +- 末行 `WINDOW2 GATE3 | CTS -> exit `:门 3 为 `PASS`(36/36、reboot-clean、同一会话)且 CTS 为 `PASS`(五块完整)才是 0; + `PASS-SUBSET` / `PASS-NOT-REBOOT-CLEAN` 不是判据,退出 1;门 3 没跑(无 `gate3/cases.txt`)记 `NOT-RUN`,同样非 0。 `reduce` 的退出码只作记录:红的门也要走完 restore。 -- 自检(主机,秒级):`python3 60-reduce-selftest.py`——造目录验:缺块 → `CTS INCOMPLETE`;boot_id 变 / 旧 `.done` / 缺 `boot-id.txt` → - `GATE3 INVALID-SESSION`;monolith 日志带 `Config: IPC` → FAIL;跨臂不同 → WARN 仍 PASS;臂内不同 → FAIL;dry run 数据上的合同口径 +- 自检(主机,~4 s):`python3 60-reduce-selftest.py`(门 3 按 36 例造)——造目录验:缺块 → `CTS INCOMPLETE`;boot_id 变 / 旧 `.done` / 缺 `boot-id.txt` → + `GATE3 INVALID-SESSION`;monolith 日志带 `Config: IPC` → FAIL;跨臂不同 → WARN 仍 PASS;臂内不同 → FAIL;**只由 rate 定**的块 + (dsa 1 / 2 / 4 例 Pass→Fail:−0.270 pp PASS、−0.541 pp FAIL、364/6/0/1/0 −1.081 pp FAIL,无 crash);caselist 没了 / 为空 / 全 NotSupported + → MISSING / FAIL 而非 PASS;2 例门 3、非 reboot-clean、`--limit` CTS → `PASS-SUBSET` / `PASS-NOT-REBOOT-CLEAN` 且退出非 0; + `$BASE` 缺的用例与 NS 变化 → WARN;`--check-block` 的 0 / 3;dry run 数据上的合同口径 (`60-reduce-selftest-pre14e1c8b9.json` = pre-14e1c8b9 五块相对 `$BASE` 的差异)dsa −0.272 pp;`~/w7/logs/devprep/w2/pre-14e1c8b9` 在时再对原始输出验一遍。 + `--reducer ` 可对别的 reducer 跑同一套检查(red-once)。 ## 4. 入库清单 → `docs/Disaggregated/notes/p7/device-window-2/` `verdict.txt`、`verdict.json`、`timing.tsv`;`session/`(`found.env`、`reboot-clean.txt`、`boot-id*.txt`、`apk.sha256`、`apk-on-device.sha256`、 `apk-install.txt`、`pin-*.txt`、`device.txt`、`restored.txt`);`gate3/{cases.txt,excluded.txt,arms.txt,progress.tsv,pin-*.txt}`; `bsl//{peaks.txt,wbuf.txt,wbuf-gauges.txt}`;`cts/{IDENTITY 即 deploy/IDENTITY.txt,arm-proof.txt,preflight.txt,report-*.txt,report-*.json,after-*.json,after-*.md,pin-*.txt}`、 -`cts/runs/*/{crashed,hung,unrun,skipped}.txt`。归档图像与日志(`gate3/archive/`、`bsl/*/archive/`、qpa)留在 `~/w7/logs/p7w7//`, +`cts/runs/*/{crashed,hung,unrun,skipped,complete,caselist}.txt` 与 `caselist.path`。归档图像与日志(`gate3/archive/`、`bsl/*/archive/`、qpa)留在 `~/w7/logs/p7w7//`, 在 README 里记绝对路径(>2 MiB 不入库)。主机制品 `/host/` 与 `host.sha256` 供之后的 TCP 对照(`run_tcp_matrix.py --library/--runner`,同 stamp)。 ## 5. Dry run(2026-09-23,已装 p7w6 APK `45759c30…`,只证管线,不是判据) diff --git a/docs/Disaggregated/notes/p7/device-window-2/tools/lib.sh b/docs/Disaggregated/notes/p7/device-window-2/tools/lib.sh index 309d6af1..9b0086f7 100755 --- a/docs/Disaggregated/notes/p7/device-window-2/tools/lib.sh +++ b/docs/Disaggregated/notes/p7/device-window-2/tools/lib.sh @@ -5,6 +5,7 @@ # device-window-1/00-session/README.md and HANDOFF-2026-09-22.md. W2_HERE=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +W2_ARGV=("$@") # the sourcing script's own arguments: w2_shared_lock re-runs it with them W2_SERIAL=${W2_SERIAL:-2f7cbe2e} W2_PKG=${W2_PKG:-top.mobilegl.plugin.p7w1.trace} W2_TOKEN=${W2_TOKEN:-p7w1b-lan-token-20260922} # >= 16 bytes (ServerMain token rule) @@ -15,6 +16,11 @@ W2_CTS_BUNDLE=${W2_CTS_BUNDLE:-$HOME/w7/logs/cts-a64/deploy} # glcts + mgprobe W2_CTS_DEV=${W2_CTS_DEV:-/data/local/tmp/mgcts} W2_IDLE_STATE=${W2_IDLE_STATE:-$HOME/.cache/mobilegl/tcp-server/${W2_SERIAL}-${W2_PKG}.json} W2_LOCK=${W2_LOCK:-$HOME/w7/logs/p7w7-device-${W2_SERIAL}.lock} +# The device lock every agent of the wave holds while it touches the phone +# ('flock -w 3600 /home/swung/w7/locks/2f7cbe2e.lock '); empty = do not take it (host tests). +W2_SHARED_LOCK=${W2_SHARED_LOCK-$HOME/w7/locks/${W2_SERIAL}.lock} +W2_SHARED_LOCK_WAIT=${W2_SHARED_LOCK_WAIT:-3600} +W2_SHARED_LOCK_RC=75 # a step's exit status when the shared lock never came free export MOBILEGL_FLATC_EXECUTABLE=${MOBILEGL_FLATC_EXECUTABLE:-/home/swung/w7/flatc-build/flatc} # The three gate-3 exclusions (CONTRACT-P7 7.1, E0-attribution/exclusions.md). They are red on @@ -50,12 +56,32 @@ w2_load_build() { } w2_require_device() { + w2_shared_lock # the first adb call of every device step already runs under the shared lock local state; state=$(timeout 20 adb -s "$W2_SERIAL" get-state 2>/dev/null | tr -d '\r') [ "$state" = device ] || die "adb does not see $W2_SERIAL (state '$state'). WSL's adb is a client of the WINDOWS adb server (mirrored networking); if it is gone, run 'adb start-server' on Windows - never start one from WSL" } +# The wave's shared device lock ($W2_SHARED_LOCK): other agents run their device work under it, so +# the window must hold it too or it does not exclude them. window.sh takes it once, after the build, +# for every device step to the end (no other agent gets the phone between two steps); a step started +# on its own takes it for itself. Taken as `flock -o` does: this script is RE-RUN (same arguments) as +# the child of a flock process that keeps the lock, and the child does not carry the lock's fd - so +# nothing a step leaves running (an adb server, a supervisor launcher) keeps the phone locked after +# it. The re-run sees W2_SHARED_LOCK_HELD and goes on; everything before this call runs twice and +# must stay idempotent. Waits up to $W2_SHARED_LOCK_WAIT s, else exits $W2_SHARED_LOCK_RC. +w2_shared_lock() { + [ -n "$W2_SHARED_LOCK" ] || return 0 + [ "${W2_SHARED_LOCK_HELD:-}" = "$W2_SHARED_LOCK" ] && return 0 + mkdir -p "$(dirname "$W2_SHARED_LOCK")" + log "taking the shared device lock $W2_SHARED_LOCK (waits up to ${W2_SHARED_LOCK_WAIT}s while another agent holds $W2_SERIAL; exit $W2_SHARED_LOCK_RC if it never frees)" + export W2_SHARED_LOCK_HELD=$W2_SHARED_LOCK + exec flock -o -w "$W2_SHARED_LOCK_WAIT" -E "$W2_SHARED_LOCK_RC" "$W2_SHARED_LOCK" bash "$0" "${W2_ARGV[@]}" +} + # One device user at a time: the runner's result root and the device are both single-tenant. +# The shared lock first (it re-runs the step), then this window's own step lock. w2_lock() { + w2_shared_lock mkdir -p "$(dirname "$W2_LOCK")" exec 9>"$W2_LOCK" flock -n 9 || die "another window-2 step holds $W2_LOCK" diff --git a/docs/Disaggregated/notes/p7/device-window-2/tools/window.sh b/docs/Disaggregated/notes/p7/device-window-2/tools/window.sh index 39b99b52..a7bf6e83 100755 --- a/docs/Disaggregated/notes/p7/device-window-2/tools/window.sh +++ b/docs/Disaggregated/notes/p7/device-window-2/tools/window.sh @@ -10,6 +10,8 @@ # 50-cts-after.sh five KHR-GL46 blocks, inproc x DirectVulkan, AFTER lib deployed to mgcts # 60-reduce.py gate-3 verdict per case, bsl peaks, CTS delta table + new-crash list # 90-restore.sh Home, supervisor as found, unpin, stay-on as found, $BASE lib back in mgcts +# From preflight to restore the window holds the wave's shared device lock ($W2_SHARED_LOCK, the +# one other agents take with 'flock -w 3600 ... '; it waits up to W2_SHARED_LOCK_WAIT s for it). # Each finished step leaves /steps/.done; a re-run skips it (gate3 / cts also resume # inside a step). A step that exits 3 (gate3 / cts: loop finished, some pairs / blocks INCOMPLETE) # is NOT marked done and the sequence carries on; a re-run of the same command retries only what @@ -51,12 +53,17 @@ step() { # log "--- $name: INCOMPLETE after $(( $(date +%s) - t0 ))s - not marked done; the next run of this command retries what is missing" return 0 fi + [ $rc -eq "$W2_SHARED_LOCK_RC" ] && die "--- $name: the shared device lock $W2_SHARED_LOCK was not free within ${W2_SHARED_LOCK_WAIT}s (another agent holds $W2_SERIAL); run the same command later" [ $rc -eq 0 ] || die "--- $name failed (rc=$rc); fix the cause and run the same command" date -Is > "$OUT/steps/$name.done" log "--- $name: OK in $(( $(date +%s) - t0 ))s" } step build bash "$HERE/10-build.sh" "$SHA" "$STAMP" +# From here to the end the window holds the wave's shared device lock (lib.sh w2_shared_lock: this +# script is re-run under `flock -o`; the build above is done by then and skipped by the re-run), so no +# other agent gets the phone between two steps; the steps see W2_SHARED_LOCK_HELD and do not re-take it. +w2_shared_lock step preflight bash "$HERE/21-preflight.sh" "$STAMP" $REBOOT step install bash "$HERE/20-install.sh" "$STAMP" # A previous pass restored the phone (unpinned, stay-on as found) with device steps still open: