问题现象
TMWebDriver 的 web_scan / web_execute_js 能正常返回数据,但返回的是历史脏数据(陈旧缓存),而非当前真实页面内容。例如:用户浏览器实际只有 2 个标签页,却读到 16 个早已关闭的历史标签页。重启浏览器、重载扩展均无效。
根本原因
TMWebDriver master 进程的 stdout(fd 1/2)被系统吊销(revoke)变成坏管道。master 在处理每条 WS 消息时会执行 print() 输出日志,而向坏管道写入会抛出 BrokenPipeError,导致当前 WebSocket 连接被关闭。
因此 Chrome 扩展每次连接 master 后约 1 秒就被断开(扩展发送 ext_ready 触发了 master 的 print),无法获取新数据,只能回退到缓存的脏数据。
最迷惑的点
此时 端口仍在 LISTEN、WS 握手仍返回 101 成功,从表面看 master 完全正常,极难定位。
证据链
- master 进程的 fd 0/1/2 全部显示
(revoked):
lsof -p <master_PID> -a -d 0,1,2 # 显示 (revoked) 即为坏管道
- 本地复现:向坏管道
print() 会抛 BrokenPipeError
lsof -nP -iTCP:18765 中 Chrome 的 ESTABLISHED 连接每隔约 1 秒被重置(重连)
诊断方法
# 1. 检查 master stdout 是否被 revoke(关键)
lsof -p <master_PID> -a -d 0,1,2
# 2. 检查 Chrome 扩展的 WS 连接是否秒断
lsof -nP -iTCP:18765
建议修复
1. 代码层面(推荐,根治):master 的日志输出不要直接写 stdout,而是写入真实文件;或对 stdout 写入做容错:
import functools, sys
def safe_print(*a, **k):
try:
print(*a, **k)
except BrokenPipeError:
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno()) # 重定向到 /dev/null,避免反复崩溃
并建议在 read-loop 的 except 分支中区分 BrokenPipeError,避免因日志输出失败而关闭正常的数据连接。
2. 启动层面:用 stdout 重定向到真实文件的方式启动,避免被系统 revoke:
nohup python -c "from TMWebDriver import TMWebDriver; import time; TMWebDriver(); [time.sleep(3600) for _ in iter(int,1)]" > /tmp/tmwd.log 2>&1 &
⚠ 注意:当前 TMWebDriver.__main__ 仅实例化而无保活循环,daemon WS 线程会随主进程退出而死亡,主进程必须加 while True: time.sleep(3600) 保活。
环境
- macOS(stdout revoke 现象在 macOS 上观察到)
- TMWebDriver 通过 Chrome 扩展接管用户浏览器(端口 18765 WS / 18766 HTTP)
排查这个隐藏 bug 花了不少时间,希望根因分析对项目有帮助 🙏
问题现象
TMWebDriver 的
web_scan/web_execute_js能正常返回数据,但返回的是历史脏数据(陈旧缓存),而非当前真实页面内容。例如:用户浏览器实际只有 2 个标签页,却读到 16 个早已关闭的历史标签页。重启浏览器、重载扩展均无效。根本原因
TMWebDriver master 进程的 stdout(fd 1/2)被系统吊销(revoke)变成坏管道。master 在处理每条 WS 消息时会执行
print()输出日志,而向坏管道写入会抛出BrokenPipeError,导致当前 WebSocket 连接被关闭。因此 Chrome 扩展每次连接 master 后约 1 秒就被断开(扩展发送
ext_ready触发了 master 的 print),无法获取新数据,只能回退到缓存的脏数据。最迷惑的点
此时 端口仍在 LISTEN、WS 握手仍返回 101 成功,从表面看 master 完全正常,极难定位。
证据链
(revoked):print()会抛BrokenPipeErrorlsof -nP -iTCP:18765中 Chrome 的 ESTABLISHED 连接每隔约 1 秒被重置(重连)诊断方法
建议修复
1. 代码层面(推荐,根治):master 的日志输出不要直接写 stdout,而是写入真实文件;或对 stdout 写入做容错:
并建议在 read-loop 的 except 分支中区分
BrokenPipeError,避免因日志输出失败而关闭正常的数据连接。2. 启动层面:用 stdout 重定向到真实文件的方式启动,避免被系统 revoke:
⚠ 注意:当前
TMWebDriver.__main__仅实例化而无保活循环,daemon WS 线程会随主进程退出而死亡,主进程必须加while True: time.sleep(3600)保活。环境
排查这个隐藏 bug 花了不少时间,希望根因分析对项目有帮助 🙏