925058a5ae
- lftpd:net_inet_ntoa 段序改 MSB 首 + 新增 net_getpeername,FTP 日志打印 真实对端地址(真机实证 192.168.1.2:55524,登记 TRAP-NET-21) - E6:NET_U15_PROBE 命中=0(含压测)→ 阶段 3 项 4 不立项,U15 登记观察项; 插桩留仓(宏=0 零成本),soak 驱动 test/host/e6_soak.py 入库 - 编号:全库 Trap NN → TRAP-NET-<nn> 迁移并重排连续 01~21(单调映射, 沿革见 CH395F_Trap_Records.md 头注);死号 12/13/19/21 引用全部清除; GD5F 域引用补正为 TRAP-GD5F-NN - 陷阱文档全新化:索引 21 行行行真陷阱;删误判存档/发现时间/git 考古/ FTP 附录;台架纪律教训沉淀第六章方法论 - RAM:Code 211348→211456(+108 lftpd 修复),RW+ZI 净增 +4B; 优化方案/台账受管区/tsv 同笔同步 - ftp_abort_probe.py 挪 test/host/;引用关系图重生成 - 回归:@build 0/0;@test PASS;ftp_abort 10/10;relisten :7003 20/20; ci_local L0+L0.5 全绿;E6 窗口零复位,7 条 WD stall 登记压测假象备查
111 lines
4.4 KiB
Python
111 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
||
"""E-02 头阻塞专项台架探针(timeSync 侧,A/B 对照用)。
|
||
|
||
场景(错误码传播审计.md E-02 的 timeSync 触发形态):
|
||
1. 一个"挂起"的对时客户端连上 :8888 后不发任何数据(空闲/故障客户端,
|
||
常见工况)——旧固件在此触发 30s 阻塞 net_recv 独占 netTask,
|
||
全体消费者的队列消息停摆最长 30s;
|
||
2. 2s 后从 PC 发起 FTP(:21) 连接,测 banner 到达时延 = 队列响应性:
|
||
修复前 ≈ 25~30s(队头冻结),修复后 ≈ 数百 ms 内;
|
||
3. 34s 时 30s 总预算应已耗尽:设备拆掉该会话(N34 阶段 3 net_drop_conn:
|
||
FIN 关闭 + 本层收口 + 重监听),随后**本探针模拟守规矩对端**:
|
||
close 自己的 socket;
|
||
4. 芯片回 CLOSED(TC-NET-204 模式 A:TIME_WAIT 过渡 ~150ms)→ relisten,
|
||
:8888 应能再接受新连接(3s 重试窗内);新连接发完整 8 字节 TIME 包 →
|
||
设备应落 "wall clock updated"。
|
||
|
||
⚠ 定案口径(TRAP-NET-07 定案更正 2026-10-11 + 实验方案 E13 §5):CH395F close
|
||
**无 RST 语义**。若对端内核存活(app 挂起但 socket 不关,内核自动 ACK FIN),
|
||
芯片停在 FIN_WAIT_2、监听恢复受限于对端 close——探针第 3 步的 close 正是
|
||
模拟"对端配合"这一可恢复边界;不配合场景的兜底是芯片 TIMEOUT/keepalive
|
||
拆僵尸会话(E13 定案,30~90s)。
|
||
|
||
用法: python e02_probe.py <run-tag>
|
||
退出码: 0 = 队列响应性 PASS;1 = FAIL;2 = 探针自身异常
|
||
"""
|
||
import socket
|
||
import sys
|
||
import time
|
||
|
||
DEV = "192.168.1.100"
|
||
tag = sys.argv[1] if len(sys.argv) > 1 else "run"
|
||
|
||
t0 = time.time()
|
||
|
||
|
||
def log(msg):
|
||
print("[%s] %9.3f %s" % (tag, time.time() - t0, msg), flush=True)
|
||
|
||
|
||
def main():
|
||
# 1. 挂起对时客户端:连接后不发任何数据
|
||
ts = socket.create_connection((DEV, 8888), timeout=5)
|
||
log("time-sync conn open (hung client: sends nothing)")
|
||
|
||
# 2. 2s 后测 FTP banner 时延(队列响应性)
|
||
time.sleep(2.0)
|
||
t1 = time.time()
|
||
banner = None
|
||
try:
|
||
ftp = socket.create_connection((DEV, 21), timeout=35)
|
||
banner = ftp.recv(256)
|
||
dt = time.time() - t1
|
||
log("FTP banner after %8.1f ms: %r" % (dt * 1000, banner[:40]))
|
||
ftp.close()
|
||
except Exception as e:
|
||
dt = time.time() - t1
|
||
log("FTP connect FAIL after %8.1f ms: %r" % (dt * 1000, e))
|
||
dt = None
|
||
queue_ok = (banner is not None) and (dt is not None) and (dt < 3.0)
|
||
log("queue responsiveness: %s (banner %s ms)"
|
||
% ("PASS" if queue_ok else "FAIL",
|
||
("%.0f" % (dt * 1000)) if dt is not None else "n/a"))
|
||
|
||
# 3. 等 30s 总预算耗尽(设备应拆会话:FIN + 本层收口 + 重监听)
|
||
remain = 34.0 - (time.time() - t0)
|
||
if remain > 0:
|
||
time.sleep(remain)
|
||
|
||
# 3b. 模拟守规矩对端:设备已发 FIN(drop_conn),本端 close 配合拆链。
|
||
# CH395F close 无 RST 语义(TRAP-NET-07 定案更正):不配合的内核存活对端
|
||
# 会把芯片停在 FIN_WAIT_2,监听恢复受限于对端 close——不在本探针判据内。
|
||
try:
|
||
ts.close()
|
||
log("hung client socket closed (cooperate with device FIN)")
|
||
except OSError:
|
||
pass # 设备已先拆连,本地 close 报 ECONNRESET 属预期
|
||
|
||
# 4. 监听应已重建(芯片 TIME_WAIT 过渡 ~150ms + relisten):3s 重试窗
|
||
t2 = time.time()
|
||
listener_ok = False
|
||
ts2 = None
|
||
while time.time() - t2 < 3.0:
|
||
try:
|
||
ts2 = socket.create_connection((DEV, 8888), timeout=2)
|
||
break
|
||
except Exception as e:
|
||
log("second time-sync conn retry: %r" % (e,))
|
||
time.sleep(0.3)
|
||
if ts2 is not None:
|
||
dt2 = time.time() - t2
|
||
epoch = int(time.time())
|
||
log("new time-sync conn in %6.0f ms, sent full TIME packet (epoch=%d)"
|
||
% (dt2 * 1000, epoch))
|
||
ts2.sendall(b"TIME" + epoch.to_bytes(4, "big"))
|
||
time.sleep(1.5) # 留时间给设备处理并打日志
|
||
ts2.close()
|
||
listener_ok = True
|
||
else:
|
||
log("second time-sync conn FAIL (3s window)")
|
||
log("OVERALL: queue=%s listener=%s"
|
||
% ("PASS" if queue_ok else "FAIL", "PASS" if listener_ok else "FAIL"))
|
||
return 0 if (queue_ok and listener_ok) else 1
|
||
|
||
|
||
if __name__ == "__main__":
|
||
try:
|
||
sys.exit(main())
|
||
except Exception as e:
|
||
print("[%s] PROBE ERROR: %r" % (tag, e), flush=True)
|
||
sys.exit(2)
|