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
$ gdb program
使用程序的文件名或路径替换 program。
设置 GDB 以开始程序的执行。您可以使用 run 命令在开始执行进程前设置断点和 gdb 环境。
将 GDB 附加到已在运行的进程
要将 GDB 附加到已作为进程运行的程序:
使用
ps命令找到进程 ID(pid):ps -C program -o pid h
$ ps -C program -o pid h pidCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用程序的文件名或路径替换 program。
将 GDB 附加到此进程:
gdb -p pid
$ gdb -p pidCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用
ps输出中的实际进程 ID 号替换 pid 。
将已在运行的 GDB 附加到已在运行的进程
要将已在运行的 GDB 附加到已在运行的程序:
使用
shellGDB 命令运行ps命令,并找到程序的进程 ID(pid):(gdb) shell ps -C program -o pid h pid
(gdb) shell ps -C program -o pid h pidCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用程序的文件名或路径替换 program。
使用
attach命令将 GDB 附加到程序:(gdb) attach pid
(gdb) attach pidCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用
ps输出中的实际进程 ID 号替换 pid。
在某些情况下,GDB 可能无法找到对应的可执行文件。使用 file 命令指定路径:
(gdb) file path/to/program
(gdb) file path/to/program
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 (而不是执行它)后停止执行。 untillocation- 继续执行,直到达到 location 选项指定的代码位置。
Fini(finish)恢复程序的执行,并在执行从函数返回时停止。程序的执行将继续,直到以下其中一个条件变为 true:
- 达到了断点。
- 满足指定的条件。
- 程序收到了信号。
- 出现了错误。
- 程序终止了。
q(quit)- 终止执行并退出 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 指的是与单个函数调用关联的数据。infoinfo命令是提供关于各种条目的信息一个通用命令。它使用指定要描述的条目的一个选项。-
info args命令显示当前所选帧的函数调用的选项。 -
info locals命令显示当前选定的帧中的局部变量。
如需可能的条目列表,请在 GDB 会话中运行命令
help info:(gdb) help info
(gdb) help infoCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
l(list)-
显示源代码中程序停止的行。此命令仅在程序执行停止时才可用。虽然严格来说不是用来显示内部状态的命令,但
list帮助用户了解在程序执行的下一步中内部状态将发生哪些变化。
3.2.5. 使用 GDB 断点在定义的代码位置停止执行 复制链接链接已复制到粘贴板!
通常,仅调查代码的一小部分。断点是标记,告知 GDB 在代码的特定位置停止程序的执行。断点通常与源代码行关联。在这种情况下,放置断点需要指定源文件和行号。
放置断点 :
指定源代码 文件的名称以及该文件中 的行 :
(gdb) br file:line
(gdb) br file:lineCopy to Clipboard Copied! Toggle word wrap Toggle overflow 如果 文件 不存在,则使用当前执行点的源文件名称:
(gdb) br line
(gdb) br lineCopy to Clipboard Copied! Toggle word wrap Toggle overflow 或者,使用函数名称将 breakpoint 放置到其启动时:
(gdb) br function_name
(gdb) br function_nameCopy to Clipboard Copied! Toggle word wrap Toggle overflow
在任务发生多次迭代后,程序可能会遇到错误。指定暂停执行的额外 条件 :
(gdb) br file:line if condition
(gdb) br file:line if conditionCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用 C 或 C++ 语言中的条件替换 条件。文件 和 行 的含义与上方相同。
要 检查 所有断点和监控点的状态:
(gdb) info br
(gdb) info brCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要使用
info br的输出中显示的 number 删除 断点:(gdb) delete number
(gdb) delete numberCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要在给定位置 删除 断点:
(gdb) clear file:line
(gdb) clear file:lineCopy to Clipboard Copied! Toggle word wrap Toggle overflow
3.2.6. 使用 GDB 监视点停止在数据访问和更改时执行 复制链接链接已复制到粘贴板!
在很多情况下,让程序执行直到某些数据改变了或被访问是有好处的。以下示例是最常见的用例。
先决条件
- 了解 GDB
在 GDB 中使用观察点
Watchpoints 是指示 GDB 停止执行程序的标记。Watchpoints 与数据相关联:放置观察点需要指定描述变量、多个变量或内存地址的表达式。
为数据 更改 放置 监视点(写入):
(gdb) watch expression
(gdb) watch expressionCopy to Clipboard Copied! Toggle word wrap Toggle overflow 将 expression 替换为描述您要监视内容的表达式。对于变量,表达式 等于变量的名称。
为 数据访问 放置 监视点(读取):
(gdb) rwatch expression
(gdb) rwatch expressionCopy to Clipboard Copied! Toggle word wrap Toggle overflow 为 任何 数据访问 放置 监视点(读写):
(gdb) awatch expression
(gdb) awatch expressionCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要 检查 所有监视点和断点的状态:
(gdb) info br
(gdb) info brCopy to Clipboard Copied! Toggle word wrap Toggle overflow 删除 观察点:
(gdb) delete num
(gdb) delete numCopy to Clipboard Copied! Toggle word wrap Toggle overflow 将 num 选项替换为
info br命令报告的编号。
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),在后台继续执行任何代码。 中断- 在后台暂停执行。