您的位置 首页 java

底层技术揭秘:java调试工具实现原理与使用技巧

原创声明

文 吴潇/ java 高级工程师


java 技术栈的程序员大多使用过远程调试。如果你还没有用过java远程调试,请仔细看一看本篇文章第一小节,查问题效率立即提升数倍;对于使用过 Java 远程调试的老手来说,有没有想过它的底层是怎么实现的呢?今天这篇文章就来揭秘(程序员应该了解自己每天使用的工具,磨炼自己的技艺)

1. Java远程调试基本操作

Java进程默认不支持远程调试,如果需要远程调试,必须在启动java之前加上特定选项。

第一步、在启动 jvm 的时候,加上以下调试选项:

  1. Java 5以前:
    -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044
  2. Java 5及以后:
    -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044

两者之间有啥区别

“-Xdebug -Xrunjdwp” 主要用于Java 5以前。其中,“-Xdebug”是让JVM开启调试支持,开启调试功能就会要求jvm运行于解释执行模式,因此,java程序的执行速度就变得非常非常慢;“-Xrunjdwp”是让JVM运行一个JDWP协议,从而允许远程调试。

-agentlib:jdwp 用于Java 5及以后,开启后JVM运行于JIT模式,速度更快。因为Java 5采用了HotSpot VM,增加了动态反优化技术,使得调试速度更快。通过这个选项长期开启调试支持也不会影响程序运行速度。同时还支持热交换技术,使得在调试的过程中可以修改Class的代码,从而更快速地定位到问题。

以上两种方式,都需要进一步配置远程调试参数,即“runjdwp:”和“jdwp=”后面的选项。选项具体配置如下。

底层技术揭秘:java调试工具实现原理与使用技巧

第二步、在 IntelliJ IDEA 中,新建一个远程调试的运行配置(Run configuration):

底层技术揭秘:java调试工具实现原理与使用技巧

第三步、点击调试按钮,就可以进行远程调试了。

注意要满足以下条件:

  1. 要确保本地代码与远程代码一致;
  2. 被调试的程序在编译的时候,加上了调试信息的(命令:javac -g …)

2. IntelliJ IDEA 或 Eclipse 如何实现调试功能的

有了远程调试经验的老手们可能会遇到一些实际问题。例如,有的bug在线上能够稳定复现,但是当我们对目标程序进行调试的时候,这个bug竟然神奇般地消失了。你有遇到过这样的问题吗?我就遇到过这样的问题。正是我遇到的这个诡异现象促使我全面了解了一遍java远程调试到底是怎么实现的,它对目标程序自身有什么影响?

比如,我们在debug一段程序的时候,需要设置断点,然后再单步执行。当程序运行到某个断点位置时,这个线程就会被suspend,等待我们的debugger告诉目标程序,下一步要怎么执行,是step还是resume还是run to cursor等。假如我们正在单步执行,又有新线程命中这个断点,那么这个 线程 是会继续执行还是会suspend呢?我们在 debug 的过程中,线程被暂停,GC又是怎么发生的,也被暂停了吗?线程被我们的断点暂停后,跟时间相关的代码会把我们调试导致的暂停的时间也包含在内吗?诸如此类的问题,要想弄明白,都需要我们深入了解java远程调试这个底层技术。

3. Java Platform Debugger Architecture (JPDA)

JPDA是Java实现调试功能的架构设计,主要目的是提供给工具开发商开发调试工具 (debugger application)。JPDA保证了调试工具可跨平台、跨JVM、跨JDK版本运行。

JPDA是分层设计的,总共分3层:

  1. Java VM Tool Interface (JVM TI): Java虚拟机工具接口层,位于JPDA最底层,负责定义由JVM提供的调试服务的接口,而JVM负责实现JVM TI接口。可见,Java程序的调试也是按照SPI(Service Provier Interface)设计模式设计的,把JVM的调试能力看作一种服务,通过接口来对外提供这种服务,而JVM负责调试功能的具体实现。
  2. Java Debug Wire Protocol (JDWP):负责被调试的进程和debugger前端之间的通信。JVM TI提供的调试服务是面向native语言的(C/C++),主要供JVM进程内部调用,因此需要通过JDWP来实现跨进程调试。
  3. Java Debug Interface (JDI):面向Java语言的最高层接口,供工具开发商快速开发debugger程序,实现远程调试。 JDK 推荐使用JDI开发debugger,从而获得java技术栈的跨平台运行能力。

