From 5b861f82ab956f1cbf2f26cf2d641be7a494b119 Mon Sep 17 00:00:00 2001 From: QIU SHENGMING Date: Fri, 2 Oct 2026 10:44:18 +0800 Subject: [PATCH] Validated metric: every individual call survives repetition, and the killer is inside draw() Each variant is for(;;) { long spin; one call; } so a live app is in the overlay most of the time: control 27.2%, display_clear 26.9%, get_key 27.1%, delay_ms(40) 26.8%, print_tiny 27.0% -- all four calls survive repetition, and the control shows the metric separates the two cases. That also means the previous round's variant B (four calls in a loop, no spin) was most likely not dead at all: it spends nearly all its time inside firmware functions, which look exactly like a dead app from the trace. The bisect has since closed on one function: Minesweeper with draw() gutted to display_clear() + blit_full() runs (97 samples inside the app, alternating with firmware PCs), while the full draw() draws nothing in twenty seconds and ink never leaves 210, so display_clear() never ran. The end of the app is inside draw()'s body. --- AGENTS.md | 22 ++++++++++++++++++++++ AGENTS.zh-CN.md | 17 +++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 5bce3fc..0f96d78 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -864,6 +864,28 @@ comparable results, they are one good measurement and two unreadable ones. The f self-contained spin with each call, so a live app is provably in the overlay most of the time and the trace separates the two cases. +**Round 15: with a validated metric, every individual call survives repetition -- and the killer is inside draw().** + +The metric first, because the previous round's was not trustworthy: each variant is for(;;) { long spin; one call; }, +so a live app is in the overlay most of the time and a dead one is never there. The control (spin only) scores +27.2%; display_clear 26.9%, get_key 27.1%, delay_ms(40) 26.8%, print_tiny 27.0% -- **so all four calls survive +being repeated**, and the control proves the metric can tell the two cases apart. + +That also means variant B of the previous round (four calls in a loop, no spin) was almost certainly not dead: it +spends nearly all its time inside firmware functions, which look exactly like a dead app from the trace. The +lesson is the same one this file keeps repeating -- a measurement that cannot show the thing you are looking for +will happily return zero. + +With that out of the way, the bisect has closed on one function. Minesweeper with its whole draw() gutted to +display_clear() + blit_full() **runs** -- 97 samples inside the app, alternating with firmware PCs, and the +firmware still serving it. The full draw() draws nothing at all: over twenty seconds the panel never leaves the +launcher's title box, and ink stays at 210, meaning display_clear() never even ran. So whatever ends the app is +inside draw()'s body -- the eleven print_tiny calls, the framebuffer writes and the cursor invert -- and that is +the last mile. + +Next: the same variant harness on the three pieces of draw() separately (the header text, the 81-cell loop, the +cursor), each with the loop spin that the surviving apps have. + That is where the next round starts: the same alternating-spin shape, one call at a time. trace. That is a much better place to be than the emulator mystery this started as. diff --git a/AGENTS.zh-CN.md b/AGENTS.zh-CN.md index 7576c9f..7139702 100644 --- a/AGENTS.zh-CN.md +++ b/AGENTS.zh-CN.md @@ -707,6 +707,23 @@ bug 是同一族:**传输还在进行时就告诉加载器「已完成」**, get_key() 和只含 delay_ms() 的循环得了 0 分 —— 这三个结果**不可比**:一个是有效测量,另两个读不出结论。修法是让 每个调用之间夹一段**自包含的长自旋**,这样活着的应用在大多数时间**确定**在叠加区里,trace 就能把两种情况分开。 +**第 15 轮:用经过验证的判据,四个调用各自被重复都没问题 —— 而凶手就在 draw() 里面。** + +先说判据,因为上一轮那个不可信:每个变体都是 for(;;) { 长自旋 ; 一个调用 },这样活着的应用大部分时间在叠加区里, +而死掉的从不出现。对照组(只有自旋)27.2%;display_clear 26.9%、get_key 27.1%、delay_ms(40) 26.8%、 +print_tiny 27.0% —— **所以这四个调用被重复都没有问题**,而对照组证明这个判据能区分两种情况。 + +这也意味着上一轮的变体 B(四个调用放进循环、中间没有自旋)**几乎肯定没死**:它把时间几乎全花在固件函数里, +而那从 trace 上看与死掉的应用一模一样。教训与本文件反复讲的那条相同 —— **一个看不见你所找之物的测量,会心安理得 +地返回零。** + +把这些排除之后,二分已经收拢到一个函数上。把扫雷整个 draw() 掏空成 display_clear() + blit_full() 之后它**能跑** —— +应用内 97 个样本,与固件 PC 交替出现,固件仍在为它服务。而完整的 draw() 什么都不画:二十秒里面板始终停在启动器 +的标题框,ink 一直是 210,说明**连 display_clear() 都没执行过**。所以结束应用的东西在 draw() 的躯干里 —— 那十一次 +print_tiny、帧缓冲写入与光标反相 —— 这就是最后一公里。 + +下一步:用同一套变体装置分别测 draw() 的三块(标题文字、81 格循环、光标),每块都带上那些能活的应用所拥有的循环自旋。 + 下一轮就从这里开始:同样的「自旋 + 单调用」形状,一次只放一个调用。 进去、读同一份 trace。比起一开始那个模拟器悬案,这是好得多的处境。