3.2. 使用 GDB 检查应用程序内部状态
若要查找应用无法正常工作的原因,请使用调试器控制其执行并检查其内部状态。本节论述了如何将 GNU Debugger(GDB)用于此任务。
3.2.1. GNU debugger(GDB)
Red Hat Enterprise Linux 包含 GNU debugger(GDB),它可让您通过命令行用户界面调查程序内发生的情况。
GDB 功能
单个 GDB 会话可以调试以下类型的程序:
- 多线程和 fork 程序
- 一次多个程序
-
远程机器上的程序或带有
gdbserver
工具的容器中的程序通过 TCP/IP 网络连接
调试要求
要调试任何可执行代码,GDB 需要该特定代码的调试信息:
- 对于由您开发的程序,您可以在构建代码时创建调试信息。
- 对于从软件包安装的系统程序,您必须安装其 debuginfo 软件包。
3.2.2. 将 GDB 附加到进程
要检查进程,GDB 必须附加到进程中。
先决条件
- 在系统中必须安装 GDB
使用 GDB 启动程序
当程序没有作为进程运行时,使用 GDB 启动它:
$ gdb program
使用到程序的文件名或路径替换 program。
GDB 设置为开始执行程序。在使用 run
命令开始执行进程前,您可以设置断点和 gdb
环境。
将 GDB 附加到已经运行的进程
将 GDB 附加到已作为进程运行的程序中:
使用
ps
命令查找进程 ID(pid):$ ps -C program -o pid h pid
使用到程序的文件名或路径替换 program。
将 GDB 附加到此过程:
$ gdb -p pid
使用
ps
输出中的实际进程 ID 编号替换 pid。
将已在运行的 GDB 附加到已经运行的进程
将已在运行的 GDB 附加到已经运行的程序中:
使用
shell
GDB 命令运行ps
命令,并查找程序的进程 ID(pid):(gdb) shell ps -C program -o pid h pid
使用到程序的文件名或路径替换 program。
使用
attach
命令将 GDB 附加到程序:(gdb) attach pid
使用
ps
输出中的实际进程 ID 编号替换 pid。
在某些情况下,GDB 可能无法找到对应的可执行文件。使用 file
命令指定路径:
(gdb) file path/to/program
其他资源
- 使用 GDB 进行调试 - 2.1 Invoking GDB
- 使用 GDB 进行调试 - 4.7 调试 Already-running Process
3.2.3. 使用 GDB 检查程序代码
将 GDB 调试器附加到程序后,您可以使用一些命令来控制程序的执行。
先决条件
您必须具有所需的调试信息:
- 程序使用调试信息编译和构建,或者
- 已安装相关的 debuginfo 软件包
- GDB 必须附加到程序中才能被调试
GDB 命令逐步完成代码
r
(run)-
开始执行程序。如果使用任何参数执行
run
,这些参数会像正常启动程序一样将这些参数传递给可执行文件。在设置断点后用户通常会发出此命令。 start
-
开始执行程序,但在程序的主函数的开头停止。如果使用任何参数执行
start
,这些参数将传送到可执行文件,就如程序启动正常一样。
c
(continue)继续从当前状态执行程序。该程序的执行将继续进行,直到以下其中之一变为 true:
- 已有一个断点。
- 满足指定的条件。
- 程序收到信号。
- 发生错误。
- 程序终止。
n
(next)继续从当前状态执行程序,直到达到当前源文件中的下一行代码。该程序的执行将继续进行,直到以下其中之一变为 true:
- 已有一个断点。
- 满足指定的条件。
- 程序收到信号。
- 发生错误。
- 程序终止。
s
(step)-
step
命令还会在当前源文件中的每个后续代码行下停止执行。但是,如果执行目前在包含 函数调用 的源行停止,GDB 会在输入函数调用(而不是执行)后停止执行。 until
location- 继续执行,直到达到 location 选项指定的代码位置。
fini
(finish)恢复执行程序并在执行从功能返回时停止执行。该程序的执行将继续进行,直到以下其中之一变为 true:
- 已有一个断点。
- 满足指定的条件。
- 程序收到信号。
- 发生错误。
- 程序终止。
q
(quit)- 终止执行并退出 GDB。
3.2.4. 使用 GDB 显示程序内部值
显示程序内部变量的值对于了解程序正在执行的操作非常重要。GDB 提供多个命令,可用于检查内部变量。以下是这些命令中最有用的命令:
p
(print)显示所给定参数的值。通常,参数是任何复杂性的变量名称,从简单的单一值到结构。参数也可以是当前语言中有效的表达式,包括使用程序变量和库函数,或者在正在测试的程序中定义的函数。
可以使用 pretty-printer Python 或 Guile 脚本对 GDB 进行扩展,以使用
print
命令自定义显示数据结构(如类、结构)等。bt
(backtrace)显示用于到达当前执行点的功能调用链,或者使用直到执行终止前所使用的功能链。这在调查严重错误(如分段错误)时非常有用,并带有严重原因。
在
backtrace
命令中添加full
选项也会显示本地变量。可以使用 帧过滤 Python 脚本扩展 GDB,以使用
bt
和info
框架命令对显示的数据进行自定义显示。术语 帧(frame) 指的是与单个功能调用关联的数据。info
info
命令是提供有关各种项目的通用命令。它取指定要描述的项目的选项。-
info args
命令显示当前选择帧的功能调用选项。 -
info locals
命令在当前选定的框中显示本地变量。
如需可能的项目列表,请在 GDB 会话中运行命令
help info
:(gdb) help info
-
l
(list)-
显示程序停止的源代码中的行。此命令仅在程序执行停止时可用。虽然不是严格显示内部状态的命令,但
list
有助于用户了解执行程序在下一步中将发生对内部状态的更改。
其他资源
- GDB Python API - Red Hat Developers Blog 条目
- 使用 GDB 进行调试 - Pretty Printing
3.2.5. 使用 GDB 断点在定义的代码位置停止执行
通常,只调查一小部分代码。断点(breakpoints)是用来告诉 GDB 在代码中某个特定位置停止执行程序的标记。断点与源代码行最常关联。在这种情况下,放置断点需要指定源文件和行号。
要放置断点 :
指定源代码 file 的名称以及在该文件中的 line :
(gdb) br file:line
如果 file 不存在,则使用当前执行点的源文件的名称:
(gdb) br line
或者,使用函数名称将断点放在其开始上:
(gdb) br function_name
在任务进行一定迭代后,程序可能会遇到错误。要指定额外的 condition 停止执行:
(gdb) br file:line if condition
使用 C 或 C++ 语言条件替换 condition。file 和 line 如以上是相同的。
检查所有断点和监视点的状态:
(gdb) info br
使用
info br
输出中的数字来删除断点:(gdb) delete number
删除给定位置的断点:
(gdb) clear file:line
其他资源
- 使用 GDB 进行调试 - 断点、观察点和捕获点
3.2.6. 使用 GDB 观察点停止对数据访问和更改执行
在很多情况下,在某些数据更改或访问前,让程序执行得很好。以下示例是最常见的用例。
先决条件
- 了解 GDB
在 GDB 中使用监视点
Watchpoints 是用来告诉 GDB 停止执行某个程序的标记。Watchpoints 与数据相关联:放置监视点需要指定一个表达式来描述变量、多个变量或内存地址。
为数据 change (写) 放置一个观察点:
(gdb) watch expression
使用描述您要监视的表达式替换 expression。对于变量,expression 等于变量的名称。
为数据 access (读) 放置一个观察点:
(gdb) rwatch expression
要针对 任何 数据访问 放置 监视点(读取和写入):
(gdb) awatch expression
检查所有观察点和断点的状态:
(gdb) info br
删除一个监视点:
(gdb) delete num
将 num 选项替换为
info br
命令报告的编号。
其他资源
- 使用 GDB 调试 - 设置 Watchpoints
3.2.7. 使用 GDB 调试 fork 或线程程序
有些程序使用分叉或线程来实现并行代码执行。调试多个同时执行路径需要特殊考虑。
先决条件
- 您必须了解进程分叉和线程的概念。
使用 GDB 调试 fork 程序
当程序(父)创建本身的独立副本(子)时,分叉是一个状况。使用以下设置和命令影响 GDB 在进行分叉时的作用:
follow-fork-mode
设置控制 GDB 在分叉后是否遵循父项或子项。设置 follow-fork-mode 父项
- 在分叉后,调试父进程。这是默认值。
设置 follow-fork-mode 子项
- 在分叉后,调试子进程。
显示后续模式
-
显示
follow-fork-mode
的当前设置。
set detach-on-fork
设置控制 GDB 是否控制其他进程(未跟随)进程,还是保留它继续运行。设置 detach-on-fork on
-
未遵循的进程(取决于
follow-fork-mode
值 )将独立分离并运行。这是默认值。 set detach-on-fork off
-
GDB 控制这两个进程。其后的进程(取决于
follow-fork-mode
的值)会正常进行调试,而另一个被暂停。 显示 detach-on-fork
-
显示
detach-on-fork
的当前设置。
使用 GDB 调试线程程序
GDB 能够单独调试单个线程,并单独操作并检查它们。要使 GDB 只停止检查的线程,请使用命令 set non-stop on
和 set target-async on
您可以将这些命令添加到 .gdbinit
文件中。在打开该功能后,GDB 已准备好进行线程调试。
GDB 使用 当前线程 的概念。默认情况下,命令仅应用到当前的线程。
info 线程
-
显示带有相应
id
和gid
的线程列表,代表当前的线程。 线程 ID
-
将指定
id
的线程设置为当前的线程。 线程应用 ids command
-
将
command
命令应用到ids
列出的所有线程。ids
选项是以空格分隔的线程 ID 列表。特殊值all
将命令应用到所有线程。 break location thread id if condition
-
只对线程号
id
在带有特定条件
的特定位置
设置一个断点。 watch expression thread id
-
仅为线程编号 ID
id
设置由expression
定义的观察点。 command&
-
执行
command
命令并立即返回到 gdb 提示符(gdb)
,然后在后台继续执行任何代码。 interrupt
- 在后台停止执行。
其他资源
- 使用 GDB 进行调试 - 4.10 使用多个线程调试程序
- 使用 GDB 进行调试 - 4.11 调试 Forks