Sun Studio 12:使用 dbx 调试程序

第 17 章 使用 dbx 调试 Java 应用程序

本章说明如何使用 dbx 来调试由 JavaTM 代码和 C JNI(Java Native Interface,Java 本地接口)代码或 C++ JNI 代码混合编写的应用程序。

本章由以下部分组成:

使用 dbx 调试 Java 代码

可以使用 Sun Studio dbx 调试运行在 SolarisTM OS 和 Linux OS 下的混合代码(Java 代码和 C 代码或 C++ 代码)。

使用 dbx 调试 Java 代码的功能

使用 dbx 可以调试几种类型的 Java 应用程序(请参见开始调试 Java 应用程序)。在调试本地代码和 Java 代码时,大多数 dbx 命令的使用方式都类似。

使用 dbx 调试 Java 代码的限制

dbx 在调试 Java 代码时有以下限制:

Java 调试的环境变量

以下环境变量专门用于使用 dbx 调试 Java 应用程序。在启动 dbx 之前,可以在 shell 提示符下设置环境变量 JAVASRCPATHCLASSPATHXjvm_invocation。环境变量 jdbx_mode 的设置会在调试应用程序的过程中发生变化。可以使用 jon 命令(请参见jon 命令)和 joff 命令(请参见joff 命令)更改其设置。

jdbx_mode

环境变量 jdbx_mode 的设置如下:java jninative。有关 Java、JNI 和本地模式的说明以及模式变化的方式和时机,请参见调试 Java 代码的 dbx 模式。缺省值:java.

JAVASRCPATH

可以使用 JAVASRCPATH 环境变量指定 dbx 查找 Java 源文件的目录。Java 源文件与 .class.jar 文件不在同一目录中时,此变量很有用。有关更多信息,请参见指定 Java 源文件的位置

CLASSPATHX

使用 CLASSPATHX 环境变量可以为 dbx 指定由定制类加载器装入的 Java 类文件的路径。有关更多信息,请参见为使用自定义类加载器的类文件指定路径

jvm_invocation

使用 jvm_invocation 环境变量可以定制 JVMTM 软件的启动方式。(术语“Java 虚拟机”和 "JVM" 表示用于 Java 平台的虚拟机。)有关更多信息,请参见定制 JVM 软件的启动

开始调试 Java 应用程序

可以使用 dbx 调试以下类型的 Java 应用程序:

在上述所有情况下,dbx 均可识别其正在调试的是 Java 应用程序。

调试类文件

可以使用 dbx 调试文件扩展名为 .class 的文件,如下例所示。


(dbx) debug myclass.class

如果定义应用程序的类在包中定义,便需要如同在 JVM 软件上运行应用程序那样加入包的路径,如下例所示。


(dbx) debug java.pkg.Toy.class

也可以使用类文件的全路径名。dbx 通过在 .class 文件中进行查找来自动确定类路径的软件包部分,然后将全路径名的剩余部分添加到类路径中。例如,假定有以下路径名,dbx 会确定 pkg/Toy.class 是主类名,然后将 /home/user/java 添加到类路径中。


(dbx) debug /home/user/java/pkg/Toy.class

调试 JAR 文件

Java 应用程序可以使用 JAR(Java 归档)文件打包。可以使用 dbx 调试 JAR 文件,如下例所示。


(dbx) debug myjar.jar

开始调试文件名以 .jar 结尾的文件时,dbx 会使用在此 JAR 文件的清单中指定的 Main_Class 属性来确定主类。(主类是 JAR 文件内作为应用程序入口点的类。)如果使用全路径名或相对路径名来指定 JAR 文件,dbx 会使用目录名,并在 Main-Class 属性中将其作为类路径的前缀。

如果调试无 Main-Class 属性的 JAR 文件,可以使用 Java 2 平台标准版的 JarURLConnection 类中指定的 JAR URL 语法 jar:<url>!/{entry} 来指定主类的名称,如下例所示。