JPDA架构图

底层技术揭秘:java调试工具实现原理与使用技巧

  1. debuggee: 被调试进程,包含正在被调试的应用、运行应用的VM、debugger后端Java Virtual Machine: java的调试功能最终是由 VM 负责实现的 。back-end: 主要职责是通信,把调试请求从debugger前端发送到VM,再把response返回回去。debugger后端与前端通信是基于JDWP协议。
  2. communication channel: debugger后端与debugger前端之间的连接,包含2个组件。connector: 通过connector建立连接。connector分三类:listening connector, attaching connector, launching connectortransport: 负责底层数据交换,可以用的transport有sockets, serial lines, 和 shared memory
  3. front-end: 负责实现 JDI 接口
底层技术揭秘:java调试工具实现原理与使用技巧

JPDA只是定义了接口规范 ,需要具体实现,才能实现java调试。

Oracle JDK 针对这3个接口,提供了参考实现,具体包含:

  1. 在VM上实现了JVM TI 接口
  2. 一个debugger back-end 实现(使用了JVM TI接口,实现了JDWP协议的debuggee一端)
  3. 一个debugger front-end 实现(使用了JDWP协议的debugger一端的功能,实现了JDI接口)
  4. 2个简单的基于JDI的调试工具,即 jdb

JDK提供的参考实现

底层技术揭秘:java调试工具实现原理与使用技巧

JPDA是分层架构设计,其中的每一层都是可以替换的。调试工具可以基于JDI开发,也可以基于JDWP开发,或直接基于JVM TI开发。

Java中的调试功能的实现步骤

  1. VM提供调试服务,例如提供API用于设置断点,取消断点,设置watch point,单步执行、查看stack frame等。
  2. VM把调试功能封装成API,通过JVM TI接口的形式提供给调用方。
  3. debugger后端程序通过调用JVM TI获得调试信息或设置调试动作,然后按照JDWP协议进行封装,实现远程调试。
  4. debugger前端按照JDWP协议调用debugger后端功能,把功能封装成JDI接口。
  5. 调试工具,例如IntelliJ IDEA中的debugger,直接调用JDI实现调试功能。

4. Java虚拟机工具接口 (JVM TI)

JVM TI (Java Virtual Machine Tool Interface) 是一个本地编程接口(C++),主要给工具开发商使用,例如像Eclipse、IntelliJ IDEA这样的工具。JVM TI 可以实现的功能有:

  1. 查看Java应用程序内部状态
  2. 控制Java应用程序的执行
  3. 查看JVM的内部状态

通过用JVM TI来访问JVM的内部状态,可以实现各种各样的工具,例如 profiling(分析)、debugging(调试)、monitoring(监控)、thread analysis(线程分析) 和 coverage analysis等类型工具。

JVM的调试功能是在JVM内部实现,然后通过JVM TI接口开放给第三方工具厂商的。

JVM TI是一个双向接口。JVM TI的客户端(也就是agent)可以利用events功能感知到JVM中发生的事件,还可以通过functions功能来查询程序的状态和控制程序的执行。

这个agent与JVM在同一个进程中运行,它通过直接调用JVM TI接口与JVM通信。agent再被另外一个进程控制,这样实现java本地调试功能。

在windows系统上,agent一般是一个DLL库,在类unix系统上,agent一般是一个so文件。

agent是在JVM启动的时候,通过命令行选项加载的,有2种加载方式:

加载方式一

 -agentlib:<agent-lib-name>=<options>brbragent-lib-name 指的是agent的名称broptions是传给这个agents的选项  

加载方式二

 -agentpath:<path-to-agent>=<options>  

JVM TI 过于靠近底层,大部分工具是通过JPDA间接访问JVM TI的。

