本期将为大家演示如何使用JDB命令行工具调试Java应用,尽管本文并不会涉及Android的相关内容,但却是理解本系列下一期“寻找可调试的安卓应用”前提。
什么是JDB?
JDB是一个简单的Java命令行调试器,包含在JDK中。
我们在本文中将会使用一台Ubuntu主机,我们可以在/usr/bin中找到JDB:
#cd /usr/bin
#ls | grep jdb
提示:如果你使用的是Windows,可以在Java目录下的bin目录中找到JDB,本文的示例主要在Ubuntu下完成,但即使在windows中相关的技术也基本是相同的。
简介
本文将会接合一个实例来理解如何用JDB命令调试Java程序,而不是直接去看JDB的用法。
以下是本文用作示例的代码:
文件名:Debug.java
编译生成的Class文件:Debug.class
这段代码片段中,Debug类的main方法调用了该类中的其他两个方法,编译后执行,会产生以下输出,如图:
我们使用了-g选项来编译程序,编译器会在类文件中生成一些调试信息。
运行JDB
要调试Java程序,我们需要一条JDB到JVM的通信信道,因为我们的Java程序实际上是运行在JVM(java虚拟机)中的
如下所示,有多种连接JDB和JVM的方法。
方法1
使用这个方法,我们直接使用JDB来加载类文件,JDB会自动创建一个JAVA虚拟机,并建立连接。
图中的Debug代表编译后生成的类文件
方法2
使用这种方法,我们先使用以下命令启动一个Java虚拟机,Java虚拟机会监听54321端口。
java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=54321 Debug
然后使用如下命令启动JDB连接到JVM。
jdb -attach 54321
这种方法也可以用于远程调试,在下一期中,我们会使用这种方法来远程调试Android应用。本文中会使用第一种方法。
开始调试
我们现在开始用第一种方法来调试示例程序,但我们需要执行一条run命令来让JDB启动Java虚拟机,如下图:
图中显示,启动了Java虚拟机后,程序立刻执行完成并退出了。
为了中断程序执行以便手工单步调试,我们需要在程序运行之前设置断点。
我们可以用”stop in”命令在方法开始的地方设置断点,如下图:
值得注意的是,设置断点的时候,除了指定类和方法名之外,还要指定参数类型。
我们已经在Debug类中的main方法上设置了断点。
现在我们就能运行程序来触发断点,这里一样使用之前提到的run命令。
触发断点后,JDB会自动显示将要执行的下一行代码:
System.out.println(“We are in main method”);
可以使用“list“命令来查看当前的上下文代码
使用“clear“命令查看设置的所有断点:
如图,clear命令显示出来我们设置断点及位置
使用”threadgroups”命令查看所有的线程组。
如图,当前有两个线程组:“system“和”main“
使用”threads”查看所有线程:
如上图,我们当前的system线程组中有三个线程,而main线程组中有一个线程,这就是我们要调试的。
使用”classes”命令查看当前Java虚拟机所加载的类的信息:
上图中显示了当前Java虚拟机所加载的类(为节省空间,截短了输出)
要查看特定类的更加详细的信息,可以使用以下命令:
class <classname>
下图显示了Debug类的详细信息:
同样,我们也能查看其它类的详细信息,例如下图就显示了 java.io.DatqaInputStream类的详细信息:
使用”methods < classname > ”命令查看所加载的方法:
以上介绍了以下常用的重要命令,现在我们将深入程序的执行流程,看怎样用JDB来帮助我们调试程序。
我们可以使用”next”命令执行下一行代码:
执行完当前代码后,JDB会自动显示下一行代码:调用test方法。这里,我们再执行”next”命令后,会执行完test方法并中断到下一行代码:passCheck:
现在,如果我们想进入passCheck方法进行调试,就应该使用”step”命令,而不再是”next”。我重启了程序,在test方法处输入了step命令:
现在,输入”next”命令继续运行下一条代码:
现在,如果我们因为某些原因希望直接离开该方法,而不是运行余下的代码,我们可以使用”step up”命令。
当前代码已经离开test方法,回到main方法,等待执行下一条。
下面几行会毕竟有趣,我们将会学到如何查看变量中储存的数据,在此之前,我们先介绍一天更有趣的命令:“where”。
“where”命令会打印显示当前的调用栈。我们先在main方法中运行该命令,然后在另一个方法中运行同样的命令:
如图,当前程序正在main方法中,现在使用”step”命令进入方法,并检查调用栈:
如上图,程序当前正在”Debug.passCheck”中运行,而”Debug.passCheck”又是被“Debug.main”调用的假如我现在对passCheck这个方法比较干兴趣,想看看该方法的局部变量中有不有一些敏感信息,我们可以使用”locals”命令查看所有的局部变量。(如果程序没有使用-g选项编译,该命令无效).
如图显示,该方法接收了一个main方法传入的密码变量,因为参数还没有被赋值给局部变量,所有上图只显示了参数而没有局部变量,我们先执行下一行代码再查看局部变量”password”。
可以使用”print”命令打印出指定变量的内容:
总结
本文简介介绍了JDB,以及几个常用的JDB命令的用法。
相关链接
http://docs.oracle.com/javase/7/docs/technotes/tools/windows/jdb.html