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 附加到已作为进程运行的程序中:

  1. 使用 ps 命令查找进程 ID(pid):

    $ ps -C program -o pid h
     pid

    使用到程序的文件名或路径替换 program

  2. 将 GDB 附加到此过程:

    $ gdb -p pid

    使用 ps 输出中的实际进程 ID 编号替换 pid

将已在运行的 GDB 附加到已经运行的进程

将已在运行的 GDB 附加到已经运行的程序中:

  1. 使用 shell GDB 命令运行 ps 命令,并查找程序的进程 ID(pid):

    (gdb) shell ps -C program -o pid h
     pid

    使用到程序的文件名或路径替换 program

  2. 使用 attach 命令将 GDB 附加到程序:

    (gdb) attach pid

    使用 ps 输出中的实际进程 ID 编号替换 pid

注意

在某些情况下,GDB 可能无法找到对应的可执行文件。使用 file 命令指定路径:

(gdb) file path/to/program

其他资源

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,以使用 btinfo 框架命令对显示的数据进行自定义显示。术语 帧(frame) 指的是与单个功能调用关联的数据。

info

info 命令是提供有关各种项目的通用命令。它取指定要描述的项目的选项。

  • info args 命令显示当前选择帧的功能调用选项。
  • info locals 命令在当前选定的框中显示本地变量。

如需可能的项目列表,请在 GDB 会话中运行命令 help info

(gdb) help info
l (list)
显示程序停止的源代码中的行。此命令仅在程序执行停止时可用。虽然不是严格显示内部状态的命令,但 list 有助于用户了解执行程序在下一步中将发生对内部状态的更改。

其他资源

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++ 语言条件替换 conditionfileline 如以上是相同的。

  • 检查所有断点和监视点的状态:

    (gdb) info br
  • 使用 info br 输出中的数字删除断点:

    (gdb) delete number
  • 删除给定位置的断点:

    (gdb) clear file:line

其他资源

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 命令报告的编号。

其他资源

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 onset target-async on您可以将这些命令添加到 .gdbinit 文件中。在打开该功能后,GDB 已准备好进行线程调试。

GDB 使用 当前线程 的概念。默认情况下,命令仅应用到当前的线程。

info 线程
显示带有相应 idgid 的线程列表,代表当前的线程。
线程 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
在后台停止执行。

其他资源