在预发环境下进行debug时,时常因为工具和环境的限制,导致debug体验非常差,那么有什么方法能够简化我们进行debug的体验吗?JDB就是一种。
JDB是 The Java Debugger 的简称,它可以用来debug一个Java程序,同时它是 JPDA 的一个参考实现,只是这个实现是基于命令行的。提到命令行,有些同学心里就犯嘀咕了,离开了GUI,能debug吗?当然可以,只要熟记几个命令,在受限环境下debug不是不可能。
使用JDB的目的是,更细节的诊断和操控代码,如果只是观察值,可以使用arthas之类的工具
JPDA
JPDA将调试过程分为两部分:被调试的程序(被调试者-debuggee)和JDI(调试者-debugger)。JDI一般为一个调试应用程序的用户接口(或Java IDE的一部分),本文中就是JDB。被调试的应用程序在后端运行,而JDI在前端运行。在前端与后端之间有一个通信通道运行JDWP(Java Debug Wire Protocol)协议,因此,被调试程序与调试器可以位于同一个系统内,也可位于不同的系统中。
jpda.png 从开发者的角度,一个调试应用程序可进入任何JPDA层面。只要JDI基于JDWP,就可以debug任何厂商实现的JVM了,比如:j9等。
JDWP 有两种基本的包(packet)类型:命令包(command packet)和回复包(reply packet)。
在JDB里的例子中,我们使用JDB通过socket向本地的JVM发送JDWP请求。
Debugger 和 target Java 虚拟机都有可能发送 command packet。Debugger 通过发送 command packet 获取 target Java 虚拟机的信息以及控制程序的执行。Target Java 虚拟机通过发送 command packet 通知 debugger 某些事件的发生,如到达断点或是产生异常。
Reply packet 是用来回复 command packet 该命令是否执行成功,如果成功 reply packet 还有可能包含 command packet 请求的数据,比如当前的线程信息或者变量的值。从 target Java 虚拟机发送的事件消息是不需要回复的。
环境
一般来说需要准备一个应用、代码包括数据库等,这样真实的案例才容易有感觉。这里笔者准备了一个简单的项目:https://github.com/weipeng2k/spring-guide
,其中:spring-guide-spring-boot项目,可以尝试构建。
在11.239.175.138,用docker拉起了一个mysql实例,具体的jdbc-url为:jdbc:mysql://11.239.175.138:23306/book,这个在应用中的application.properties中已经配置了。
应用实际就提供了一个rest接口,访问:localhost:8080/api/author/1 时,会返回作者的信息。
源码
AuthorApi是控制器,代码和准备debug的点:
code-api.png 可以看到逻辑非常简单,就是根据Id查询作者信息,准备debug的位置在24行。
AuthorQueryServiceImpl是服务的实现,代码和准备debug的点:
服务实现就是查询DAO后,将结果返回,如果找得到则返回值,否则返回null,我们会在26行进行debug。
示例
首先启动应用,如果要进行debug,一般都会在应用上配置对应的调试参数,包括调试的端口。
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar ~/spring-guide-spring-boot/target/spring-guide-spring-boot-1.0.0-SNAPSHOT.jar
本例中是将调试端口开在5005。
接下来运行jdb,连接到调试端口上进行调试。
点击观看,操作流程。
asciicast命令 | 说明 |
---|---|
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=5005 | 使用jdb连接本地的调试端口,线上可以在/opt/taobao/java对应的JavaHome下找到jdb命令 |
stop at com.murdock.books.spring.guide.springboot.service.impl.AuthorQueryServiceImpl:26 | 断点打在com.murdock.books.spring.guide.springboot.service.impl.AuthorQueryServiceImpl的第26行 |
clear | 列出所有断点 |
clear com.murdock.books.spring.guide.springboot.service.impl.AuthorQueryServiceImpl:26 | 清除对应的断点 |
locals | 列出当前的变量,包括本地变量,参数变量等 |
print x | 打印输出变量x |
dump x | dump对应的变量x |
wherei | 当前debug所在的线程堆栈,输出当前的堆栈 |
next | 下一步,代码向下执行 |
cont | 代码执行放过,继续执行,会停留在下一个断点处 |
网友评论