5. 远程调试通信协议:Java Debug Wire Protocol (JDWP)

JDWP是一种实现Java应用程序远程调试的通信协议,即jvm和debugger之间实现通信,例如传输线程状态、异常信息等。

JDWP实现debuggee和debugger之间的隔离,所以debugger可跨平台运行和调试。

隔离除了可以实现跨平台的优势以外,被调试的JVM中的GC、OOM等事件不会影响debugger自身运行。

JDWP只定义数据的格式,没有指定传输协议,只需一个简单API就可以支持多种传输方式。

JDWP简单,容易实现,灵活,便于扩展(JDWP设计考虑到了JDI的使用方便)。

JDWP Start Up(握手机制)

建立连接之后,首先要经过一次握手,然后才开始发送packet。

debugger → target vm: JDWP-Handshake

target vm → debugger: JDWP-Handshake

JDWP Packet (数据包)

packet分为2类:1. command packets 2. reply packets

debugger往target vm发送command packet,从而:1)获取信息 2)控制程序执行

target vm往debugger发送command packet,从而:通知debugger,vm中发生的事件(event)。

replay packet 用于通知操作是否成果和获取返回数据

Command Packet

Reply Packet

  • Headerlength (4 bytes)id (4 bytes)flags (1 byte)command set (1 byte)command (1 byte)
  • data (Variable)
  • Headerlength (4 bytes)id (4 bytes)flags (1 byte)error code (2 bytes)
  • data (Variable)

6. Java调试接口:Java Debug Interface (JDI)

JDI是JPDA架构中最高层Java API。通过调用JDI接口可以获得debugger需要的信息,也可以控制被调试程序的运行。

JDK通过SPI模式为JDI提供了具体实现(位于 lib/tools.jar),完整实现了JDWP协议后端部分,可以远程连接、Attach、监控和控制target JVM。

JDI组成部分

name

desc

com.sun.jdi

core package,定义了value, type 和 虚拟机的镜像

com.sun.jdi.connect

提供debugger和target vm之间的Connector实现,实现网络连接

com.sun.jdi.connect.spi

如果JDK自带的Connector实现不能满足需要,可以通过这里的接口自定义Connector

com.sun.jdi.event

JDI中的event定义和处理工具

com.sun.jdi.request

用于实现满足特定条件就发送event

基于JDI开发debugger步骤

1. 调用JDI中用于建立与target vm建立连接的API,创建Connector。本地调试可以创建LaunchingConnector,远程调试可以创建AttachingConnector。

底层技术揭秘:java调试工具实现原理与使用技巧

2. Connector创建好之后launch或者attach到target vm。

底层技术揭秘:java调试工具实现原理与使用技巧

3. 设置event,例如ClassPrepareEvent、 Break pointRequest等:

例如设置断点可以创建BreakpointRequest

底层技术揭秘:java调试工具实现原理与使用技巧

底层技术揭秘:java调试工具实现原理与使用技巧

4. 断点触发之后,需要处理BreakpointEvent,例如打印所有变量

底层技术揭秘:java调试工具实现原理与使用技巧

5. 综合起来

底层技术揭秘:java调试工具实现原理与使用技巧

这个例子来自:www.baeldung.com/java-debug-interface

7. 基于JDI开发debugger的示例

JDK的一个子目录(JDK demo包)即 $JDK/demo/jpda 中,有3个JDI接口的使用示例(源码和文档)。

  1. trace:显示程序执行轨迹。
  2. jdb:JDK中自带的命令行调试工具。
  3. javadt:一个简单的GUI调试工具。


希望这篇文章能对您有帮助!如果您对互联网、前/后/客户端、架构/分布式/高可用/高并发/高实时、电商、Redis、MySQL、Zookeeper、Spring、Android、浏览器插件、Java、C/C++、Linux、个性化推荐、社区发现、机器学习、数据挖掘等感兴趣,欢迎关注。

文章来源:智云一二三科技

文章标题:底层技术揭秘:java调试工具实现原理与使用技巧

文章地址:https://www.zhihuclub.com/176057.shtml

关于作者: 智云科技

热门文章

网站地图