本章说明如何启动、执行、保存、恢复和退出 dbx 调试会话。其中包含以下各节:
dbx 的启动方式取决于:调试的对象、所在的位置、dbx 需要执行的任务、您对 dbx 的熟悉程度,以及是否设置了 dbx 环境变量。
启动 dbx 会话的最简单方法是在 shell 提示符下键入 dbx 命令。
$ dbx |
要从 shell 中启动 dbx 并装入要调试的程序,请键入:
$ dbx program_name |
要启动 dbx 并装入 Java 代码和 C JNI 代码或 C++ JNI 代码混编的程序:
$ dbx program_name{.class | .jar} |
Sun Studio 软件包括两个 dbx 二进制文件,一个是只能调试 32 位程序的 32 位 dbx,另一个是可同时调试 32 位程序和 64 位程序的 64 位 dbx。启动 dbx 时,它会决定执行哪一个二进制文件。在 64 位操作系统上,64 位 dbx 是缺省值。要在 64 位 OS 上启动 32 位 dbx,请将 dbx 命令与 -x exec32 选项一起使用(请参见选项)或者设置 _DBX_EXEC_32 环境变量。
在 Linux OS 上,64 位 dbx 无法调试 32 位程序。要在 Linux OS 上调试 32 位程序,必须启动 32 位 dbx。
在 64 位 Linux OS 上使用 32 位 dbx 时,如果结果是将执行 64 位程序,则不要使用 debug 命令或将 follow_fork_mode 环境变量设置为子进程。退出 dbx 并启动 64 位 dbx 以调试 64 位程序。
有关 dbx 命令和启动选项的更多信息,请参见dbx 命令和 dbx(1) 手册页。
如果转储核心的程序与所有共享库动态链接,最好在创建核心转储文件的相同操作环境中调试核心转储文件。dbx 对调试“不匹配的”核心转储文件(例如,运行其他版本或修补程序级别的 Solaris 操作系统的系统上生成的核心转储文件)具有有限的支持。
dbx 无法像对待本机代码那样通过核心转储文件来指明 Java 应用程序的状态。
要调试核心转储文件,键入:
$ dbx program_name core |
如果 dbx 已在运行,也可以使用 debug 命令调试核心转储文件。
(dbx) debug -c core program_name |
可以用 - 替换程序名,dbx 将尝试从核心转储文件中提取程序名。如果在核心转储文件中未提供可执行文件的全路径名,则 dbx 可能找不到可执行文件。如果 dbx 找不到可执行文件,请在指示 dbx 装入核心转储文件时指定二进制文件的完整路径名。
如果核心转储文件不在当前目录下,可指定它的路径名(例如,/tmp/core)。
使用 where 命令(请参见where 命令)确定程序在进行核心转储时的执行位置。
调试核心转储文件时,也可以求变量和表达式的值来查看程序崩溃时的值,但不能求调用函数的表达式的值。无法单步执行或设置断点。
如果装入核心存储文件时存在问题,请检查是否有被截断的核心转储文件。如果在创建核心转储文件时将其最大允许大小设得太低,那么 dbx 将无法读取最终被截断的核心转储文件。在 C shell 中,可使用 limit 命令来设置允许的最大核心转储文件大小(请参见 limit(1) 手册页)。在 Bourne shell 和 Korn shell 中,应使用 ulimit 命令(请参见 ulimit(1) 手册页)。可以在 shell 启动文件中更改核心转储文件大小的限制,重新寻找启动文件,然后重新运行生成该核心转储文件的程序,以生成完整的核心转储文件。
如果核心转储文件不完整并且缺少栈段,则栈跟踪信息不可用。如果缺少运行时链接程序信息,则装入对象列表不可用。在这种情况下,您会收到 librtld_db.so 未初始化的错误消息。如果缺少 LWP 列表,则线程信息、lwp 信息或栈跟踪信息不可用。如果运行 where 命令,将看到一条错误消息,指出程序未处于“活动状态”。
有时核心转储文件在一个系统(核心主机)上创建,而您想在另一台机器(dbx 主机)上装入该核心转储文件。但这样做可能会引起两个与库有关的问题:
核心主机上的程序所使用的共享库与 dbx 主机上的共享库可能不相同。要获取该库相关的正确栈跟踪,需要在 dbx 主机上提供这些原始库。
dbx 使用 /usr/lib 中的系统库来帮助了解系统内有关运行时链接程序和线程库实现的详细信息。可能还需要通过核心主机提供这些系统库,以便 dbx 能够了解运行时链接程序数据结构和线程数据结构。
用户库和系统库可随着修补程序以及主要的 Solaris 操作环境升级而改变,因此如果在收集核心转储文件之后但在核心转储文件上运行 dbx 之前安装修补程序,此问题仍会出现在同一台主机上。
当装入“不匹配”的核心转储文件时,dbx 可能会显示以下一条或多条错误消息:
dbx: core file read error: address 0xff3dd1bc not available dbx: warning: could not initialize librtld_db.so.1 -- trying libDP_rtld_db.so dbx: cannot get thread info for 1 -- generic libthread_db.so error dbx: attempt to fetch registers failed - stack corrupted dbx: read of registers from (0xff363430) failed -- debugger service failed |
将 dbx 环境变量 core_lo_pathmap 设置为 on。
使用 pathmap 命令告知 dbx 核心转储文件的正确库的位置。
使用 debug 命令装入程序和核心转储文件。
例如,假定核心主机的根分区已通过 NFS 导出,并且可以通过 dbx 主机上的 /net/core-host/ 访问,应使用以下命令装入 prog 程序和 prog.core 核心转储文件来进行调试:
(dbx) dbxenv core_lo_pathmap on (dbx) pathmap /usr /net/core-host/usr (dbx) pathmap /appstuff /net/core-host/appstuff (dbx) debug prog prog.core |
如果没有导出核心主机的根分区,则必须手动复制这些库。不需要重新创建符号链接。(例如,您不必建立从 libc.so 到 libc.so.1 的链接,只要确保 libc.so.1 可用。)
调试不匹配的核心转储文件时应注意:
pathmap 命令不能识别 "/" 路径映射,因此不能使用以下命令:
pathmap / /net/core-host
pathmap 命令的单参数模式不能与装入对象路径名同时使用,因此请使用双参数(来源路径和目标路径)模式。
如果 dbx 主机使用的 Solaris 操作环境版本与核心主机相同或比它更新,那么调试核心转储文件时效果可能会更好,虽然这并不总是必要。
可能需要的系统库是:
对于运行时链接程序:
/usr/lib/ld.so.1
/usr/lib/librtld_db.so.1
/usr/lib/64/ld.so.1
/usr/lib/64/librtld_db.so.1
对于线程库,取决于您所使用的 libthread 执行:
/usr/lib/libthread_db.so.1
/usr/lib/64/libthread_db.so.1
/usr/lib/lwp/libthread_db.so.1
/usr/lib/lwp/64/libthread_db.so.1
/usr/lib/lwp 文件仅适用于在 Solaris 8 操作环境中运行 dbx 的情况,并且仅在您使用备用 libthread 库时适用。
如果 dbx 在 64 位版本的 Solaris OS 上运行,您将需要 64 位版本的 xxx_db.so 库,因为这些系统库是作为 dbx 的一部分(而不是目标程序的一部分)装入和使用的。
与 libc.so 或任何其他库一样,ld.so.1 库是核心转储文件映像的一部分,因此需要与创建核心转储文件的程序相匹配的 32 位 ld.so.1 库或 64 位 ld.so.1 库。
如果正在查看来自某个线程程序的核心转储文件,并且 where 命令未显示栈,请尝试使用 lwp 命令。例如:
(dbx) where current thread: t@0 [1] 0x0(), at 0xffffffff (dbx) lwps o>l@1 signal SIGSEGV in _sigfillset() (dbx) lwp l@1 (dbx) where =>[1] _sigfillset(), line 2 in "lo.c" [2] _liblwp_init(0xff36291c, 0xff2f9740, ... [3] _init(0x0, 0xff3e2658, 0x1, ... ... |
如果 LWP 的帧指针 (fp) 被破坏,则 lwp 命令的 -setfp 和 -resetfp 选项会很有用。这些选项在调试核心转储文件时使用,此时 assign $fp=... 不可用。
缺少线程栈表明 thread_db.so.1 有问题,因此,您可能还需要尝试从核心主机中复制适当的 libthread_db.so.1 库。
将进程 ID 用作 dbx 命令的参数,可以将正在运行的进程连接到 dbx。
$ dbx program_name process_id |
将 dbx 连接到包含 JavaTM 代码和 C JNI(Java Native Interface,Java 本地接口)代码或 C++ JNI 代码的正在运行的进程:
$ dbx program_name{.class | .jar} process_id |
您也可以在不知道程序名的情况下,使用进程 ID 连接进程。
$ dbx - process_id |
由于 dbx 仍然不知道程序名,因此无法将参数传递到 run 命令中的进程。
有关更多信息,请参见将 dbx 连接到正在运行的进程。
启动 dbx 时,如果不指定 -S 选项,dbx 将在目录 /installation_directory/lib 中查找已安装的启动文件 dbxrc。(在 Solaris 平台和 Linux 平台上,缺省 installation_directory 分别是 /opt/SUNWspro 和 /opt/sun/sunstudio10u1。)如果 Sun Studio 软件没有安装在缺省目录下,dbx 将从 dbx 可执行文件路径派生出 dbxrc 文件路径。
然后 dbx 在当前目录中搜索 .dbxrc 文件, 然后在 $HOME 中进行搜索。通过使用 -s 选项指定文件路径,可以显式指定与 .dbxrc 不同的启动文件。有关更多信息,请参见使用 dbx 初始化文件。
启动文件可以包含任何 dbx 命令,通常包含 alias 命令、dbxenv 命令、pathmap 命令以及 Korn shell 函数定义。但某些命令要求已经装入程序或已经连接进程。所有启动文件均在装入程序或进程之前装入。启动文件也可以使用 source 或 .(句点)命令查找其他文件。您还可以使用启动文件设置其他 dbx 选项。
dbx 在装入程序信息的同时,将输出一系列的消息,如 Reading filename。
完成程序装入后,dbx 进入就绪状态,访问程序的 "main" 块(对于 C 或 C++ 而言:main();对于 Fortran 95 而言:MAIN())。一般来说,应设置断点(例如,stop in main),然后对 C 程序发出 run 命令。
可使用 pathmap、dbxenv 和 alias 命令为 dbx 会话设置启动属性。
缺省情况下,dbx 在编译程序的目录中查找与所调试的程序相关联的源文件。如果源文件或目标文件不在此目录下,或者所使用的机器没有使用相同的路径名,您必须通知 dbx 这些文件的位置。
如果移动源文件或目标文件,可以将它们的新位置添加到搜索路径。pathmap 命令可创建从文件系统的当前视图到可执行映像中的名称的映射。该映射应用于源路径和目标文件路径。
向 .dbxrc 文件中添加公共 pathmap。
(dbx) pathmap [ -c ] from to |
如果使用 -c,该映射还将应用于当前工作目录。
pathmap 命令对于处理在不同主机上具有不同基路径的自动挂载和显式 NFS 挂载文件系统很有用。尝试解决由于在自动挂载的文件系统上 CWD 不准确而由自动安装程序引起的问题时,可使用 -c。
缺省情况下,存在 /tmp_mnt 到 / 的映射。
有关更多信息,请参见pathmap 命令。
可使用 dbxenv 命令列出或设置 dbx 定制变量。可以将 dbxenv 命令放置在 .dbxrc 文件中。要列出变量,请键入:
$ dbxenv |
也可以设置 dbx 环境变量。有关 .dbxrc 文件以及设置这些变量的更多信息,请参见使用 replay 恢复和保存。
有关更多信息,请参见设置 dbx 环境变量和dbxenv 命令。
可使用 kalias 或 dalias 命令创建自己的 dbx 命令。有关更多信息,请参见dalias 命令。
在利用 dbx 调试程序前,必须使用 -g 或 -g0 选项进行编译。
-g 选项指示编译器在编译期间生成调试信息。
例如,要用 C++ 进行编译,键入:
% CC -g example_source.cc |
在 C++ 中,-g 选项打开调试并关闭函数的内联。-g0(零)选项打开调试但并不影响函数的内联。不能用 -g0 选项调试内联函数。-g0 选项可大大减少链接时间和 dbx 启动时间(取决于程序所使用的内联函数)。
要编译优化代码以用于 dbx,请使用 -O(大写字母 O)和 -g 选项编译源代码。
dbx 允许您在 objcopy 命令(在 Linux 平台上)和 gobjcopy 命令(在 Solaris 平台上)中使用选项,将调试信息从可执行文件复制到独立的调试文件、从可执行文件中删除该信息,以及在这两个文件之间创建链接。
dbx 按照以下顺序搜索独立的调试文件,并从找到的第一个文件中读取调试信息:
包含可执行文件的目录。
包含可执行文件的目录中名为 debug 的子目录。
全局调试文件目录的子目录;如果 dbx 环境变量 debug_file_directory 设置为该目录的路径名,您可以查看或更改该目录。环境变量的缺省值为 /usr/lib/debug。
例如,要为可执行文件 a.out 创建独立的调试文件,应执行以下操作。
创建包含调试信息的、名为 a.out.debug 的独立调试文件。
从 a.out 中删除调试信息。
在两个文件之间建立链接。在 Solaris 平台上,使用 gobjcopy 命令。在 Linux 平台上,使用 objcopy 命令。
在 Linux 平台上,可以使用 objcopy -help 命令来确定该平台是否支持 -add-gnu-debuglink 选项。可以使用 cp a.out a.out.debug 命令替换 objcopy 命令的 -only-keep-debug 选项,以便使 a.out.debug 成为完全可执行文件。
dbx 为优化代码提供部分调试支持。支持的程度主要取决于编译程序的方式。
当分析优化代码时,可以:
在任何函数的开始处停止执行(stop in function 命令)
计算、显示或修改参数
计算、显示或修改全局变量、局部变量或静态变量
从一行到另一行单步执行(next 或 step 命令)
如果在同时启用优化和调试的情况下(使用 -O -g 选项)编译程序,dbx 将在限定模式下进行操作。
关于在什么情况下哪些编译器发出哪种符号信息的详细信息通常被认为是不稳定的接口,可能随版本的变化而不同。
源代码行信息可用,但一个源代码行的代码可能会出现在优化程序的几个不同位置上,所以按源代码行在程序中单步执行会导致“当前行”在源文件中跳转,这取决于优化器如何调度代码。
当函数中的最后一个有效操作是调用另一个函数时,尾部调用优化会导致丢失栈帧。
通常,对于优化程序而言,参数、局部变量和全局变量的符号信息可用。结构、联合、C++ 类的类型信息,以及局部变量、全局变量和参数的类型和名称应该可用。C++ 编译器不提供局部变量的符号类型信息;C 编译器则提供。
优化代码中经常缺少有关参数和局部变量的位置的信息。如果编译器生成 Dwarf 位置列表,则 dbx 将使用该信息输出局部变量和参数的值。如果在优化函数的第一个指令处停止,则 dbx 可输出参数的值,因为这些值将位于符合 ABI 标准的寄存器或栈位置中。要查看局部变量的值,可能还需要设置 dbx 环境变量 optim_local_vars。
调试信息中的一个简单位置描述了一个寄存器或栈位置。位置列表描述了变量在代码中不同点的不同位置,使得位置列表能够更好地描述优化代码。
从 Sun Studio 12 发行版开始,Sun Studio 编译器将不生成位置列表,但会针对某些优化代码生成简单位置。更新版本的 GNU 编译器使用位置列表描述帧指针,并描述某些参数和局部变量。编译器记录该信息的方式可能会随发行版不同而异。
尽管在尚未发生最终寄存器至内存存储的情况下全局变量的值可能不准确,但您仍可以输出全局变量并为这些变量赋值。
《Sun Studio 11 性能分析器》手册第 8 章介绍了有关编译器优化的信息,这些信息在调试优化程序时可能很有用。
对于 OpenMP 程序,使用 -xopenmp=noopt 选项进行编译即指示编译器不要应用任何优化。但是,为了实现 OpenMP 指令,优化器仍会处理代码,因此,使用 -xopenmp=noopt 编译的程序可能会出现所描述的一些问题。
虽然大多数调试支持要求使用 -g 选项编译程序,dbx 仍为未使用 -g 选项进行编译的代码提供以下级别的支持:
栈回溯 (dbx where 命令)
调用函数(但没有参数检查)
检查全局变量
但请注意,除非用 -g 选项编译代码,否则 dbx 无法显示源代码。此限制也适用于使用 strip -x 的代码。
要获得完全支持,共享库也必须使用 -g 选项进行编译。如果利用没有使用 -g 选项进行编译的共享库模块来生成程序,则仍可以调试该程序。但由于未为这些库模块生成信息,所以无法获得完全 dbx 支持。
dbx 工具能够调试已完全剥离的程序。这些程序包含一些可用来调试程序的信息,但只有外部可见函数可用。有些运行时检查对剥离程序或装入对象有效:内存使用检查有效,访问检查对使用 strip -x 剥离的代码有效,对使用 strip 剥离的代码无效。
dbx 会话在启动 dbx 之后将持续运行,直到退出 dbx 为止;在 dbx 会话期间,可以连续调试任何数目的程序。
要退出 dbx 会话,请在 dbx 提示符下键入 quit。
(dbx) quit |
如果启动 dbx 时使用 process_id 选项将其连接到正在运行的进程,退出调试会话,该进程仍存在并继续运行。dbx 在退出会话之前执行隐式 detach。
随时都可以通过按下 Ctrl+C 组合键停止执行进程,而无需退出 dbx。
如果已将 dbx 连接到一个进程,通过使用 detach 命令,无需中止进程或 dbx 会话便可从 dbx 中分离进程。
要想不中止进程而从 dbx 中分离,请键入:
(dbx) detach |
在临时应用其他基于 /proc 的调试工具(这些工具可能由于 dbx 专用访问而被阻止)时,可以分离进程,同时将进程保留在停止状态。有关更多信息,请参见从进程中分离 dbx。
有关 detach 命令的更多信息,请参见detach 命令。
dbx kill 命令用于终止当前进程的调试和中止进程。但 kill 命令保留 dbx 会话,让 dbx 准备调试另一个程序。
中止程序是无需退出 dbx 即可消除正在调试的程序的剩余部分的好方法。
要中止 dbx 中正在执行的程序,请键入:
(dbx) kill |
有关更多信息,请参见kill 命令。
dbx 工具提供了三个命令,用于保存全部或部分调试运行,还可以重新运行调试:
save 命令将自上一 run 命令、rerun 命令或 debug 命令开始直到 save 命令期间所发出的所有调试命令保存到文件中。调试会话中的此段称为调试运行。
save 命令不仅仅保存已发出的调试命令列表。它还保存运行开始时与程序状态相关联的调试信息,如断点、显示列表等等。当恢复已保存的运行时,dbx 将使用保存文件中的信息。
可以保存调试运行的一部分,即,整个运行从最后输入命令开始减去指定数目的命令。
debug |
debug |
||
stop at line |
stop at line |
||
run |
run |
||
next |
next |
||
next |
next |
||
保存整个运行 |
stop at line |
保存运行,但不包括 |
stop at line |
continue |
最后两步 |
continue |
|
next |
next |
||
next |
next |
||
step |
step |
||
next |
next |
||
save |
save-2 |
||
如果不能确定要在何处结束正在保存的运行,可以使用 history 命令查看自会话开始以来发出的调试命令列表。
缺省情况下,save 命令将信息写入特定的保存文件。如果要将调试运行保存到稍后可以恢复的文件,可使用 save 命令来指定文件名。请参见将系列调试运行另存为检查点。
要保存直到 save 命令的整个调试运行,请键入:
(dbx) save |
要保存部分调试运行,可使用 save number 命令,其中 number 是在 save 命令之前所不想保存的命令的个数。
(dbx) save -number |
如果保存调试运行时没有指定文件名,dbx 将信息写入特定的保存文件。每次保存时,dbx 都会覆盖此保存文件。但可通过为 save 命令指定 filename 参数,将调试运行保存到稍后可以恢复的文件,因为信息已保存到 filename,所以即使保存了其他调试运行,这个文件今后也能恢复。
保存一系列运行可为您提供一组检查点,从会话中较早的时间开始依次排列每个检查点。您可以恢复这些已保存运行中的任意一个运行,继续,然后将 dbx 重置到早期运行中所保存的程序位置和状态。
要将调试运行保存到其他文件而不是缺省的保存文件:
(dbx) save filename |
保存运行后,可使用 restore 命令恢复该运行。dbx 使用保存文件中的信息。恢复运行时, dbx 首先将内部状态重置到运行开始时的状态,然后重新发出所保存的运行中的每个调试命令。
source 命令也可以重新发出存储在文件中的命令集,但不重置 dbx 的状态;它仅从当前程序位置重新发出命令列表。
要精确恢复已保存的调试运行,运行的所有输入必须完全相同:run 类型命令的参数、手动输入和文件输入。
如果在执行 restore 之前保存段,然后发出 run、rerun 或 debug 命令,restore 将使用第二个保存后 run、rerun 或 debug 命令的参数。如果这些参数不同,则不能进行精确恢复。
(dbx) restore |
要恢复已保存到其他文件而不是缺省的保存文件的调试运行,请键入:
(dbx) restore filename |
replay 命令是一个组合命令,相当于在 save -1 命令后立即发出一个 restore 命令。replay 命令带有负的 number 参数,此参数将传递给命令的 save 部分。缺省情况下,-number 的值为 -1,因此 replay 相当于撤消命令,可以恢复直到(但不包括)最后发出的命令为止的最后一个运行。
要重新运行当前的调试运行,但不包括最后发出的调试命令,请键入:
(dbx) replay |
要重新运行当前调试运行并在特定命令前停止该运行,请使用 dbx replay 命令,其中 number 是最后一个调试命令之前的命令个数。
(dbx) replay -number |