(dbx) debug jar:myjar.jar!/myclass.class
(dbx) debug jar:/a/b/c/d/e.jar!/x/y/z.class
(dbx) debug jar:file:/a/b/c/d.jar!/myclass.class

对于这些示例中的每一个示例,dbx 都会执行以下操作:

调试有包装器的 Java 应用程序

Java 应用程序通常有一个用于设置环境变量的包装器。如果 Java 应用程序有包装器,需要通过设置 jvm_invocation 环境变量将要使用包装器脚本这一情况告知 dbx(请参见定制 JVM 软件的启动)。

dbx 连接到正在运行的 Java 应用程序

可以将 dbx 连接到正在运行的 Java 应用程序,前提是在启动该应用程序时指定了下例所示的选项。启动应用程序后,可以将 dbx 命令(请参见dbx 命令) 与正在运行的 Java 进程的进程 ID 结合使用以开始调试。


$ java -Djava.compiler=NONE -Xdebug -Xnoagent -Xrundbx_agent myclass.class
$ dbx - 2345

为了使 JVM 软件能够找到 libdbx_agent.so,需要在运行 Java 应用程序前将适当路径添加到 LD_LIBRARY_PATH 中:

installation_directory 是 Sun Studio 软件的安装位置。

dbx 连接到正在运行的应用程序时,dbx 会以 Java 模式开始调试应用程序。

如果 Java 应用程序需要 64 位目标库,请在启动应用程序时包括 -d64 选项。之后,将 dbx 连接到应用程序时,dbx 将使用运行该应用程序的 64 位 JVM 软件。


$ java -Djava.compiler=NONE -Xdebug -Xnoagent -Xrundbx_agent -d64 myclass.class
$ dbx - 2345

调试内嵌 Java 应用程序的 C 应用程序或 C++ 应用程序

可以使用 JNI_CreateJavaVM 接口调试内嵌 Java 应用程序的 C 应用程序或 C++ 应用程序。C 应用程序或 C++ 应用程序必须通过为 JVM 软件指定以下选项,才能启动 Java 应用程序:


-Xdebug -Xnoagent -Xrundbx_agent

为了使 JVM 软件能够找到 libdbx_agent.so,需要在运行 Java 应用程序前将适当路径添加到 LD_LIBRARY_PATH 中:

installation_directory 是 Sun Studio 软件的安装位置。

将参数传递给 JVM 软件

在 Java 模式下使用 run 命令时,会将提供的参数传递给应用程序,而非 JVM 软件。要将参数传递给 JVM 软件,请参见定制 JVM 软件的启动

指定 Java 源文件的位置

有时 Java 源文件与 .class.jar 文件不在同一目录中。可以使用 $JAVASRCPATH 环境变量指定 dbx 查找 Java 源文件的目录。例如,JAVASRCPATH=.:/mydir/mysrc:/mydir/mylibsrc:/mydir/myutils 会使 dbx 在与正被调试的类文件对应的源文件的列出目录中查找。

指定 C 源文件或 C++ 源文件的位置

在以下情况下,dbx 可能找不到 C 源文件或 C++ 源文件:

在此类情况下,请使用 pathmap 命令(请参见pathmap 命令)将一个路径名映射到另一个路径名,以便 dbx 可以找到您的文件。

为使用自定义类加载器的类文件指定路径

应用程序可以有从可能不属于常规类路径的位置装入类文件的定制类加载器。在这种情况下,dbx 找不到类文件。使用环境变量 CLASSPATHX 可以为 dbx 指定由定制类加载器装入的类文件的路径。例如,CLASSPATHX=.:/myloader/myclass:/mydir/mycustom 会使 dbx 在尝试查找类文件时到列出的目录中查找。

在 JVM 软件尚未装入的代码上设置断点

要在 JVM 软件尚未装入的类文件中的 Java 方法上设置停止断点,请在 stop in 命令中使用类的全名,或在 stop inmethod 命令中使用类名。参见下例。


(dbx) stop in Java.Pkg.Toy.myclass.class.mymethod
(dbx) stop inmethod myclass.class.mymethod

