merge: task/n2-logpipe 合入 v2-on-ui

任务 n2(1 个提交)经 task-worktree 回收:
- docs(日志): N-2 × S8 合并评估收口——S8 判已达成、IT/DMA 维持否决、N-2 收敛 V5 复跑
This commit is contained in:
2026-10-25 11:02:25 +08:00
3 changed files with 92 additions and 1 deletions
@@ -307,6 +307,7 @@ sequenceDiagram
| 2026-10-02 | **ORE 僵尸通道喂狗节奏保护**(台架 gen-288):round-3 的"单次 drain 预算 250ms"只限单次调用——ORE 锁存在外设软复位后**毫秒级重锁**(PA10 悬空 + RX 使能),背靠背 drain 每轮再耗 100ms 块超时 → 主循环周期拉到 450~500ms → 100ms 节流的喂狗抽样间隙超过 IWDG 最坏容限 1.7s → 停喂 → 误复位(且心跳位全员健康、无 stall 日志,纯**节奏性饥饿**,非死等)。修复:发送故障后立即结束本轮 drain 并进入 300ms 发送冷却(冷却期内 drain 直接让位主循环),保证喂狗节奏 ≤ ~300ms |
| 2026-10-02 | **round 8b 加固**:发送超时判据改用 DWT `CYCCNT`(时间源无关)。原 `HAL_UART_Transmit` 的 `HAL_GetTick() - Tickstart` 依赖 TIM7 的 `uwTick`,TIM7 一旦停摆超时永不成立 → 写者死等 TXE → 唯一喂狗者停摆 → IWDG 复位。裸发送与缓冲满等待两处均换成 DWT 周期计数硬上界(DWT 使能失败时退回旧 HAL 行为);另加 `huart1.Instance == NULL` 短路,防早期/断言路径解引用 NULL |
| 2026-10-10 | **新增 `dbg_console_writer_ready()` + 未注册期纪律固化**:裸发为寄存器级轮询(无 `gState`)→ 未注册期多任务并发**逐字节交错**(台架 `adcTask` 首帧日志与 `[NAND_FTL]` 行交错实证)。新增 `dbg_console_writer_ready()` 供周期任务门控;`adcTask` 首帧/心跳日志据此门控(§3.4 / §4.4) |
| 2026-10-25 | **N-2 × S8 合并评估收口(评估任务,零代码)**:S8 生产者侧判定已达成、TX IT/DMA 维持否决并绑定 S-3 复议条件、N-2 收敛到 V5 复跑双零判据——全文见文末附录 |
| 2026-10-12 | **纪律盲区台架实证(B6)**:`protectTask`(B5 接线,台架腿当时未跑)启动锚点行在写者注册前裸发 → 与 defaultTask 启动日志**逐字节交错**(`st[art: cycle=220ms elm=70`,两次开机同形复现 = 启动调度确定性的竞态,非 USB 噪声)→ `@test` 锚点 MISS。修复 = 启动行改 `dbg_console_writer_ready()` 首拍补打(adcTask 首帧同款),`@build` 0/0 + `@test` PASS 复验(锚点完好 +577 ms)。§3.4 纪律示例更新 |
## 12. ORE 僵尸通道(2026-10-02 台架实证)
@@ -343,6 +344,80 @@ sequenceDiagram
- `测试说明/自动化测试流程.md` —— `@test` 标记体系(Required / Forbidden 模式,`[ERR]` 语义)
- AGENTS.md —— 平台约束(内核前禁内核对象 / C 堆禁令 / 事件位语义 / 内存预算)
## 附录:N-2 × S8 合并评估(2026-10-25,道F 批 2 / task-18,零代码改动)
> 评估任务(`docs/任务规划/登记簿.md` N-2 行「残余动作:与 S8 合并评估」):N-2
> (49s 全系统日志静默,2026-09-22 L3 台架世代号 181)与 S8(日志非阻塞化,需求
> 出处 = `docs/测试说明/链路延迟测试说明.md` §9.2/§10.2)的合并性价比判定。
> 评估纪律 = 结论即交付,不顺手重构;本节结论:**无需最小实现**(不新增代码)。
### A.1 S8:目标已由「单写者形态」在**生产者侧**达成
S8 要治的是阻塞式日志税(L10 实测 `logtax_line` **4402 µs/行**,`fputc` 逐字节
@115200 阻塞调用方整行)与并发丢行(实测 `docs/测试说明/链路延迟测试说明.md` §9.2:
必检标记被竞态吞)。2026-09-24 单写者
改造正是同一需求落的方案(§1/§2 G1-G5):
| S8 子项(需求出处 `docs/测试说明/链路延迟测试说明.md` §10.2 第 9 条) | 原案 | 现状(代码实证) |
|---|---|---|
| `fputc` → 环形缓冲 | 串口 TX 环形 | ✅ 4KB BSS 环 + 事件位唤醒(§4.1/§4.2);生产者 = 亚微秒~µs 级临界区整行入环(≤192B 拷贝),日志税较 4402µs/行**降 3 个量级** |
| TX 中断/DMA | 非阻塞发送 | ❌ 有意不做(轮询单写者,D4);复议时机见 A.2 |
| 溢出丢弃策略 | 待定义 | ✅ 有界等待 200ms(K4)→ 丢弃 + `dropped` 账 → `[ERR] CONSOLE dropped`(永不静默,§4.2/§7) |
| HardFault 冲刷 | 待处理 | ✅ 有界裸发 ≤256B(G4/§4.4,DWT/计数硬上界,不依赖 tick、零内核对象) |
热路径日志税另已由 D6 单独削掉(删 per-recv 日志,`queue_rt_recv` 5997→1223µs)。
**结论:S8 按“生产者不阻塞业务”的意图已闭环**;剩余的“阻塞”只在写者任务
(defaultTask)自身——drain 预算 250ms + 故障冷却 300ms + DWT 硬上界(§11/§12),
**不在业务任务关键链上**,且预算值按“让位 800ms 停喂容忍”定档,属设计内行为。
### A.2 TX 中断/DMA(原案后半):维持否决,排序绑定 S-3
D4 否决理由对现状仍逐条成立(115200 发送率低、日志行稀疏;IT/DMA 引入新内核
对象、缓冲所有权/双缓冲复杂度、DMA 可达性清单)。三点补充判断:
1. 把 drain 拖长的真凶是 **ORE 僵尸通道**(登记簿 S-3:PA10 悬空 + RX 使能,
软复位后毫秒级重锁 → 每轮块超时 100ms 级)——`.ioc` 改 TX-only(批 2.5
独占轮,CubeMX 重生成约束见 AGENTS)落地后该形态整体消失;
2. 根治前后数据已可机读:`dbg_console_tx_timeout()`(§13-O2 三本账,v1.5 起)
就是为“IT/DMA 还剩多少收益”留的量尺;
3. S-3 之前动 TX 路径 = 给即将被移除的场景写两遍代码。
**结论:不采。复议触发条件 = S-3 落地后实测 `tx_timeout` 增量仍 > 0。**
### A.3 N-2:观察窗收敛到 V5 复跑;“真停滞 vs 无源”判别成立
- 判据②(静默 → 可观测)= **已具备**:控制台三本账 `dbg_console_dropped /
dbg_console_tx_fault / dbg_console_tx_timeout` + syslog 的
`queue full, drop` / `preboot full, drop` / `hold-over drop`
(`App/log/sys_log_mcu.c`);任何“静默丢行”现形为 `[ERR]` → `@test` FAIL。
- 判据①(复现 49s 并定位):V5 30min 未复现;实测(`docs/测试说明/实机测试实施指南.md` §7.2)
运行期 >10s 静默 24 处**全部落在 e02_probe 窗口、长度 ≈28s**——其
同源阻塞机制(`drop_conn` 同步占用 netTask 400-600ms)已随 N-17 修复
(登记式拆链,`docs/网络/网络自愈架构设计.md` §4.6)。本评估补充一个此前
缺位的替代解释:**探针窗内的静默很可能本来就是“无日志源”的正常形态**——
探针一轮(挂起 30s + 短暂复连)业务日志天然稀疏,defaultTask 无周期串口输出
(LED 静默;`[STACK]/[PROF]` 仅新峰值/条件满足才打印),~28s ≈ time_sync
两条 INFO 之间的静默带,不是排空停滞。
- **若再现的判别口径**(已具备,无需新增):静默段前后对照三本账与 syslog
丢弃账——账目零增量且静默后无 `[ERR] CONSOLE dropped` = **无源**(正常,
不开卡);账目有增量或出现 dropped 行 = **真停滞**(开卡,按 §12 ORE 与
冷却路径核对)。
- **处置建议**:N-2 保留为低风险观察,至 N-17 修复版(merge 969fc5c)的 V5
复跑(判据 = 运行期 `[WD] task stall` 与非开机 `MISS start` 双零,台架由
lead 排窗)达成后销项;本附录即销项结论落点。登记簿 N-2/N-17 行与波次行
的清理归单写者(lead)。
### A.4 评估后的遗留面(全部既有,无新增)
| 项 | 归属 | 处置 |
|---|---|---|
| ORE 僵尸根治(TX-only) | 登记簿 S-3 / §12 / §13-O4 | 批 2.5 独占轮;落地后触发 A.2 复议 |
| `tx_timeout` 读口接线(STALL 证据行加字段) | §13-O2 | 涉 `App/util/task_watchdog.c` 与被解析证据行的台架判据,非本卡写域;维持 O2“后续项” |
| 未注册期裸发互斥 / `printf` 遗留开销 | §13-O1/O3 | 现状 = 纪律已落地,开机日志密度上升再议 |
| N-2 观察项 | 登记簿 §1 | 见 A.3:V5 复跑双零后销项(归 lead) |
---
## 修订记录
| 版本 | 日期 | 说明 |
@@ -352,4 +427,5 @@ sequenceDiagram
| v1.2 | 2026-10-02 | 新增 §12 ORE 僵尸通道(台架 gen-288 实证:节奏性喂狗饥饿,非死等;缓解已做、TX-only 根治待办)+ §11 时间线两条 10-02 记录;§13 新增 O4;原 §12/§13 顺延为 §13/§14 |
| v1.3 | 2026-10-10 | 栈链整改(**未抬栈**)三处:① 裸发改为纯寄存器轮询——DWT 不可用时走 `con_tx_counted`(计数上界),**不再回退 `HAL_UART_Transmit`**(tick 依赖不可信 + 其调用链污染所有任务最坏栈链);② 切断 `drain → syslog_post → write_line` **调用图成环**,改为置标志 + 写者任务在 drain 之外补记(新 API `dbg_console_tx_fault_log_take`);③ 行缓冲按模块可收紧(`dbg_log.h` 新增 `DBG_LINE_BUF_MAX`,深链模块 128)+ 时间戳缓冲并入行缓冲(32 → 24)。§4.4 / §5 / §6 / §7 / §8 同步;实测 defaultTask 静态最坏链 5312 → 2776 B |
| v1.4 | 2026-10-10 | 新增 `dbg_console_writer_ready()`(写者就绪查询)并固化"未注册期只有 defaultTask 记日志、周期任务用就绪门控"纪律;更正 §3.4 / §7 / §9-D5 / §13-O1 的"只丢不交错"旧结论(裸发已改寄存器级轮询、无 `gState` 互斥) |
| v1.6 | 2026-10-25 | 新增文末**附录:N-2 × S8 合并评估**(道F 批 2 / task-18,零代码):A.1 S8 已由单写者形态在生产者侧达成(4402µs/行 → µs 级入环);A.2 TX 中断/DMA 维持 D4 否决、复议绑定 S-3 落地后 `tx_timeout` 实测;A.3 N-2 判据②已具备、①给出「无源 vs 真停滞」判别口径与 V5 复跑销项条件;A.4 遗留面全部既有、无新增。§11 时间线同步 |
| v1.5 | 2026-10-24 | **主机单测批次照出的四条现状全部处置**(生产码在 `App/log/console_log.c`,用例在 `test/host/console_log/`):① 冷却判据改用独立 `s_con_cooling` 标志,不再拿 `HAL_GetTick()` 的 0 值当哨兵——旧写法在 tick==0 那一拍(开机头 1 ms,或 49.7 天回绕到 0)冷却等于没记;② `con_append` 的空位判据 / 推进 head / 整行拷贝收进同一临界区——旧实现是临界区外 check-then-act,多生产者可在环 >95% 满时双双过判据把 head 推过容量 → 静默覆盖且**既不打 [ERR] 也不计 dropped**;③ `Instance == NULL`(未初始化)与僵尸通道**分诊**,未初始化绝不 reinit(旧路径会在 HAL 未就绪时经 MspInit 重配 RCC/GPIO);④ 三本账,见本文 §13-O2。**dropped 的单位口径变了**:一次 = 一行(缓冲满)或一个块(没能确认送达 / 通道不可用),故 `[ERR] CONSOLE dropped N lines` 的 N **不恒等于行数**(为兼容 `@test` 标记匹配,文案未改词)。本文 §3.1 与 §4.1 的「单生产者 vs 全上下文生产者」矛盾一并消除 |
@@ -271,6 +271,13 @@ $cli = if ($env:DTU_STLINK_CLI) { $env:DTU_STLINK_CLI } else { (Get-Command ST-L
挂起客户端窗口内**——与 **N-17** 同源(探针场景专属,非通用负载)。
- **结论**:49s 静默在当前固件 + V5 负载下**不复现**;可观测性缺口已由既有代码补齐。
N-2 降级为**低风险观察项**(保留登记,若再现则借 `[ERR] CONSOLE dropped` 计数定位)。
- **后续(2026-10-25,道F 批 2 合并评估)**:窗内静默的同源阻塞机制
(`drop_conn` 同步占用 netTask 400-600ms)已随「登记式拆链」修复
(`../网络/网络自愈架构设计.md` §4.6);N-2 × S8 合并评估已完成,结论与「无源 vs
真停滞」判别口径落 `../子系统设计/控制台单写者日志设计说明.md` 文末附录——
再现静默先按三本账(`dbg_console_dropped/_tx_fault/_tx_timeout`)+ syslog 丢弃
账对照定性,再决定是否开卡;销项条件 = N-17 修复版 V5 复跑(e02_probe + FTP
≥30 min)运行期 stall 与非开机 `MISS start` 双零。
**执行步骤**:长跑(§4.2 V5)期间周期性打点 + 对照 `CH395F` 无关的 SD2506 侧日志判别卡点。
@@ -706,7 +706,12 @@ sock4 auto-relisten ok / relisten retry ok ← 监听器随后自行恢复
**另**:MSS/块配额变更**必须**跑可信用例(`test/host/ch395f_socket_test.py tcp_client` 全序列 + 新连接独立 4096B 对照),判据 = **客户端侧内容完全匹配**(勿用带固件内读者的台架配方,其短收是读者吞数据不是故障);
7. **S5** 去掉一次 3200B 拷贝(`g_tx` 直接承接位图,省 ~3.2KB 常驻 RAM;改前跑 `scripts/ram_report.py`);
8. **S7** uiTask:`ui_net_sink_poll()` 前置到 `ui_port_step` 前 + while 排空(省按键一整拍);
9. **S8** 日志改非阻塞(`fputc` → 环形缓冲 + TX 中断/DMA;处理溢出丢弃与 HardFault 冲刷);
9. **~~S8~~ 已实施·形态变更(生产者侧 2026-09-24 单写者;2026-10-25 N-2 合并评估收口)**:
`fputc` → 环形缓冲**已落地**(4KB 环 + 单写者 + 事件位;4402µs/行 → µs 级入环,
溢出 = 有界 200ms + dropped 账 + `[ERR]` 留痕;冲刷 = 有界裸发,见
`../子系统设计/控制台单写者日志设计说明.md` §4);**TX 中断/DMA 维持否决**
(该文档 D4),复议绑定 S-3 TX-only 根治后的 `tx_timeout` 实测——N-2 × S8 全文
结论见同文档文末附录;
10. **S9** 客户端侧(DTU-RemtoeLCD):按键后立即取帧、不传 PNG(直接发 1bpp/差量 + canvas 绘制)、
自适应节拍(帧耗时补偿)、验证 PC 一律有线同交换机;
11. **M4**(可选)`[计划] test/run_latency_test.py` 一键编排(open/clear/flash/wait-idle/peer/collect/merge/verdict)——待排期;
@@ -716,6 +721,9 @@ sock4 auto-relisten ok / relisten retry ok ← 监听器随后自行恢复
(实测 §9.2:必检标记 `net init: OK` 曾被竞态吞掉 → 影响开机验收可靠性)。过渡:必现证据行按
"静默采集 + 延迟到安静窗口打印"模式(范例:P0-a 验收行,验收完成后已移除);根治 = S8(串口 TX 环形缓冲
+ 中断/DMA,溢出丢弃策略需定义)。
**现状(2026-10-25)**:根治已随单写者落地——行级原子 = 临界区整行入环、单写者发出,
交错/丢行结构性消除;「写者注册前」窗口为裸发例外,纪律 = `dbg_console_writer_ready()`
门控(`../子系统设计/控制台单写者日志设计说明.md` §3.4)。
---