Java平台远程调试
背景
最近我们做的AI决策算法作为服务器逻辑为诛仙2项目提供服务。由于公司采用了内外网分离的开发策略,我们的服务器在内网进行开发,项目组的服务器是严谨进入外网的。到了联调的时候就特别头疼。因为我们大多数的逻辑都是一场战役的逻辑处理和NPC行为的决策展现,使用log调试的方式分外艰难。所以,就想法在测试服进行调试,测试服属于运维网段,是项目组游戏服务器除了它的开发环境和最终生产环境唯一能接触的生产环境了。这个服务器上,我们偷偷钻个洞进行远程调试。
Java的远程调试
JPDA 体系
对于 Java 虚拟机接口熟悉的人来说,您一定还记得 Java 提供了两个接口体系,JVMPI(Java Virtual Machine Profiler Interface)和 JVMDI(Java Virtual Machine Debug Interface),而它们,以及在 Java SE 5 中准备代替它们的 JVMTI(Java Virtual Machine Tool Interface),都是 Java 平台调试体系(Java Platform Debugger Architecture,JPDA)的重要组成部分。
JPDA 组成模块
JPDA 定义了一个完整独立的体系,它由三个相对独立的层次共同组成,而且规定了它们三者之间的交互方式,或者说定义了它们通信的接口。这三个层次由低到高分别是 - Java 虚拟机工具接口(JVMTI) - Java 调试线协议(JDWP) - Java 调试接口(JDI)
这三个模块把调试过程分解成几个很自然的概念:调试者(debugger)和被调试者(debuggee),以及他们中间的通信器。
在调试者和被调试着之间,调试命令和调试结果,都是通过 JDWP 的通讯协议传输的。所有的命令被封装成 JDWP 命令包,通过传输层发送给被调试者,被调试者接收到 JDWP 命令包后,解析这个命令并转化为 JVMTI 的调用,在被调试者上运行。类似的,JVMTI 的运行结果,被格式化成 JDWP 数据包,发送给调试者并返回给 JDI 调用。而调试器开发人员就是通过 JDI 得到数据,发出指令。

模块 | 层次 | 编程语言 | 作用 |
---|---|---|---|
JVMTI | 底层 | C | 获取及控制当前虚拟机状态 |
JDWP | 中介层 | C | 定义 JVMTI 和 JDI 交互的数据格式 |
JDI | 高层 | Java | 提供 Java API 来远程控制被调试虚拟机 |
Java 调试连接模式
按照工作方式来分,Java调试工作模式有两种,Socket套接字或者共享内存。
在远程调试中,显然共享内存是无法工作的,它只适应于同一台机器上的调试。所以,接下来只讨论基于网络套接字的工作模式。
站在调试器(JDI)的视角,把调试分为两种类别,分别是:
- 主动调试:jvm启动时设置调试端口并监听,在调试器发来调试信息时进行命令响应。这种模式在调试过程中很常用,适用于跟踪一些可重复性的逻辑bug。
- 被动模式:jvm启动时设置调试器端口,在jvm启动时会尝试建立链接并停下等待命令。这种模式主要用于调试应用无法正常启动的情况。(主动调试无效,因为启动不起来我们没有机会做连接)
主动调试
主动调试时,对jvm添加参数
1 |
|
被动调试
被动调试时,对jvm添加参数
1 |
|
在IDEA上实践
菜单->Run->EditConfigurations...
左侧点击+,添加Remote, 右侧选择 Use module classpath为要调试的项目, Debugger mode进行选择后, 虚拟机参数会提示给出,按照给出的参数启动虚拟机即可。