Sun Studio 12:使用 dbx 调试程序

第 7 章 使用调用栈

本章讨论 dbx 如何使用调用栈,以及在处理调用栈时如何使用 where 命令、hide 命令、unhide 命令和 pop 命令。

调用栈表示那些已调用但尚未返回至各自调用方的所有当前活动例程。栈帧是分配供一个函数使用的调用栈的一段。

由于调用栈是从高端内存(较大地址)延伸到低端内存,因此向上意味着向调用函数的帧移动(最终移动到 main()),向下意味着向被调用函数的帧移动(最终移动到当前函数)。程序在断点处停止、单步执行后或出错并生成核心转储文件时,用于例程执行的帧位于低端内存中。调用程序例程(如 main())位于高端内存中。

本章由以下部分组成:

确定在栈中的位置

使用 where 命令确定当前在栈中的位置。


where [-f] [-h] [-l] [-q] [-v] number_id

调试混合使用 JavaTM 代码和 C JNI(Java Native Interface,Java 本地接口)代码和 C++ JNI 代码编写的程序时,where 命令的语法为:


where [-f] [-q] [-v] [ thread_id ] number_id

where 命令对于了解已崩溃并已生成核心转储文件的程序的状态也很有用。出现这种情况时,可将核心转储文件装入 dbx 中 (请参见调试核心转储文件)。

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

栈中移动和返回起始位置

在栈中上下移动称为 “栈中移动”。当您通过在栈中上下移动的方式访问函数时,dbx 会显示当前函数和源代码行。起始位置起始是程序停止执行的点。可以使用 up 命令、down 命令或 frame 命令从起始处在栈中上下移动。

dbx 命令 updown 均接受 number 参数,该参数指示 dbx 在栈中从当前帧向上或向下移动若干帧。如果未指定 number,缺省值为 1。-h 选项表示将所有隐藏帧都计数在内。

在栈中上下移动

可以检查非当前函数中的局部变量。

栈中上移

要在调用栈中上移(向 main 移动)number 级:


up [-h] [ number ]

如果未指定 number,缺省值为一级。有关更多信息,请参见up 命令

栈中下移

在调用栈中下移(向当前停止点移动)number 级:


down [-h] [ number ]

如果未指定 number,缺省值为一级。有关更多信息,请参见down 命令

移到特定帧

frame 命令与 up 命令和 down 命令类似。利用它可直接转到 where 命令显示的编号所指示的帧。


frame
frame -h
frame [-h] number
frame [-h] +[number]
frame [-h] -[number]

如果执行 frame 命令时不使用参数,将显示当前帧编号。如果使用 number,则直接转到编号所指示的帧。在命令中加入 +(加号)或 -(减号)可向上 (+) 或向下 (-) 移动一级。如果在 number 中包括加号或减号,可以向上或向下移动指定数量的级别。- h 选项将所有隐藏帧都包括在计数中。

也可以使用 pop 命令移到特定帧(请参见弹出调用栈)。

弹出调用栈

可以从调用栈中删除停止于函数,从而使调用函数成为新的停止于函数。

与在调用栈中上下移动不同,弹出栈会更改程序的执行。从栈中删除停止于函数后,该函数会使程序恢复到其先前状态,但对全局或静态变量、外部文件、共享成员及类似全局状态的更改不会恢复到先前状态。

pop 命令可从调用栈中删除一个或多个帧。例如,要从栈中弹出五个帧,请键入:


pop 5

也可以弹到特定帧。要弹到第 5 帧,请键入:


pop -f 5

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

隐藏栈帧

可使用 hide 命令列出当前正在使用的栈帧过滤器。

要隐藏或删除所有与正则表达式匹配的栈帧,请键入:


hide [ regular_expression ]

regular_expression 与函数名或装入对象的名称匹配,并使用 shksh 语法进行文件匹配。

可使用 unhide 删除所有栈帧过滤器。


unhide 0

由于 hide 命令列出过滤器时会列出相应号码,因此还可以在使用 unhide 命令时使用过滤器号码。


unhide [ number | regular_expression ]

显示和读取栈跟踪

栈跟踪显示执行在程序流中停止的位置及执行到达此点的过程。它提供了非常简明的程序状态描述。

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

对于使用 -g 选项编译的函数,由于参数的名称和类型已知,因此显示的是准确值。对于无调试信息的函数,显示的参数值是十六进制数。这些数字未必都有意义。通过函数指针 0 进行函数调用时,函数值显示为一个小的十六进制数,而非符号名。

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

在以下示例中,main() 是使用 -g 选项编译的,因此会显示符号名以及参数值。main() 调用的库函数不是使用 -g 编译的,因此会显示函数的符号名,但对于参数而言,只会显示 SPARC 输入寄存器 $i0$i5 的十六进制内容。


(dbx) where
  [1] _libc_poll(0xffbef3b0, 0x1, 0xffffffff, 0x0, 0x10, 0xffbef604), at 0xfef9437c
  [2] _select(0xffbef3b8, 0xffbef580, 0xffbef500, 0xffbef584, 0xffbef504, 0x4), at 0xfef4e3dc
  [3] _XtWaitForSomething(0x5a418, 0x0, 0x0, 0xf4240, 0x0, 0x1), at 0xff0bdb6c
  [4] XtAppNextEvent(0x5a418, 0x2, 0x2, 0x0, 0xffbef708, 0x1), at 0xff0bd5ec
  [5] XtAppMainLoop(0x5a418, 0x0, 0x1, 0x5532d, 0x3, 0x1), at 0xff0bd424
=>[6] main(argc = 1, argv = 0xffbef83c), line 48 in "main.cc"
:

在本示例中,程序因段故障而崩溃。同时只有 main() 是使用 -g 选项编译的,因此库函数的参数显示为不带符号名的十六进制内容。造成崩溃的原因很可能是 SPARC 输入寄存器 $i0$i1 中的 strlen() 的参数为空。


(dbx) run
Running: Cdlib
(process id 6723)

CD Library Statistics:

 Titles:         1

 Total time:     0:00:00
 Average time:   0:00:00

signal SEGV (no mapping at the fault address) in strlen at 0xff2b6c5c
0xff2b6c5c: strlen+0x0080:    ld      [%o1], %o2
Current function is main
(dbx) where
  [1] strlen(0x0, 0x0, 0x11795, 0x7efefeff, 0x81010100, 0xff339323), at 0xff2b6c5c
  [2] _doprnt(0x11799, 0x0, 0x0, 0x0, 0x0, 0xff00), at 0xff2fec18
  [3] printf(0x11784, 0xff336264, 0xff336274, 0xff339b94, 0xff331f98, 0xff00), at 0xff300780
=>[4] main(argc = 1, argv = 0xffbef894), line 133 in "Cdlib.c"
(dbx)

有关更多栈跟踪示例,请参见查看调用栈跟踪调用