Red Hat Training
A Red Hat training course is available for RHEL 8
3.2. 使用 GDB 检查应用程序内部状态
要找出应用不能正常工作的原因,请控制其执行并使用调试器检查其内部状态。本节描述了如何对此任务使用 GNU Debugger(GDB)。
3.2.1. GNU 调试器(GDB)
Red Hat Enterprise Linux 包含 GNU 调试器(GDB),允许您通过命令行用户界面调查程序内部发生了什么情况。
GDB 功能
单个 GDB 会话可以调试以下类型的程序:
- 多线程和分叉程序
- 同时有多个程序
-
远程机器上或带有
gdbserver
工具的容器中的程序通过 TCP/IP 网络连接
调试要求
要调试任何可执行代码,GDB 需要该特定代码的调试信息:
- 对于由您开发的程序,您可以在构建代码时创建调试信息。
- 对于从软件包安装的系统程序,您必须安装它们的 debuginfo 软件包。
3.2.2. 将 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 调用 GDB
- 使用 GDB 进行调试 - 4.7 调试已在运行的进程
3.2.3. 使用 GDB 逐步浏览程序代码
GDB 调试器附加到程序后,您可以使用一些命令来控制程序的执行。
先决条件
您必须有所需的调试信息:
- 程序使用调试信息编译和构建,或者
- 相关的 debuginfo 软件包已安装
- GDB 必须附加到要调试的程序
单步调试代码的 GDB 命令
r
(run)-
开始程序的执行。如果使用任何参数执行
run
,则这些参数将被传给可执行文件,如同程序已正常启动了一样。用户通常在设置断点后发出此命令。 开始
-
开始程序的执行,但会在程序主函数的开头停止。如果使用任何参数执行
start
,则这些参数将被传给可执行文件,就像程序已正常启动了一样。
c
(continue)从当前状态继续程序的执行。程序的执行将继续,直到以下其中一个条件变为 true:
- 达到了断点。
- 满足指定的条件。
- 程序收到了信号。
- 出现了错误。
- 程序终止了。
n
(next)从当前状态继续程序的执行,直到达到当前源文件中的下一行代码。程序的执行将继续,直到以下其中一个条件变为 true:
- 达到了断点。
- 满足指定的条件。
- 程序收到了信号。
- 出现了错误。
- 程序终止了。
s
(step)-
step
命令还会在当前源文件中的每一行代码处停止执行。但是,如果执行当前在包含 function call 的源行处停止,则 GDB 会在输入 function call (而不是执行它)后停止执行。 until
location- 继续执行,直到达到 location 选项指定的代码位置。
Fini
(finish)恢复程序的执行,并在执行从函数返回时停止。程序的执行将继续,直到以下其中一个条件变为 true:
- 达到了断点。
- 满足指定的条件。
- 程序收到了信号。
- 出现了错误。
- 程序终止了。
q
(quit)- 终止执行并退出 GDB。
其他资源
- 第 3.2.5 节 “使用 GDB 断点在定义的代码位置停止执行”
- 使用 GDB 进行调试 - 启动程序
- 使用 GDB 进行调试 - 继续和步进
3.2.4. 使用 GDB 显示程序内部值
显示程序内部变量的值对于了解程序正在做什么非常重要。GDB 提供了多个您可用来检查内部变量的命令。以下是这些命令中最有用的命令:
p
(print)显示给定的参数的值。通常,参数是任何复杂程度的变量的名称,从简单的单个值到结构。参数也可以是在当前语言中一个有效的表达式,包括程序变量和库函数的使用,或者在测试的程序中定义的函数。
可以使用 pretty-printer Python 或 Guile 脚本来扩展 GDB, 以便使用
print
命令来自定义对数据结构(如类、结构)的显示。bt
(backtrace)显示用于到达当前执行点的函数调用链,或者显示在执行终止之前所使用的函数链。这对于调查原因不明的严重 bug(如分段错误)非常有用。
向
backtrace
命令中添加full
选项也会显示本地变量。可以使用 frame filter Python 脚本来扩展 GDB,以便使用
bt
和info frame
命令自定义显示的数据。术语 frame 指的是与单个函数调用关联的数据。info
info
命令是提供关于各种条目的信息一个通用命令。它使用指定要描述的条目的一个选项。-
info args
命令显示当前所选帧的函数调用的选项。 -
info locals
命令显示当前选定的帧中的局部变量。
如需可能的条目列表,请在 GDB 会话中运行命令
help info
:(gdb) help info
-
l
(list)-
显示源代码中程序停止的行。此命令仅在程序执行停止时才可用。虽然严格来说不是用来显示内部状态的命令,但
list
帮助用户了解在程序执行的下一步中内部状态将发生哪些变化。
其他资源
- GDB Python API -红帽开发者博客条目
- 使用 GDB 进行调试 - Pretty Printing
3.2.5. 使用 GDB 断点在定义的代码位置停止执行
通常,仅调查代码的一小部分。断点是标记,告知 GDB 在代码的特定位置停止程序的执行。断点通常与源代码行关联。在这种情况下,放置断点需要指定源文件和行号。
放置断点 :
指定源代码 文件的名称以及该文件中 的行 :
(gdb) br file:line
如果 文件 不存在,则使用当前执行点的源文件名称:
(gdb) br line
或者,使用函数名称将 breakpoint 放置到其启动时:
(gdb) br function_name
在任务发生多次迭代后,程序可能会遇到错误。指定暂停执行的额外 条件 :
(gdb) br file:line if condition
使用 C 或 C++ 语言中的条件替换 条件。文件 和 行 的含义与上方相同。
要 检查 所有断点和监控点的状态:
(gdb) info br
要使用
info br
的输出中显示的 number 删除 断点:(gdb) delete number
要在给定位置 删除 断点:
(gdb) clear file:line
其他资源
- 使用 GDB 进行调试 - 断点、监视点和捕捉点
3.2.6. 使用 GDB 监视点停止在数据访问和更改时执行
在很多情况下,让程序执行直到某些数据改变了或被访问是有好处的。以下示例是最常见的用例。
先决条件
- 了解 GDB
在 GDB 中使用观察点
Watchpoints 是指示 GDB 停止执行程序的标记。Watchpoints 与数据相关联:放置观察点需要指定描述变量、多个变量或内存地址的表达式。
为数据 更改 放置 监视点(写入):
(gdb) watch expression
将 expression 替换为描述您要监视内容的表达式。对于变量,表达式 等于变量的名称。
为 数据访问 放置 监视点(读取):
(gdb) rwatch expression
为 任何 数据访问 放置 监视点(读写):
(gdb) awatch expression
要 检查 所有监视点和断点的状态:
(gdb) info br
删除 观察点:
(gdb) delete num
将 num 选项替换为
info br
命令报告的编号。
其他资源
- 使用 GDB 调试 - 设置 Watchpoints
3.2.7. 使用 GDB 调试或线程程序
些程序使用分叉或线程来实现并行代码执行。调试多个同步执行路径需要特殊考虑。
先决条件
- 您必须了解进程分叉和线程的概念。
使用 GDB 调试程序
分叉是一种程序(父)创建自身(子)的一个独立副本的情况。使用以下设置和命令来影响 GDB 在发生分叉时执行的操作:
following
-fork-mode 设置控制 GDB 是否紧跟
父项还是分叉后面的子级。设置 follow-fork-mode parent
- 分叉后,调试父进程。这是默认值。
设置 follow-fork-mode child
- 分叉后,调试子进程。
显示 follow-fork-mode
-
显示
follow-fork-mode
的当前设置。
set detach-on-fork
设置控制 GDB 是否保持对其他(未跟随)进程的控制,或使其保持运行。设置 detach-on-fork on
-
不遵循的进程(取决于
follow-fork-mode 的值)将被
分离并独立运行。这是默认值。 设置 detach-on-fork off
-
GDB 保持对这两个进程的控制。随后的进程(取决于
follow-fork-mode
的值)像往常一样被调试,而另一个则暂停。 显示 detach-on-fork
-
显示当前
detach-on-fork 的设置
。
使用 GDB 调试线程程序
GDB 能够调试各个线程,并且能够独立地操作和检查它们。要使 GDB 仅停止检查的线程,请使用命令 设置不停止并在其
上设置 target-async。
您可以将这些命令添加到 .gdbinit
文件。启用该功能后,GDB 已准备好执行线程调试。
GDB 使用 当前线程 的概念。默认情况下,命令仅应用于当前线程。
info 线程
-
显示
ID
和gid
编号的线程列表,指示当前的线程。 线程 ID
-
使用指定
id
作为当前线程设置线程。 线程应用 ids 命令
-
将
命令
应用到ids
列出的所有线程。ids
选项是空格分隔的线程 ID 列表。一个特殊值all
将命令应用到所有线程。 如果 条件中断 位置 线程 id
-
在特定位置设置断点,并且仅针对线程编号
ID
具有
特定条件
。 watch 表达式 线程 ID
-
设置仅用于线程编号
ID
的表达式
定义的观察点。 命令&
-
执行命令
并立即返回到 gdb 提示符(gdb)
,在后台继续执行任何代码。 中断
- 在后台暂停执行。
其他资源
- 使用 GDB 进行 调试 - 使用多个线程调试程序 4.10
- 使用 GDB 进行调试 - 4.11 Debugging Forks