HybridCLR 热更工程断点调试实战指南

August 18, 2026 · View on GitHub

目标:让 DSH 调试桥在 HybridCLR 热更工程(ScriptsHotUpdate / HotUpdate.dll.bytes)上 也能命中热更代码断点。本文以 ProjectDrift 工程实测(MainView.OnInitStart 断点)为准。

一、结论先行(实测验证)

  • HybridCLR 热更代码断点完全可行:在干净 Unity 实例上,MainView.OnInitStart 等热更程序集断点正常命中(快照、求值都工作)。
  • 断点不命中的首因不是 HybridCLR,而是 Unity 调试端口被污染(见下)。
  • 编辑器模式下热更代码以源码编译运行(Unity 日志调用栈显示 at Assets/ScriptsHotUpdate/... 源码行号),不是 dll 解释执行——Mono 调试器可断。

二、正确调试流程(务必遵守)

1. Unity 打开目标工程(干净实例)
2. 进入 Play 模式并走到要调试的界面   ← 先 Play!
3. attach(读 Library/EditorInstance.json 自动发现进程/端口)
4. 设断点(热更源码绝对路径 + 行号)
5. 断点命中 → 自动快照(栈+变量)→ Agent 决策

关键点:

  • 先 Play 后 attach:跨域重载(进出 Play)设的断点会丢失绑定。Play 稳定后 attach, 断点绑定当前运行域,命中可靠。
  • 域重载保活(bridge 内置):Play 切换(域重载)后自动重设断点(3s 周期 rearmer), 与 Play 状态无关;依赖控制层信号的 playmode watcher 另有基线保护。
  • 不要反复 attach/detach 同一个实例:见"端口污染"。

三、Unity 调试端口污染(最大的坑)

症状

  • attach 返回成功、断点 verified: true(adapter 乐观确认),但永远不命中
  • 任何 DAP 请求(stackTrace/evaluate/continue)超时
  • unity_diagnosecontrolMatchesEditor 正常、无其他异常。

根因

Unity 2022.3 的调试端口(56000 + process_id % 1000一次性 DWP 握手

  • 优雅断开(DAP disconnect + adapter 优雅退出)后可再次 attach(已实测多会话通过);
  • 强杀(kill adapter 进程)后端口挂起,后续连接假成功但无响应,只能重启编辑器恢复

触发污染的常见操作

操作是否污染
DAP disconnect + adapter 优雅退出否(可复用)
直接 kill UnityDebug.exe / 热重载强杀(需重启 Unity)
对端口做 TCP 探测(connect+close)(不要做任何预检连接)
同一时刻第二个调试器 attach是(互斥)

规避(已内建)

  • bridge 提供 POST /api/shutdown:先优雅 detach adapter 再退出进程;
  • ui-plugin 看门狗重启 bridge 前先请求 shutdown,2.5s 未退出才强杀;
  • bridge 收到 SIGTERM/SIGINT 同样优雅 detach 后退出。
  • 教训:开发期反复改 bridge 代码曾多次污染端口,才逼出这套优雅退出。

四、静态 vs 运行时(为什么必须断点取证)

以 MainView 按钮为例:

来源结果
磁盘 prefab(Assets/Package/UI/Main/MainView.prefab只有 storeBtn 节点有绑定
运行时(AB 版 prefab,断点命中后求值)ButtonStartGame / SettingBtn / talentBtn / storeBtn / SellBtn 全部绑定;mailBtn 为 null(bug)
  • 游戏运行时加载的是 AssetBundle 版本的 UI 预制体(YooAsset),与磁盘源 prefab 不同;
  • 静态分析不可信,必须以运行时断点取证——这正是 AI 断点调试器的价值。

五、Agent 决策工作流(本项目实测)

unity_attach(projectPath)
unity_set_breakpoint(MainView.cs, 49)          # OnInitStart 方法体首句
(用户在游戏里触发界面)
unity_wait_hit({since:0})                       # 触发式:命中即返回快照
unity_evaluate({expression: "xxxBtn?.name"})    # 取证:拿字段运行时值
unity_continue({resumeAfterStop:true})          # 放行,游戏继续

六、诊断清单(断点不命中时按序排查)

  1. unity_diagnoseattached / 断点 verified / 控制层状态
  2. unity_stack 短测:超时 = 端口污染 → 重启 Unity 实例
  3. 确认已进 Play 且目标界面已打开(OnInitStart 只执行一次,需重新触发)
  4. 确认断点是方法体行(签名行可能映射不到 IL)
  5. 若热更程序集仍不命中:查 Unity 日志确认是源码编译还是 dll 解释执行 (at Assets/ScriptsHotUpdate/... 源码行号 = 可断;LoadImage HotUpdate.dll = 解释执行不可断)

七、配套

  • 控制层(Assets/Editor/UnityBridgeControl.cs,可选):提供 Play 控制(cmd.json)+ playmode 信号。 未安装的工程(如 ProjectDrift)断点调试完全正常,仅 Play 控制不可用。