[docs] (P7): magma-b3 - fix round 2: the audit's anchored log, char prefix and walk stops, the server-log fatal after the ARMED check on every transport, the pull-library control, and the round's gate

- §0: the previous round's note commit gets its own row (20), and rows 21-27 for this round
- §1.5: the three audit holes the second review found and the fixtures that pin them, with
  each new fixture's reading on the previous audit and on this one (three false greens, two
  false reds, one that stays red on purpose); 14/14, tree 53 / 53 / 0
- §4.1: the server-log fatal now sits after the marker and ARMED checks and covers inproc;
  the four fail-closed demonstrations with the fatal at its new position, and the case where
  the old order reported the server-log message for a launch-failure shape
- §4.5 (new): the pull-library control had refused its own red since P6's per-role log rename
  - read from Log.h and the runner, then measured with the real ctest - and the one-place fix
- §7.2: the landed-tree line of the stale WaitForFrameSerial comment corrected; two debts
  recorded: the census sits after the replay's exit-code fatal, and B4's restart must not
  read `record` after OnSubmitsCompletedUpTo erases it
- §5: the round's gate readings on the code head
This commit is contained in:
2026-09-22 21:28:02 -04:00
parent d80dc21299
commit f4b0662d7e
+112 -3
View File
@@ -39,6 +39,12 @@ spawn)每跑一次是金图 `ace2af04` 与错图 `fb75d412`(14658 px)之
| 14 | 修正轮 | `run_trace_case.cmake`:spawn 无服务端日志即 FATAL(§4.1) |
| 15–16 | 修正轮 | 注释/日志措辞:image-flags 出口的注释;`Magma wire decline [Site]`(§1.5) |
| 17–19 | 本文(修正轮) | §5 标明包树/落地树;§7.2 行号;§7.2 `WaitForFrameSerial` 答 true 的债 |
| 20 | 本文(修正轮) | 修正轮的描述:审计的 strip / 块走查 / 自测、spawn 服务端日志 FATAL、修正轮的门 |
| 21–23 | 修正轮 2(fable 对修正轮的复审,land with fixes) | 审计:站点的日志必须是**以** `MGLOG_W/E` 开头的语句(`if (g) MGLOG_W(...)` 不算);带前缀的字符字面量 `L'"'` / `u8'"'`;块走查在同缩进 `return`/`break;`/`case …:` 处停、跨过第 0 列 `#if`/`#endif`(§1.5) |
| 24 | 修正轮 2 | `run_trace_case.cmake`:服务端日志缺失即 FATAL 移到 marker / ARMED 检查**之后**,inproc 也要(§4.1) |
| 25 | 修正轮 2 | `retrace_pull_library_control.sh`:接受 P6 日志改名后 pull 库实际得到的那句 FATAL(§4.5) |
| 26 | 修正轮 2 | 注释:`WireDeclineTally.h` 引用了不存在的 `MGL_WIRE_DECLINE_FALSE`;`.def` 横幅说 wire 臂而非 draw |
| 27 | 本文(修正轮 2) | — |
---
@@ -114,6 +120,26 @@ OpenRA / DirectVulkan / `MOBILEGL_TRANSPORT=inproc` / lavapipe:retrace `PASS s
8 行之内(向上遇到缩进更浅的行即停);`.def` 同样去注释后再认行。`--self-test` 的 8 个内联夹具
(// 注释站点、/* */ 注释站点、字符串站点、别的分支的日志、嵌套分支的日志、注释里的日志、`MGLOG_D` → rc 1;
好站点 → rc 0,且都要打出对应的那句话)在 CI 同一步里先跑。树上仍是 53 / 53 / 0。
**修正轮 2(fable 对修正轮的复审)**:又三个洞。`LOGGED` 没锚定行首,`if (g) MGLOG_W("x");` /
`g ? MGLOG_W("x") : (void)0;` 这种只在某些时候才打的单行守卫日志,站在 `Count` 的缩进上就算成了站点自己的日志
——计数每次动、日志只偶尔打,正是审计要拒的那种站点;带前缀的字符字面量 `L'"'` / `u8'"'` 里的 `'` 被当成数字分隔符,
里面的 `"` 打开一个幻影字符串把本行剩下的部分(连同站点)抹掉(fail-closed 的误红);块走查遇到第 0 列的
`#if`/`#endif` 就停(误红),而同缩进的 `return`/`break;`/`case …:` **之上**的日志(控制流到不了 `Count`)却算数。
现在:`LOGGED` 锚定 `^\s*MGLOG_[WE]`,走查用 `match` 不用 `search`;`CHAR_PREFIX` 与 `RAW_PREFIX` 同形(前缀之前
须是非标识符字符或行首,所以 `0xAB'CD` 不受影响);走查跨过 `#if`/`#ifdef`/`#ifndef`/`#endif`(`#else`/`#elif`
仍是停点——另一侧的日志属于另一个构建),在同缩进的 `case`/`default:`/`break;`/`return`/`continue;`/`goto`/`throw`
处停。夹具 8 → **14**,新夹具在旧审计(`53471e7b`)与新审计上的读数:
| 夹具 | 期望 | 旧审计 | 新审计 |
|---|---|---|---|
| guarded one-liner log at the Count's indentation | rc 1 | rc 0 **误绿** | rc 1 |
| log on the far side of a same-indent return | rc 1 | rc 0 **误绿** | rc 1 |
| log in the previous case of a same-indent switch | rc 1 | rc 0 **误绿** | rc 1 |
| log only on the other side of a #else | rc 1 | rc 1 | rc 1(钉住 `#else` 仍是停点) |
| prefixed char literals (L'"', u8'"') before a site on the same line | rc 0 | rc 1 **误红** | rc 0 |
| count under its log across a column-0 #if/#endif | rc 0 | rc 1 **误红** | rc 0 |
14/14,树上仍是 **53 / 53 / 0**(树上今天没有带前缀的字符字面量,也没有跨 `#if` 的裸 `Count`,所以两处误红没有真的红过)。
| 审计 | 行 | 有站点 | 未记日志 | rc |
|---|---|---|---|---|
@@ -350,7 +376,42 @@ CMake Error at run_trace_case.cmake:321 (message):
counted as a clean one.
```
inproc 也写 `mobilegl.server.log`(apply 线程),但本轮按复审只收紧了 spawn;inproc 仍是 `if(EXISTS …)`。
**修正轮 2:这条 FATAL 移到 marker 与 ARMED 检查之后,并且 inproc 也要服务端日志。** 修正轮把它放在了
marker 检查与 `spawn ARMED … pid` 检查**之前**,于是一次启动/握手失败(没有服务端进程、也没有 ARMED 行)报的是
「服务端没留下文件」,而不是更具体的「resolved, but carries no spawn ARMED」,把读者指向错误的进程。现在它紧挨在
census 之前(`:384`),tcp 臂原来同样位置的「no forwarded server log」检查随之并入;也不再按 transport 分支:
runner 承认的每一行 transport 都有服务端角色的文件——spawn 是服务端进程写的(tcp 是夹具转发的行落在同一文件),
inproc 是 `mgl-srv-apply` 线程的行按线程角色路由到同一个 `.server.log` sink(首条服务端角色行时惰性打开),
而本车道要求的 INFO 档下 `ServerLoop.cpp:365` 在 apply 线程启动时就写一行,所以干净的 inproc 回放**一定**有这个文件
(修正轮那句「inproc 仍是 `if(EXISTS …)`」到此为止);两条 census 的读取也不再 `if(EXISTS …)`。
演示(OpenRA DirectVulkan,包装真回放、回放之后毁掉一份日志):
```
1. inproc,删服务端日志(536 行)
修正轮 runner(53471e7b) rc 0 Fatal{ lines ...: 0 / MGWIRE-FLOOR unsound-serial-complete lines: 0 ← 误绿
修正轮 2 runner rc 1
CMake Error at run_trace_case.cmake:384 (message):
OpenRA DirectVulkan: MOBILEGL_TRANSPORT=inproc but the run wrote no
.../output/mobilegl.server.log, so the server role's Fatal{ and MGWIRE-FLOOR
lines cannot be counted. A split retrace with no server log cannot be
counted as a clean one.
2. spawn,删服务端日志(541 行)
修正轮 2 runner rc 1 同上,`MOBILEGL_TRANSPORT=spawn but the run wrote no …`,同在 `:384`
3. spawn,删服务端日志**并**删掉 client 日志里的 `spawn ARMED` 行(启动/握手失败的形状)
修正轮 runner(53471e7b) rc 1 run_trace_case.cmake:321 … but the run wrote no …server.log ← 指向服务端
修正轮 2 runner rc 1
CMake Error at run_trace_case.cmake:356 (message):
OpenRA DirectVulkan: .../output/mobilegl.client.log reports
MOBILEGL_TRANSPORT=spawn resolved, but carries no "spawn ARMED - the server
role runs in pid ". That sentence is written by
ClientSession::StartSpawned AFTER the launch, the connect and the handshake
all succeeded, ...
4. spawn,只删 `spawn ARMED` 行(服务端日志保留)
修正轮 2 runner rc 1 同 3,`:356` 的 ARMED 消息
```
不毁日志时 OpenRA DirectVulkan **与 DirectGLES** 的 SPLIT / SPAWN 四条全绿(ssim 1.000000 / 0 px / `MGWIRE-FLOOR` 0 行;
inproc 的服务端日志 DirectVulkan 536 行、DirectGLES 987 行)。
**图是金的、用例是红的**——这正是这条门要的:它红在「地板断言了一次没等过的完成」,
不管这台驱动的时序有没有把它变成像素。
@@ -425,6 +486,38 @@ inproc 与 spawn **sha 逐字相同**,证明机制在**服务端**。
未决。本包已有的 red-once 是 §4.1 的确定性计数器(B §5.4 的形状)加 §4.2 的两进程像素用例,
两者都不靠时序。**如实记录**:主机上因此**没有**一张自然发生的错图,只有计数器与强制图。
### 4.5 范围外:pull 库对照自 P6 日志改名起就到不了绿(复审点名,修正轮 2 修)
`scripts/ci/retrace_pull_library_control.sh`(`test.yml` retrace-split 矩阵的两行 transport 都跑它)要求 red 里带
`run_trace_case.cmake` 的 `never reported resolving it`——那是 marker 搜索那一句。P6 一角色一日志的改名(`b3155525`)
之后 pull 库到不了那一步:pull 构建只有一个角色,`RoleLogPath` 原样返回 `MOBILEGL_LOG_FILE_PATH`(`MG_Util/Debug/Log.h`
的 pull 臂),写的是 `output/mobilegl.log`;runner 读的是 `output/mobilegl.client.log`(`run_trace_case.cmake:115`),
于是在 marker 搜索**之前**一步就 FATAL(`:269-273`:「the run wrote no …client.log, so there is no evidence the transport
ever resolved」)。这句里没有对照要的话,对照的第二道门(「red 必须是本对照自己的 red」)把自己的 red 拒了,步骤 rc 1。
**本树实测**(真 ctest;`build-linux` 的 pull 库盖到 `build-split` 的冻结路径上,`SPLIT_TRANSPORT=inproc`,
未加后缀的 `MobileGLTraceReplay.OpenRA.DirectVulkan` 条目,与 CI 同形):
```
pull control library: .../build-split/libMobileGL.so: MG_Remote=0
CMake Error at run_trace_case.cmake:270 (message):
... .../OpenRA/DirectVulkan/output/mobilegl.client.log,
so there is no evidence the transport ever resolved. A split retrace with ...
::error::the split retrace went red (ctest exit 8) with the pull library in place, but the failure
never says the library did not resolve the transport - run_trace_case.cmake's "never reported
resolving it" is absent from the output. ...
控制脚本 rc=1
```
修法一处:`EVIDENCE` 变成 `grep -F` 的两行模式(`never reported resolving it` 与 `no evidence the transport ever resolved`),
红检扰动的那一行(`grep -qF "${EVIDENCE}"`)原样不动;两句都是同一条 transport 身份断言以同一理由拒绝同一个库
(pull 构建没有 split 日志可供 marker 落脚),都不是 loader 失败 / 缺夹具 / 超时 / SSIM 掉——那才是第二道门要拒的。
`stub_ctest.sh` 加 `retrace-evidence-nolog`(按 CMake 折行的样子复现这句),`control_smoke_test.sh` 期望它 PASS
(22 → **23**/23),`redcheck_control_smoke_test.sh` 仍把「red without the transport-resolution message」变红。
修后本树实测 inproc 与 spawn 两臂的对照都 rc 0:「the pull library turned the split retrace red for its own reason
(ctest exit 8): 1 selected case(s) named the transport, not the picture」。CI 上这一步自改名以来的读数本包看不到,
只能说:按同一份代码它每次都会 rc 1。
---
## 5. 门
@@ -479,6 +572,14 @@ census rc 0 **79**;audit `--self-test` **8/8** rc 0、树上 **53 / 53 / 0** r
`unit` **2420/2420**、`integration-magma-split` **90/90**、`integration-magma-spawn` **69/69**;
OpenRA DirectVulkan SPLIT / SPAWN PASS、ssim 1.000000、0 px、`MGWIRE-FLOOR` 0 行。
**修正轮 2 的门——包树 `3977901f`(修正轮 2 最后一个代码提交),合并前**(`~/w7/b3bin/r3/gate3.log`):
split 构建 rc 0;G1 rc 0、`.text` **`0xa52203`**、nm **0 / 0**(产品文件改动只有两处 disagg-only 的注释);
census rc 0 **79**;audit `--self-test` **14/14** rc 0、树上 **53 / 53 / 0** rc 0;ratchet **unchanged at 186**;parity rc 0;
`control_smoke_test.sh` **23/23** rc 0、`redcheck_control_smoke_test.sh` rc 0;
`unit` **2420/2420**、`integration-magma-split` **90/90**、`integration-magma-spawn` **69/69**;
OpenRA DirectVulkan SPLIT / SPAWN PASS、ssim 1.000000、0 px、`MGWIRE-FLOOR` 0 行(DirectGLES 两臂同,§4.1);
§4.1 的四个 fail-closed 演示与 §4.5 的 pull 库对照(inproc 与 spawn 各一次,rc 0)。
包树上外加 `MobileGLTraceReplay.OpenRA.DirectVulkan.SPLIT` / `.SPAWN`(带 §4.1 的新红条件):
PASS、ssim 1.000000、0 px、`MGWIRE-FLOOR` 0 行。为跑这两条,本树 `build-split` 以
`-DMOBILEGL_BUILD_TRACE_REPLAY=ON` 重配,并初始化了 `3rdparty/apitrace` 的嵌套子模块(只动子模块工作树)。
@@ -541,7 +642,8 @@ Present(`:14094`)三处「等一条 fence、把它之前的都记成完成
每第 8 次 drain 会调 `BeginFrame`)。结论(不在该处回收 arena 存储)仍然正确,只是理由写错了;
改注释会和 B2 在同文件区域的改动打架。
- `VulkanRenderer::WaitForFrameSerial` 里 fence 等待循环上方的注释(包树 `774bc17f` 为
`VulkanRenderer.cpp:13400-13403`,落地树 `pipe@6207c9c9` 为 `:13530-13533`;以函数名为准)的
`VulkanRenderer.cpp:13400-13403`,落地树 `pipe@6207c9c9` 为 `:13529-13532`、引用的那句在 `:13531-13532`;
以函数名为准)的
「OnSubmitsCompletedUpTo calls NotifyFrameSerialComplete for every record it retires, so the
completed-serial floor still advances correctly after one fence wait」——disagg 构建上 §3.1 之后
**不再逐记录通知**,地板只推到无在飞持有的序号。该函数是 pull 与 disagg 共用代码,注释归 B4 一起改。
@@ -553,7 +655,14 @@ Present(`:14094`)三处「等一条 fence、把它之前的都记成完成
`IsTimerQueryResultReady(record)`,所以今天它退化为一次**假超时**(结果这次拿不到、下次再收),不是错读。
**修法归 B4**:`OnSubmitsCompletedUpTo` 之后只在 `IsFrameSerialComplete(serial)` 为真时返回,否则**从头重扫**
——`OnSubmitsCompletedUpTo` 会从正在迭代的 vector 里 erase,所以是 restart,不能 continue;
与 §7.1 的聚合等待与上面的注释一起做。
与 §7.1 的聚合等待与上面的注释一起做。**修正轮 2 补给 B4 的一句**:`OnSubmitsCompletedUpTo(record.submitIndex)`
之后**不得再读 `record`**——`:13404` 的 range-for 拿的是 `m_inFlightSubmits` 元素的引用,而 `OnSubmitsCompletedUpTo`
正是从这个 vector 里 erase;要用的字段先拷出来(`RefreshCompletedSubmits` `:13471` 的
「Copy before OnSubmitsCompletedUpTo erases the front record」就是这条规则)。
- **`run_trace_case.cmake` 的 census 排在 replay 退出码之后(修正轮 2 记债,本轮不动)**:`:172-174` 对非零
`replay_result` 直接 `FATAL_ERROR`,两条 census(`Fatal{` 与 `MGWIRE-FLOOR`,修正轮 2 之后在 `:390` 与 `:416` 起)都在它之后——一次带 abort 的
回放只留下退出码,日志里的 `Fatal{` 名字与地板计数不会被打印出来,读者得自己去翻两份日志。census 应当先于退出码判定
读两份日志(存在则读);与 §4.1 的 fail-closed 顺序一起改时要分清:「无服务端日志即 FATAL」是对**退出码为 0** 的回放说的。
- **disaggregated 构建的 monolith 臂现在比 pull 构建更可靠(复审 (a))**:monolith 的
`VkBufferResource` 路径 `OnSubData` 经 `IsResourceBusy`(`VkBufferManager.cpp:709`)读的是**同一个**
`GetCompletedSerial()` 地板,所以 §3.1 的钳制在 disagg 构建里也保护了 monolith 臂的 mid-frame flush;