Oracle Solaris Studio 12.2:使用 dbx 调试程序

使用 dbx 调试程序

可能出于下列原因之一调试程序:

检查核心转储文件

确定程序发生崩溃的位置,可能需要检查核心转储文件,即程序崩溃时的程序内存映像。可使用 where 命令(请参见where 命令)确定程序在转储核心时的执行位置。


注 –

dbx 无法像对待本机代码那样通过核心转储文件来指明 Java 应用程序的状态。


要调试核心转储文件,请键入:


$ dbx program_name core


$ dbx - core

在下面的示例中,程序因段故障和转储核心而崩溃。用户启动 dbx 并装入核心转储文件。然后使用 where 命令显示栈跟踪,其中显示在 foo.c 文件的第 9 行发生崩溃。


% dbx a.out core
Reading a.out
core file header read successfully
Reading ld.so.1
Reading libc.so.1
Reading libdl.so.1
Reading libc_psr.so.1
program terminated by signal SEGV (no mapping at the fault address)
Current function is main
    9       printf("string ’%s’ is %d characters long\n", msg, strlen(msg));
(dbx) where
  [1] strlen(0x0, 0x0, 0xff337d24, 0x7efefeff, 0x81010100, 0xff0000), at
0xff2b6dec
=>[2] main(argc = 1, argv = 0xffbef39c), line 9 in "foo.c"
(dbx)

有关调试核心转储文件的更多信息,请参见调试核心转储文件。有关使用调用栈的更多信息,请参见查看调用栈


注 –

如果程序与共享库动态链接,最好在创建核心转储文件的操作环境中调试该文件。有关如何调试在不同的操作环境中创建的核心转储文件的信息,请参见调试不匹配的核心转储文件


设置断点

断点是程序中要暂时停止程序的执行并让 dbx 进行控制的位置。在程序内怀疑存在错误之处设置断点。如果程序崩溃,请确定崩溃的发生位置,然后在这部分代码前设置断点。

程序在断点处停止时,便可以检查程序的状态和变量值。使用 dbx 可以设置多种类型的断点(请参见使用 Ctrl+C 停止进程)。

最简单的断点类型就是停止断点。可以设置用于在函数或过程中停止的停止断点。例如,要在调用 main 函数时停止:


(dbx) stop in main
(2) stop in main

有关 stop in 命令的更多信息,请参见在函数中设置 stop 断点stop 命令

也可以设置用于在源代码的特定行处停止的停止断点。例如,要在源文件 t.c 中的第 13 行处停止:


(dbx) stop at t.c:13
(3) stop at “t.c”:13

有关 stop at 命令的更多信息,请参见在源代码行设置 stop 断点stop 命令

可以使用 file 命令设置当前文件并使用 list 命令列出要在其中停止的函数来确定要停止在那里的行。然后使用 stop at 命令在源代码行设置断点:


(dbx) file t.c
(dbx) list main
10    main(int argc, char *argv[])
11    {
12        char *msg = "hello world\n";
13        printit(msg);
14    }
(dbx) stop at 13
(4) stop at “t.c”:13

要使程序在断点处停止后继续执行,请使用 cont 命令(请参见继续执行程序cont 命令)。

要获取所有当前断点的列表,请使用 status 命令:


(dbx) status
(2) stop in main
(3) stop at "t.c":13

现在如果运行程序,程序将在第一个断点处停止:


(dbx) run
...
stopped in main at line 12 in file "t.c"
12        char *msg = "hello world\n";

单步执行程序

程序在断点处停止后,可能希望按一次执行一个源代码行的方式执行程序,在此时比较程序的实际状态与预期状态。可以使用 stepnext 命令来执行此操作。这两个命令都是执行程序的一个源代码行,当执行完相应行时即停止。但在处理包含函数调用的源代码行时有所差别:step 命令步入函数,而 next 命令步过函数。

step up 命令会一直执行,直至当前函数将控制权返回给调用它的函数为止。

step to 命令会尝试步入当前源代码行中的指定函数;如果未指定任何函数,则尝试步入由当前源代码行的汇编代码确定调用的最后一个函数。

