Sun Studio 12 Update 1:使用 dbx 调试程序

第 1 章 dbx 入门

dbx 是一个交互式源码级命令行调试工具。可以使用它来以可控方式运行程序以及检查已停止程序的状态。使用 dbx 可以完全控制程序的动态执行过程,包括收集性能和内存使用数据、监视内存访问及检测内存泄漏。

可以使用 dbx 调试采用 C、C++ 或 Fortran 编写的应用程序。也可以调试使用 JavaTM 代码和 C JNI(Java Native Interface,Java 本地接口)代码或 C++ JNI 代码混合编写的应用程序,但存在一些限制(请参见使用 dbx 调试 Java 代码的限制)。

dbxtool dbx 提供图形用户界面。

本章提供使用 dbx 调试应用程序的基本知识。其中包含以下各节:

编译调试代码

在使用 dbx 对程序进行源代码级调试前,必须使用 -g 选项(C 编译器、C++ 编译器、Fortran 95 编译器和 Java 编译器均接受此选项)编译程序。有关更多信息,请参见编译调试程序

启动 dbxdbxtool 和装入程序

要启动 dbx,请在 shell 提示符下键入 dbx 命令:


$ dbx

要启动 dbxtool,请在 shell 提示符下键入 dbxtool 命令:


$ dbxtool

要启动 dbx 并装入要调试的程序:


$ dbx program_name

要启动 dbxtool 并装入要调试的程序:


$ dbxtool program_name

要启动 dbx 并装入 Java 代码和 C JNI 代码或 C++ JNI 代码混编的程序:


$ dbx program_name{.class | .jar}

可使用 dbx 命令启动 dbx 并通过指定进程 ID 将其连接到运行中的进程。


$ dbx - process_id

可使用 dbxtool 命令启动 dbxtool 并通过指定进程 ID 将其连接到运行中的进程。


$ dbxtool - process_id

如果不知道进程的进程 ID,请在 dbx 命令中加入 pgrep 命令来查找并连接至进程。例如:


$ dbx - `pgrep Freeway`
Reading -
Reading ld.so.1
Reading libXm.so.4
Reading libgen.so.1
Reading libXt.so.4
Reading libX11.so.4
Reading libce.so.0
Reading libsocket.so.1
Reading libm.so.1
Reading libw.so.1
Reading libc.so.1
Reading libSM.so.6
Reading libICE.so.6
Reading libXext.so.0
Reading libnsl.so.1
Reading libdl.so.1
Reading libmp.so.2
Reading libc_psr.so.1
Attached to process 1855
stopped in _libc_poll at 0xfef9437c
0xfef9437c: _libc_poll+0x0004:   ta     0x8
Current function is main
   48   XtAppMainLoop(app_context);
(dbx)

有关 dbx 命令和启动选项的更多信息,请参见dbx 命令dbx(1) 手册页,或键入 dbx -h

如果 dbx 已在运行,可使用 debug 命令装入要调试的程序,或从正在调试的程序切换到其他程序:


(dbx) debug program_name

要装入或切换到包含 Java 代码和 C JNI 代码或 C++ JNI 代码的程序:


(dbx> debug program_name{.class | .jar}

如果 dbx 已在运行,还可以使用 debug 命令将 dbx 连接到运行中的进程:


(dbx) debug program_name process_id

要将 dbx 连接到包含 Java 代码和 C JNI(Java Native Interface,Java 本地接口)代码或 C++ JNI 代码的运行中的进程:


(dbx) debug program_name{.class | .jar} process_id

有关 debug 命令的更多信息,请参见debug 命令

dbx 中运行程序

要在 dbx 中运行最近装入的程序,请使用 run 命令。如果最初键入 run 命令时没有使用参数,则程序便在没有参数的情况下运行。要传递参数或重定向程序的输入或输出,请使用下列语法:


run [ arguments ] [ < input_file ] [ > output_file ]

例如:


(dbx) run -h -p < input > output
Running: a.out
(process id 1234)
execution completed, exit code is 0
(dbx)

运行包含 Java 代码的应用程序时,运行参数传递给 Java 应用程序而不是 JVM 软件。不要把主类名当作参数。

如果重复执行 run 命令时没有使用参数,程序重新启动时使用上一个 run 命令中的参数或重定向。可以使用 rerun 命令重置选项。有关 run 命令的更多信息,请参见run 命令。有关 rerun 命令的更多信息,请参见rerun 命令

应用程序可能会运行完毕或正常终止。如果设置了断点,程序可能会在断点处停止。如果应用程序中有错误,会因内存故障或段故障而停止。

使用 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";

单步执行程序

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

退出 dbx

dbx 会话在启动 dbx 之后将持续运行,直到退出 dbx 为止;在 dbx 会话期间,可以连续调试任意数量的程序。

要退出 dbx 会话,请在 dbx 提示符下键入 quit


(dbx) quit

如果启动 dbx 时使用 process_id 选项将其连接到正在运行的进程,退出调试会话,该进程仍存在并继续运行。dbx 在退出会话之前执行隐式 detach

有关退出 dbx 的更多信息,请参见退出调试.。

访问 dbx 联机帮助

dbx 包含一个帮助文件,可使用 help 命令进行访问:


(dbx) help