调试协议适配与远程调试进阶¶
调试器与 XDebugger 介绍了调试器扩展架构。本章进一步讨论调试协议适配:如何把 Debug Adapter Protocol、自定义 socket 协议、语言运行时调试 API 或远程调试服务接到 IntelliJ Platform 的 Execution/XDebugger 模型上。
官方 SDK 对 XDebugger 的高层文档较少,实际实现通常需要结合 Execution、Run Configurations、Plugin Compatibility、目标语言插件源码和 API 文档一起做。本文给出工程结构和风险清单,避免直接绑定某个不稳定实现。
架构分层¶
RunConfiguration
-> ProgramRunner / RunProfileState
-> Debug backend process or remote connection
-> Protocol adapter
-> XDebugProcess
-> Breakpoints / StackFrames / Variables / Evaluator
建议把协议适配拆成独立层:
| 层 | 职责 |
|---|---|
| Run Configuration | 保存用户配置、校验目标、选择 run/debug |
| Process launcher | 启动本地进程、容器、远程连接或 debug adapter |
| Protocol client | 处理 JSON-RPC、socket、stdio、认证、重连 |
| Debug model mapper | 把协议对象映射为 XDebugger 模型 |
| UI integration | Console、断点、变量树、表达式求值 |
不要在 XDebugProcess 中塞满协议细节。它应该协调调试会话,而不是直接承担所有 IO、序列化和状态机。
DAP 与自定义协议¶
Debug Adapter Protocol 常见对象:
- initialize / launch / attach
- setBreakpoints
- configurationDone
- threads
- stackTrace
- scopes
- variables
- evaluate
- continue / pause / next / stepIn / stepOut
- stopped / continued / terminated / output events
映射到 IntelliJ:
| DAP / 协议概念 | IntelliJ 概念 |
|---|---|
| launch / attach | Run Configuration + ProgramRunner |
| stopped event | session paused,创建 stack frame |
| stackTrace | XStackFrame 列表 |
| source path + line | XSourcePosition |
| scopes / variables | XValue tree |
| evaluate | XDebuggerEvaluator |
| setBreakpoints | XBreakpointHandler |
| output | Console / ProcessHandler |
自定义协议也按这张表设计,不要把协议对象直接暴露给 UI。
断点同步¶
断点同步要处理这些情况:
- IDE 中新增、删除、启用、禁用断点。
- 调试会话启动前已有断点。
- 后端返回断点已验证或不可用。
- 源文件路径映射失败。
- 条件断点、日志断点、命中次数。
- 远程源代码和本地源代码不一致。
建议维护一个断点映射表:
XBreakpoint id -> protocol breakpoint id / source path / line / verified state
如果后端支持 verified breakpoint,应该把不可命中原因反馈到 IDE UI 或 console,而不是静默失败。
Source Path Mapping¶
远程调试最常见的问题是源位置映射:
| 问题 | 处理 |
|---|---|
| 远程路径和本地项目路径不同 | 提供 path mappings 设置 |
| 容器路径不同 | 默认从工作目录推断,可手动覆盖 |
| 生成代码 | 尽量使用 source map 或语言级 symbol mapping |
| 临时脚本 | 创建 light virtual file 或只显示 console 位置 |
| 行列基准不同 | 统一 0-based / 1-based 转换 |
映射失败不要让调试器崩溃。应在 console 中给出后端路径和配置建议。
变量树与性能¶
变量树必须懒加载:
- 第一层只加载当前 scope。
- 子对象展开时再请求 variables。
- 大数组分页或限制数量。
- 字符串和集合要设置合理预览。
- 远程请求要可取消。
不要一次性把整个对象图发到前端。大型对象可能包含循环引用、惰性属性、远程 getter 或昂贵计算。
表达式求值¶
表达式求值要明确上下文:
- 当前线程。
- 当前 stack frame。
- 当前语言或模板上下文。
- 用户输入是否需要包裹成语句或表达式。
- 后端是否允许副作用。
错误要按用户可理解的方式展示,例如语法错误、上下文不可用、线程已恢复、后端超时、远程连接断开。
远程连接生命周期¶
远程调试要处理:
- 连接超时。
- 认证失败。
- 端口占用。
- debug adapter 进程退出。
- 网络断开。
- IDE Stop 被重复调用。
- 后端已结束但 IDE session 尚未清理。
stop() 必须幂等。重复 stop、进程自然退出、连接断开和用户关闭 IDE 都应走同一套资源释放路径。
Split Mode 注意事项¶
调试后端通常属于 backend。UI、console 和少量交互在 frontend。Split Mode 下:
- 不要把 socket client 放到 frontend 访问远程项目路径。
- 前端只发用户意图和展示结果。
- 后端持有 protocol client 和项目上下文。
- RPC DTO 中不要包含 PSI、VirtualFile、Swing 组件。
- 延迟环境下变量展开和表达式求值要有进度与取消。
测试矩阵¶
至少覆盖:
- 本地 launch。
- attach 到已有进程。
- 无效路径断点。
- 条件断点。
- stop 后资源释放。
- 后端异常退出。
- 远程路径映射。
- 变量树大对象展开。
- 表达式求值错误。
- Split Mode 延迟模拟。