要在 JVM 软件尚未装入的共享库中的 C 函数或 C++ 函数上设置停止断点,请在设置断点前预装共享库的符号表。例如,如果有一个名为 mylibrary.so 的库,其中包含一个名为 myfunc 的函数,便可按以下方式预装入库并在函数上设置断点:


(dbx) loadobject -load fullpathto/mylibrary.so
(dbx> stop in myfunc

还可以在开始使用 dbx 调试应用程序之前运行一次应用程序,以装入所有动态装入的共享对象的符号表。

定制 JVM 软件的启动

可能需要通过 dbx 定制 JVM 软件的启动,以便执行以下操作:

可以使用环境变量 jvm_invocation 定制 JVM 软件的启动。缺省情况下,环境变量 jvm_invocation 未定义时,dbx 将按以下方式启动 JVM 软件


java -Xdebug -Xnoagent -Xrundbx_agent:syncpid
:

定义环境变量 jvm_invocation 后,dbx 会使用该变量的值来启动 JVM 软件。

必须在环境变量 jvm_invocation 的定义中包括 -Xdebug 选项。dbx 会将 -Xdebug 扩展为内部选项 -Xdebug- Xnoagent -Xrundbxagent::sync

如果不在定义中包括 -Xdebug 选项,如下例所示,dbx 会显示错误消息。


jvm_invocation="/set/java/javasoft/sparc-S2/jdk1.2/bin/java"

dbx: Value of `$jvm_invocation’ must include an option to invoke the VM in debug mode

指定 JVM 软件的路径名

缺省情况下,如果不指定 JVM 软件的路径名,dbx 将在您的路径中启动 JVM 软件。

要指定 JVM 软件的路径名,请将环境变量 jvm_invocation 设置为相应的路径名,如下例所示。


jvm_invocation="/myjava/java -Xdebug"

此设置会使 dbx 按以下方式启动 JVM 软件:


/myjava/java -Djava.compiler=NONE -Xdebug -Xnoagent -Xrundbx_agent:sync

将运行参数传递给 JVM 软件

要将运行参数传递给 JVM 软件,请对环境变量 jvm_invocation 进行设置,以使用那些参数启动 JVM 软件,如下例所示。


jvm_invocation="java -Xdebug -Xms512 -Xmx1024 -Xcheck:jni"

这将使 dbx 按以下方式启动 JVM 软件:


java -Djava.compiler=NONE -Xdebug -Xnoagent -Xrundbx_agent:sync= -Xms512 -Xmx1024 -Xcheck:jni

指定 Java 应用程序的自定义包装器

Java 应用程序可以使用定制包装器来启动。如果应用程序使用定制包装器,便可以使用环境变量 jvm_invocation 来指定要使用的包装器,如下例所示。


jvm_invocation="/export/siva-a/forte4j/bin/forte4j.sh -J-Xdebug"

这将使 dbx 按以下方式启动 JVM 软件:


/export/siva-a/forte4j/bin/forte4j.sh - -J-Xdebug -J-Xnoagent -J-Xrundbxagent:sync=process_id

使用接受命令行选项的自定义包装器

以下包装器脚本 (xyz) 会设置几个环境变量并接受命令行选项:


#!/bin/sh
CPATH=/mydir/myclass:/mydir/myjar.jar; export CPATH
JARGS="-verbose:gc -verbose:jni -DXYZ=/mydir/xyz"
ARGS=
while [ $# -gt 0 ] ; do
    case "$1" in
        -userdir) shift; if [ $# -gt 0 ]
; then userdir=$1; fi;;
        -J*) jopt=`expr $1 : ’-J<.*>’`
; JARGS="$JARGS ’$jopt’";;
        *) ARGS="$ARGS ’$1’" ;;
    esac
    shift
done
java $JARGS -cp $CPATH $ARGS

此脚本接受 JVM 软件和用户应用程序的某些命令行选项。对于这种形式的包装器脚本,可设置环境变量 jvm_invocation 并按以下方式启动 dbx


% jvm_invocation="xyz -J-Xdebug -Jany other java options"
% dbx myclass.class -Dide=visual

使用不接受命令行选项的自定义包装器

以下包装器脚本 (xyz) 会设置几个环境变量并启动 JVM 软件,但不接受任何命令行选项或类名:


#!/bin/sh
CLASSPATH=/mydir/myclass:/mydir/myjar.jar; export CLASSPATH
ABC=/mydir/abc; export ABC
java <options> myclass

可以通过 dbx 并采用以下两种方法之一,使用此类脚本来调试包装器:

指定 64 位 JVM 软件

如果希望 dbx 启动 64 位 JVM 软件来调试需要 64 位目标库的应用程序,请在设置环境变量 jvm_invocation 时包括 -d64 选项:


jvm_invocation="/myjava/java -Xdebug -d64"

调试 Java 代码的 dbx 模式

调试 Java 应用程序时,dbx 处于以下三种模式之一:

dbx 处于 Java 模式或 JNI(Java Native Interface,Java 本地接口)模式下时,可以检查 Java 应用程序(包括 JNI 代码)的状态,并控制代码的执行。当 dbx 处于本地模式下时,可以检查 C 或 C++ JNI 代码的状态。当前模式(javajninative)存储在环境变量 jdbx_mode 中。

在 Java 模式下,可以使用 Java 语法与 dbx 交互,dbx 会使用 Java 语法显示信息。此模式用于调试纯 Java 代码或使用 Java 代码、C JNI 代码或 C++ JNI 代码混合编写的应用程序中的 Java 代码。

在 JNI 模式下,dbx 命令使用本地语法并会影响本地代码,但命令的输出既显示与 Java 有关的状态,也显示本地状态,所以 JNI 模式是一种“混合”模式。此模式用于调试使用 Java 代码、C JNI 代码或 C++ JNI 代码混合编写的应用程序的本地部分。

在本地模式下,dbx 命令只影响本地程序,所有与 Java 有关的功能都会被禁用。此模式用于调试与 Java 无关的程序。

在执行 Java 应用程序的过程中,dbx 会根据情况自动在 Java 模式和 JNI 模式间切换。例如,遇到 Java 断点时,dbx 会自动切换到 Java 模式,而当您从 Java 代码步入 JNI 代码时,它又会切换到 JNI 模式。

从 Java 或 JNI 模式切换到本地模式

dbx 不会自动切换到本地模式。可以使用 joff 命令显式从 Java 模式或 JNI 模式切换到本地模式,也可以使用 jon 命令从本地模式切换到 Java 模式。

中断执行时切换模式

如果中断执行 Java 应用程序(例如,使用 control-C),dbx 会尝试通过将应用程序置于安全状态并挂起所有线程将模式自动设置为 Java/JNI 模式。

如果 dbx 无法挂起应用程序并切换到 Java/JNI 模式,dbx 便会切换到本地模式。然后,您可以使用 jon 命令切换到 Java 模式来检查程序的状态。

在 Java 模式下使用 dbx 命令

使用 dbx 调试由 Java 代码和本地代码组成的混合型代码时,dbx 命令分为以下几类:

未包括在上述任一类别的命令只在本地模式下有效。

dbx 命令中的 Java 表达式求值

在大多数 dbx 命令中使用的 Java 表达式计算器支持以下构造:

Java 表达式计算器不支持以下构造:

一种特别有用的检查 Java 应用程序状态的方式是在 dbx 调试器中使用显示功能。

建议不要依赖表达式中作用不限于检查数据的精确值语义。

dbx 命令使用的静态和动态信息

只有在 JVM 软件启动后,有关 Java 应用程序的许多信息才可正常使用,并且执行完 Java 应用程序后,这些信息将不再使用。但是,使用 dbx 调试 Java 应用程序时,dbx 会在启动 JVM 软件前从属于系统类路径和用户类路径的类文件和 JAR 文件中收集其需要的一些信息。这样 dbx 便可在您运行应用程序前更好地对断点进行错误检查。

有些 Java 类及其属性可能无法通过类路径进行访问。dbx 可以检查并逐步执行这些类,这些类被装入后,表达式解析器便可以访问它们。但它收集的信息是临时性的,JVM 软件终止后便不再可用。

dbx 调试 Java 应用程序所需的某些信息在任何地方均无记录,因此,dbx 会在调试代码期间浏览 Java 源文件来取得这些信息。

在 Java 模式和本地模式下具有完全相同语法和功能的命令

以下 dbx 命令在 Java 模式下和本地模式下具有相同的语法并执行相同的操作。

命令 

功能 

attach

dbx 连接到正在运行的进程,从而停止执行并将程序置于调试控制之下

cont

使进程继续执行 

dbxenv

列出或设置 dbx 环境变量

delete

删除断点和其他事件 

down

将调用栈下移(远离 main

dump

打印过程或方法的所有局部变量 

file

列出或更改当前文件 

frame

列出或更改当前栈帧号 

handler

修改事件处理程序(断点) 

import

dbx 命令库中导入命令

line

列出或更改当前行号 

list

列出或更改当前行号 

next

单步执行一个源代码行(步过调用) 

pathmap

将一个路径名映射至另一个路径名,以查找源文件等 

proc

显示当前进程的状态 

prog

管理正被调试的程序和它们的属性 

quit

退出 dbx

rerun

不带参数运行程序 

runargs

更改目标进程的参数 

status

列出事件处理程序(断点) 

step up

向上单步执行并步出当前函数或方法 

stepi

单步执行一个机器指令(步入调用) 

up

将调用栈上移(靠近 main

whereami

显示当前源代码行 

在 Java 模式下有不同语法的命令

以下 dbx 命令在进行 Java 调试时所用的语法与进行本地代码调试时所用的语法不同,而且在 Java 模式下的运行方式也与本地模式下的运行方式不同。

命令 

本地模式功能 

Java 模式功能 

assign

为程序变量赋新值 

为局部变量或参数赋新值 

call

调用过程 

调用方法 

dbx

启动 dbx

启动 dbx

debug

装入指定应用程序,然后开始调试该应用程序 

装入指定 Java 应用程序,接着检查类文件是否存在,然后开始调试应用程序 

detach

使目标进程脱离 dbx 的控制

使目标进程脱离 dbx 的控制

display

在每个停止点对表达式求值并打印。 

在每个停止点对表达式、局部变量或参数求值并打印 

files

列出与某个正规表达式匹配的文件名 

列出 dbx 已知的所有 Java 源文件

func

列出或更改当前函数 

列出或更改当前方法 

next

单步执行一个源代码行(步过调用) 

单步执行一个源代码行(步过调用) 

print

打印表达式的值 

打印表达式、局部变量或参数的值。 

run

带参数运行程序 

带参数运行程序 

step

单步执行一个源代码行或语句(正在步入调用) 

单步执行一个源代码行或语句(正在步入调用) 

stop

设置源码级断点 

设置源码级断点 

thread

列出或更改当前线程 

列出或更改当前线程 

threads

列出所有线程 

列出所有线程 

trace

显示执行的源代码行、函数调用或变量更改 

显示执行的源代码行、函数调用或变量更改 

undisplay

撤消 display 命令

撤消 display 命令

whatis

打印表达式类型或类型声明 

打印标识符声明 

when

指定事件发生时执行命令 

指定事件发生时执行命令 

where

打印调用栈 

打印调用栈 

只在 Java 模式下有效的命令

以下 dbx 命令仅在 Java 模式或 JNI 模式下有效。

命令 

功能 

java

dbx 处于 JNI 模式下时,用于指示将执行的是 Java 版本的指定命令

javaclasses

发出该命令后,打印 dbx 已知的所有 Java 类的名称

joff

dbx 从 Java 模式或 JNI 模式切换到本地模式

jon

dbx 从本地模式切换到 Java 模式

jpkgs

发出该命令后,打印 dbx 已知的所有 Java 程序包的名称

native

dbx 处于 Java 模式下时,用于指示将执行的是本地版本的指定命令