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