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_diagnose的controlMatchesEditor正常、无其他异常。
根因
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}) # 放行,游戏继续
六、诊断清单(断点不命中时按序排查)
unity_diagnose:attached/ 断点verified/ 控制层状态unity_stack短测:超时 = 端口污染 → 重启 Unity 实例- 确认已进 Play 且目标界面已打开(
OnInitStart只执行一次,需重新触发) - 确认断点是方法体行(签名行可能映射不到 IL)
- 若热更程序集仍不命中:查 Unity 日志确认是源码编译还是 dll 解释执行
(
at Assets/ScriptsHotUpdate/...源码行号 = 可断;LoadImage HotUpdate.dll= 解释执行不可断)
七、配套
- 控制层(
Assets/Editor/UnityBridgeControl.cs,可选):提供 Play 控制(cmd.json)+ playmode 信号。 未安装的工程(如 ProjectDrift)断点调试完全正常,仅 Play 控制不可用。