某些函数(特别是 printf 之类的库函数)可能未使用 -g 选项编译,因此 dbx 无法步入这些函数。在这种情况下,stepnext 执行功能相似。

以下示例说明如何使用 stepnext 命令以及在设置断点中设置的断点。


(dbx) stop at 13
(3) stop at "t.c":13
(dbx) run
Running: a.out
stopped in main at line 13 in file "t.c"
   13           printit(msg);
(dbx) next
Hello world
stopped in main at line 14 in file "t.c"
   14   }

(dbx) run
Running: a.out
stopped in main at line 13 in file "t.c"
   13           printit(msg);
(dbx) step
stopped in printit at line 6 in file "t.c"
    6           printf("%s\n", msg);
(dbx) step up
Hello world
printit returns
stopped in main at line 13 in file "t.c"
   13           printit(msg);
(dbx)

有关单步执行程序的更多信息,请参见单步执行程序。有关 stepnext 命令的更多信息,请参见step 命令next 命令

查看调用栈

调用栈表示所有当前处于活动状态的例程,即那些已被调用但尚未返回至各自调用方的例程。在该栈中,函数及其参数按其调用顺序存放。栈跟踪显示程序流中执行停止位置及执行到达此点的过程。它提供了有关程序状态的最简明的描述。

要显示栈跟踪,请使用 where 命令:


(dbx) stop in printf
(dbx) run
(dbx) where
  [1] printf(0x10938, 0x20a84, 0x0, 0x0, 0x0, 0x0), at 0xef763418
=>[2] printit(msg = 0x20a84 "hello world\n"), line 6 in "t.c"
  [3] main(argc = 1, argv = 0xefffe93c), line 13 in "t.c"
(dbx)

对于使用 -g 选项编译的函数,参数名及其类型是已知的,因此会显示精确的值。对于无调试信息的函数,显示的参数值是十六进制数。这些数字未必都有意义。例如,在上述栈跟踪中,帧 1 所示为 SPARC 输入寄存器 $i0$i5 的内容,但仅寄存器 $i0$i1 的内容有意义,因为只有两个参数传递到单步执行程序所示的示例中的 printf

可以在未使用 -g 选项编译的函数中停止。在此类函数中停止时,dbx 在栈内向下搜索其函数是使用 -g 选项编译的第一帧(本例中为 printit()),并为其设置当前作用域(请参见程序作用域)。这用箭头符号 (=>) 表示。

有关调用栈的更多信息,请参见效率方面的考虑

检查变量

虽然栈跟踪可能包含足够的信息,可以完全表明程序的状态,但仍可能需要查看更多变量的值。print 命令可以求表达式的值,并根据表达式的类型输出值。以下示例中例举了几个简单的 C 表达式:


(dbx) print msg
msg = 0x20a84 "Hello world"
(dbx) print msg[0]
msg[0] = ’h’
(dbx) print *msg
*msg = ’h’
(dbx) print &msg
&msg = 0xefffe8b4

可以使用数据更改断点跟踪变量和表达式的值何时发生变化(请参见设置数据更改断点)。例如,要在变量计数值更改时停止执行,请键入:


(dbx) stop change count

查找内存访问问题和内存泄漏

运行时检查由两部分组成: 内存访问检查及内存使用和泄露检查。访问检查将检查被调试应用程序是否不当使用了内存。内存使用和泄露检查包括跟踪所有仍存在的堆空间,然后在需要时或程序终止时,扫描可用数据空间以及识别无引用的空间。

可以使用 check 命令启用内存访问检查及内存使用和泄露检查。要仅启用内存访问检查,请键入:


(dbx) check -access

要启用内存使用和内存泄漏检查,请键入:


(dbx) check -memuse

启用所需的运行时检查类型后,运行程序。程序正常运行,但速度很慢,因为每次进行内存访问前都要检查其有效性。如果 dbx 检测到无效访问,便会显示错误的类型和位置。此时,可以使用 dbx 命令(如 where 命令)获取当前栈跟踪,也可以使用 print 命令检查变量。


注 –

不能对使用 Java 代码和 C JNI 代码或 C++ JNI 代码混编的应用程序使用运行时检查。


有关使用运行时检查的详细信息,请参见第 9 章