本章说明如何使用 dbx 来调试使用 JavaTM 代码和 C JNI(Java Native Interface,Java 本地接口)代码或 C++ JNI 代码混合编写的应用程序。
本章由以下部分组成:
可以使用 Sun Studio dbx 调试运行在 SolarisTM OS 和 Linux OS 下的混合代码(Java 代码和 C 代码或 C++ 代码)。
使用 dbx 可以调试几种类型的 Java 应用程序(请参见开始调试 Java 应用程序)。在调试本地代码和 Java 代码时,大多数 dbx 命令的使用方式都类似。
dbx 无法像对待本机代码那样通过核心转储文件来指明 Java 应用程序的状态。
如果 Java 应用程序由于某种原因挂起,dbx 无法指示该应用程序的状态,且 dbx 也不能进行过程调用。
修复并继续以及运行时检查不适用于 Java 应用程序。
以下环境变量专门用于使用 dbx 调试 Java 应用程序。您可以在启动 dbx 之前在 shell 提示符下设置 JAVASRCPATH、CLASSPATHX 和 jvm_invocation 环境变量,也可以通过 dbx 命令行设置这些环境变量。jdbx_mode 环境变量的设置会在调试应用程序的过程中发生更改。可以使用 jon 命令(请参见jon 命令)和 joff 命令(请参见joff 命令)更改其设置。
jdbx_mode |
jdbx_mode 环境变量的设置如下:java、jni 或 native。有关 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 软件的启动。 |
文件名以 .class 结尾的文件
文件名以 .jar 结尾的文件
使用包装器启动的 Java 应用程序
在调试模式下启动并连接了 dbx 的运行的 Java 应用程序
使用 JNI_CreateJavaVM 接口嵌入 Java 应用程序的 C 应用程序或 C++ 应用程序
在上述所有情况下,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 |
Java 应用程序可以使用 JAR(Java 归档)文件打包。可以使用 dbx 调试 JAR 文件,如下例所示。
(dbx) debug myjar.jar |
开始调试文件名以 .jar 结尾的文件时,dbx 会使用在此 JAR 文件的清单中指定的 Main_Class 属性来确定主类。(主类是 JAR 文件内作为应用程序入口点的类。)如果使用全路径名或相对路径名来指定 JAR 文件,dbx 会使用目录名,并在 Main-Class 属性中将其作为类路径的前缀。
如果调试无 Main-Class 属性的 JAR 文件,可以使用 JAR URL 语法 jar:<url>!/{entry}(在 Java 2 平台标准版的 JarURLConnection 类中指定)来指定主类的名称,如以下示例所示。
(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 都会执行以下操作:
将 ! 字符后指定的类路径作为主类(例如,/myclass.class 或 /x/y/z.class)来处理
将 JAR 文件名 ./myjar.jar、/a/b/c/d/e.jar 或 /a/b/c/d.jar 添加到类路径中
开始调试主类
如果使用 jvm_invocation 环境变量指定了 JVM 软件的定制启动(请参见定制 JVM 软件的启动),则不会将 JAR 文件名自动添加到类路径中。在这种情况下,必须在开始调试时将 JAR 文件名添加到类路径中。
Java 应用程序通常有一个用于设置环境变量的包装器。如果 Java 应用程序有包装器,需要通过设置 jvm_invocation 环境变量将要使用包装器脚本这一情况告知 dbx(请参见定制 JVM 软件的启动)。
可以将 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 中:
如果在运行 Solaris OS 的系统上使用 32 位版本的 JVM 软件,则添加 /installation_directory/SUNWspro/lib/libdbx_agent.so。
如果在运行 Solaris OS 且基于 SPARC 的系统上使用 64 位版本的 JVM 软件,则将 /installation_directory/SUNWspro/lib/v9/libdbx_agent.so 添加至 LD_LIBRARY_PATH。
如果在运行 Linux OS 且基于 x64 的系统上使用 64 位版本的 JVM 软件,则将 /installation_directory/sunstudio12/lib/amd64/libdbx_agent.so 添加至 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 |
可以使用 JNI_CreateJavaVM 接口调试内嵌 Java 应用程序的 C 应用程序或 C++ 应用程序。C 应用程序或 C++ 应用程序必须通过为 JVM 软件指定以下选项,才能启动 Java 应用程序:
-Xdebug -Xnoagent -Xrundbx_agent |
为了使 JVM 软件能够找到 libdbx_agent.so,需要在运行 Java 应用程序前将适当路径添加到 LD_LIBRARY_PATH 中:
如果在运行 Solaris OS 的系统上使用 32 位版本的 JVM 软件,则将 /installation_directory/SUNWspro/lib/libdbx_agent.so 添加至 LD_LIBRARY_PATH。
如果在运行 Solaris OS 且基于 SPARC 的系统上使用 64 位版本的 JVM 软件,则将 /installation_directory/SUNWspro/lib/v9/libdbx_agent.so 添加至 LD_LIBRARY_PATH。
如果在运行 Linux OS 且基于 x64 的系统上使用 64 位版本的 JVM 软件,则将 /installation_directory/sunstudio12/lib/amd64/libdbx_agent.so 添加至 LD_LIBRARY_PATH。
installation_directory 是 Sun Studio 软件的安装位置。
在 Java 模式下使用 run 命令时,会将提供的参数传递给应用程序,而非 JVM 软件。要将参数传递给 JVM 软件,请参见定制 JVM 软件的启动。
有时 Java 源文件与 .class 或 .jar 文件不在同一目录中。可以使用 $JAVASRCPATH 环境变量指定 dbx 查找 Java 源文件的目录。例如,JAVASRCPATH=.:/mydir/mysrc:/mydir/mylibsrc:/mydir/myutils 会使 dbx 在与正被调试的类文件对应的源文件的列出目录中查找。
在以下情况下,dbx 可能找不到 C 源文件或 C++ 源文件:
如果源文件不在编译时所在的位置
如果编译源文件的系统与运行 dbx 的系统不同,而且编译目录的路径名不同
在此类情况下,请使用 pathmap 命令(请参见pathmap 命令)将一个路径名映射到另一个路径名,以便 dbx 可以找到您的文件。
应用程序可以有从可能不属于常规类路径的位置装入类文件的定制类加载器。在这种情况下,dbx 找不到类文件。使用环境变量 CLASSPATHX 可以为 dbx 指定由定制类加载器装入的类文件的路径。例如,CLASSPATHX=.:/myloader/myclass:/mydir/mycustom 会使 dbx 在尝试查找类文件时到列出的目录中查找。
与本地应用程序不同,Java 应用程序不包含便于访问的名称索引。因此不能简单地键入:
(dbx) stop in myMethod |
而是需要使用方法的完整路径:
(dbx) stop in com.any.library.MyClass.myMethod |
使用 MyClass 的某个方法停止时是一种例外情况,在这种情况下,使用 myMethod 就应足可以了。
避免包含该方法的完整路径的一种方式是,使用 stop inmethod:
(dbx) stop inmethod myMethod |
但是这样做可能会导致在多个方法名称 myMethod 中停止。
包含 JNI C 或 C++ 代码的共享库由 JVM 动态装入,在这些库中设置断点需要一些其他步骤。有关更多信息,请参见在动态装入的库中设置断点。
可能需要通过 dbx 定制 JVM 软件的启动,以便执行以下操作:
为 JVM 软件指定路径名(请参见指定 JVM 软件的路径名)
将一些运行参数传递到 JVM 软件(请参见将运行参数传递给 JVM 软件)
指定定制包装器而不是缺省的 Java 包装器,供运行 Java 应用程序时使用(请参见指定 Java 应用程序的自定义包装器)
指定 64 位 JVM 软件(请参见指定 64 位 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 软件的路径名,dbx 将在您的路径中启动 JVM 软件。
要指定 JVM 软件的路径名,请将环境变量 jvm_invocation 设置为相应的路径名,如下例所示。
jvm_invocation="/myjava/java -Xdebug" |
此设置会使 dbx 按以下方式启动 JVM 软件:
/myjava/java -Djava.compiler=NONE -Xdebug -Xnoagent -Xrundbx_agent:sync |
要将运行参数传递给 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 应用程序可以使用定制包装器来启动。如果应用程序使用定制包装器,便可以使用环境变量 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 并采用以下两种方法之一,使用此类脚本来调试包装器:
可以对脚本进行修改,即将变量 jvm_invocation 的定义添加到脚本中并启动 dbx,以从包装器脚本内部启动 dbx:
#!/bin/sh CLASSPATH=/mydir/myclass:/mydir/myjar.jar; export CLASSPATH ABC=/mydir/abc; export ABC jvm_invocation="java -Xdebug <options>"; export jvm_invocation dbx myclass.class |
修改后,便可运行脚本来启动调试会话。
可以略微修改脚本来接受某些命令行选项,如下所示:
#!/bin/sh CLASSPATH=/mydir/myclass:/mydir/myjar.jar; export CLASSPATH ABC=/mydir/abc; export ABC JAVA_OPTIONS="$1 <options>" java $JAVA_OPTIONS $2 |
修改后,便可设置环境变量 jvm_invocation,并按以下方式启动 dbx:
% jvm_invocation="xyz -Xdebug"; export jvm_invocation % dbx myclass.class |
如果希望 dbx 启动 64 位 JVM 软件来调试需要 64 位对象库的应用程序,请在设置环境变量 jvm_invocation 时包含 -d64 选项:
jvm_invocation="/myjava/java -Xdebug -d64" |
调试 Java 应用程序时,dbx 处于以下三种模式之一:
Java 模式
JNI 模式
本地模式
当 dbx 处于 Java 模式或 JNI(Java Native Interface,Java 本地接口)模式下时,可以检查 Java 应用程序(包括 JNI 代码)的状态,并控制代码的执行。当 dbx 处于本地模式下时,可以检查 C 或 C++ JNI 代码的状态。当前模式(java、jni、native)存储在环境变量 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 模式。
dbx 不会自动切换到本地模式。可以使用 joff 命令显式从 Java 模式或 JNI 模式切换到本地模式,也可以使用 jon 命令从本地模式切换到 Java 模式。
如果中断执行 Java 应用程序(例如,使用 Ctrl-C),dbx 会尝试通过将应用程序置于安全状态并挂起所有线程将模式自动设置为 Java/JNI 模式。
如果 dbx 无法挂起应用程序并切换到 Java/JNI 模式,dbx 便会切换到本地模式。然后,您可以使用 jon 命令切换到 Java 模式来检查程序的状态。
使用 dbx 调试由 Java 代码和本地代码组成的混合型代码时,dbx 命令分为以下几类:
第一类命令:接受相同的参数,而且在 Java 模式或 JNI 模式下的运行方式与本地模式下相同(请参见在 Java 模式和本地模式下具有完全相同语法和功能的命令)。
第二类命令:具有仅在 Java 模式或 JNI 模式下有效的参数,或者仅在本地模式下有效的参数(请参见在 Java 模式下有不同语法的命令))。
第三类命令:仅在 Java 模式或 JNI 模式下有效(请参见只在 Java 模式下有效的命令)。
未包括在上述任一类别的命令只在本地模式下有效。
在大多数 dbx 命令中使用的 Java 表达式计算器支持以下构造:
所有文字
所有名称和字段访问
this 和 super
数组访问
类型转换
条件二进制运算
方法调用
其他一元/二进制运算
对变量或字段赋值
instanceof 操作符
数组长度操作符
Java 表达式计算器不支持以下构造:
限定的 this,例如 <ClassName>.this
类实例创建表达式
数组创建表达式
字符串并置操作符
条件操作符 ?:
复合赋值操作符,例如 x += 3
一种特别有用的检查 Java 应用程序状态的方式是在 IDE 或 dbxtool 中使用监视功能。
建议不要依赖表达式中作用不限于检查数据的精确值语义。
只有在 JVM 软件启动后,有关 Java 应用程序的许多信息才可正常使用,并且执行完 Java 应用程序后,这些信息将不再使用。但是,使用 dbx 调试 Java 应用程序时,dbx 会在启动 JVM 软件前从属于系统类路径和用户类路径的类文件和 JAR 文件中收集其需要的一些信息。这样 dbx 便可在您运行应用程序前更好地对断点进行错误检查。
有些 Java 类及其属性可能无法通过类路径进行访问。dbx 可以检查并逐步执行这些类,这些类被装入后,表达式解析器便可以访问它们。但它收集的信息是临时性的,JVM 软件终止后便不再可用。
dbx 调试 Java 应用程序所需的某些信息在任何地方均无记录,因此,dbx 会在调试代码期间浏览 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 |
显示当前源代码行 |
以下 dbx 命令在进行 Java 调试时所用的语法与进行本地代码调试时所用的语法不同,而且在 Java 模式下的运行方式也与本地模式下的运行方式不同。
命令 |
本地模式功能 |
Java 模式功能 |
---|---|---|
assign |
为程序变量赋新值 |
为局部变量或参数赋新值 |
call |
调用过程 |
调用方法 |
dbx |
启动 dbx |
启动 dbx |
debug |
装入指定应用程序,然后开始调试该应用程序 |
装入指定 Java 应用程序,接着检查类文件是否存在,然后开始调试应用程序 |
detach |
使目标进程脱离 dbx 的控制 |
使目标进程脱离 dbx 的控制 |
display |
在每个停止点对表达式求值并打印。 |
在每个停止点对表达式、局部变量或参数求值并打印 |
files |
列出与某个正规表达式匹配的文件名 |
列出 dbx 已知的所有 Java 源文件 |
func |
列出或更改当前函数 |
列出或更改当前方法 |
next |
单步执行一个源代码行(步过调用) |
单步执行一个源代码行(步过调用) |
|
打印表达式的值 |
打印表达式、局部变量或参数的值。 |
run |
带参数运行程序 |
带参数运行程序 |
step |
单步执行一个源代码行或语句(正在步入调用) |
单步执行一个源代码行或语句(正在步入调用) |
stop |
设置源码级断点 |
设置源码级断点 |
thread |
列出或更改当前线程 |
列出或更改当前线程 |
threads |
列出所有线程 |
列出所有线程 |
trace |
显示执行的源代码行、函数调用或变量更改 |
显示执行的源代码行、函数调用或变量更改 |
undisplay |
撤消 display 命令 |
撤消 display 命令 |
whatis |
打印表达式类型或类型声明 |
打印标识符声明 |
when |
指定事件发生时执行命令 |
指定事件发生时执行命令 |
where |
打印调用栈 |
打印调用栈 |
以下 dbx 命令仅在 Java 模式或 JNI 模式下有效。
命令 |
功能 |
---|---|
java |
当 dbx 处于 JNI 模式下时,用于指示将执行的是 Java 版本的指定命令 |
javaclasses |
发出该命令后,打印 dbx 已知的所有 Java 类的名称 |
joff |
将 dbx 从 Java 模式或 JNI 模式切换到本地模式 |
jon |
将 dbx 从本地模式切换到 Java 模式 |
jpkgs |
发出该命令后,打印 dbx 已知的所有 Java 程序包的名称 |
native |
当 dbx 处于 Java 模式下时,用于指示将执行的是本地版本的指定命令 |