0%

JPDA--java调试体系

JPDA介绍

JPDA(Java Platform Debugger Architecture)是 Java 平台调试体系结构的缩写,通过 JPDA 提供的 API,开发人员可以方便灵活的搭建 Java 调试应用程序。

JPDA 主要由三个部分组成:

  1. JVMTI(Java Virtual Machine Tool Interface,Java 虚拟机工具接口)
  2. JDWP(Java Debug Wire Protocol,Java 调试线协议)
  3. JDI(Java Debug Interface,Java 调试接口)

JPDA 定义了一个完整独立的体系,它由三个相对独立的层次共同组成,而且规定了它们三者之间的交互方式,即通信的接口。

这三个层次由低到高分别是 Java 虚拟机工具接口(JVMTI),Java 调试线协议(JDWP)以及 Java 调试接口(JDI)。可以分别比喻为调试者(debugger)和被调试者(debuggee),以及他们中间的通信器。

被调试者:运行于我们想调试的 Java 虚拟机之上,它可以通过 JVMTI 这个标准接口,监控当前虚拟机的信息;

调试者:定义了用户可使用的调试接口,通过这些接口,用户可以对被调试虚拟机发送调试命令,同时调试者接受并显示调试结果。

调试者和被调试者之间:调试命令和调试结果,都是通过 JDWP 的通讯协议传输的。所有的命令被封装成 JDWP 命令包,通过传输层发送给被调试者,被调试者接收到 JDWP 命令包后,解析这个命令并转化为 JVMTI 的调用,在被调试者上运行。类似的,JVMTI 的运行结果,被格式化成 JDWP 数据包,发送给调试者并返回给 JDI 调用。

JVMTI

JVMTI全称是Java Virtual Machine Tool Interface,中文翻译是Java虚拟机工具接口。它是一套有JVM提供的native接口,它是JPDA体系最底层,也是核心层,所有的调试功能都需要JVMTI来提供。

JDWP
JDWP全称是Java Debug Wire Protocol,中文翻译是一种专门为Java调试而设计的一个通讯交互协议。协议包里的内容包括请求命令、回应数据以及错误代码。Sun公司的实现为jdwp.dll(jdwp.so),它实现了一个代理,负责解析前端向JVM发送的请求或命令,将其转化为JVMTI调用,然后将JVMTI函数的返回值封装成JDWP数据包发给前端。注意:JDWP本身不包括传输层的实现,传输层需要独立实现,但是JDWP包含了和传输层交互的严格定义。Sun公司提供的jdk中,在传输层上它提供了socket方式。

JDI
JDI全称是Java Debug Interface,是三个模块中的最高层,在大多数JDK中,它都是有Java语言来实现的,通过它,开发者可以通过前端JVM上的调试器来调试后端JVM上被调试的程序。

JVMTI详解

JVMTI(Java Virtual Machine Tool Interface)是一套由 Java 虚拟机提供的,为 JVM 相关的工具提供的本地编程接口集合

JVMTI 是从 Java SE 5 开始引入,整合和取代了以前使用的 Java Virtual Machine Profiler Interface (JVMPI) 和 the Java Virtual Machine Debug Interface (JVMDI),而在 Java SE 6 中,JVMPI 和 JVMDI 已经消失了。

JVMTI 提供了一套”代理”程序机制,可以支持第三方工具程序以代理的方式连接和访问 JVM,并利用 JVMTI 提供的丰富的编程接口,完成很多跟 JVM 相关的功能。

JVMTI可以用来开发并监控虚拟机,可以查看JVM内部的状态,并控制JVM应用程序的执行。可实现的功能包括但不限于:调试、监控、线程分析、覆盖率分析工具等。

另外,需要注意的是,并非所有的JVM实现都支持JVMTI。

JVMTI只是一套接口,我们要开发JVM工具就需要写一个Agent程序来使用这些接口。Agent程序其实就是一个C/C++语言编写的动态链接库。

我们通过JVMTI开发好agent程序后,把程序编译成动态链接库,之后可以在jvm启动时指定加载运行该agent。

1
-agentlib:<agent-lib-name>=<options>

Agent的工作形式

agent启动后是和JVM运行在同一个进程,大多agent的工作形式是作为服务端接收来自客户端的请求,然后根据请求命令调用JVMTI的相关接口再返回结果。

很多java监控、诊断工具都是基于这种形式来工作的。如果arthas、jinfo、brace等。

Debug 原理

无论我们在开发调试时,都会用到调试工具。其实我们用的所有调试工具其底层都是基于JVMTI的调用。JVMTI本身就提供了关于调试程序的一系列接口,我们只需要编写agent就可以开发一套调试工具了。

虽然对应的接口已经有了,但是要基于这些接口开发一套完整的调试工具还是有一定工作量的。
为了避免重复造轮子,sun公司定义了一套完整独立的调试体系,也就是JDPA。

其实有了jdwp Agent以及知道了交互的消息协议格式,我们就可以基于这些开发一套调试工具了。
但是相对还是比较费时费力,所以才有了JDI的诞生,JDI是一套JAVA API。
这样对于不熟悉C/C++的java程序员也能开发自己的调试工具了。

另外,JDI 不仅能帮助开发人员格式化 JDWP 数据,而且还能为 JDWP 数据传输提供队列、缓存等优化服务

再回头看一下启动JVM debug时需要带上的参数:

1
java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000 -jar test.jar 

jdwp.dll作为一个jvm内置的agent,不需要上文说的-agentlib来启动agent。这里通过-Xrunjdwp来启动该agent。后面还指定了一些参数:

  • transport=dt_socket,表示用监听socket端口的方式来建立连接,这里也可以选择dt_shmem共享内存方式,但限于windows机器,并且服务端和客户端位于一台机器上
  • server=y 表示当前是调试服务端,=n表示当前是调试客户端
  • suspend=n 表示启动时不中断(如果启动时中断,一般用于调试启动不了的问题)
  • address=8000 表示本地监听8000端口

参考

https://blog.csdn.net/tterminator/article/details/52135630