本文内容
- GDB 调试器基础
- GDB 使用示例
- 总结
无论你作为程序员经验多么丰富,你开发的任何软件都不可能完全没有 bug。因此,找出并修复 bug 是软件开发周期中最重要的任务之一。虽然查找 bug 的方法有很多(测试、代码自查等),但也存在一种特殊软件——称为调试器(debugger)——能帮你准确了解问题所在,从而轻松修复。
假设你是一名 C/C++ 程序员,或者使用 Fortran 和 Modula-2 编程语言开发软件,那么你会很高兴地了解到,有一个优秀的调试器——称为 GDB——可以用来轻松检查代码中的错误及其他问题。在本文中,我们将讨论 GDB 的基础知识及其一些有用的特性/选项。
GDB 调试器基础
通俗地说,GDB 让你能够在程序执行时窥探程序内部,从而帮助你准确找出问题所在。我们将在下一节通过一个实际示例来讨论 GDB 调试器的用法,但在此之前,我们先介绍一些对你后续操作有帮助的基本要点。
首先,要成功使用像 GDB 这样的调试器,你必须编译程序,使编译器同时生成调试器所需的调试信息。例如,对于 gcc 编译器(本教程后面将用它来编译示例 C 程序),你需要在编译代码时使用 -g 命令行选项。
要了解 gcc 编译器手册页中关于该命令行选项的内容,请前往此处查看。
下一步,确认你的系统中已安装 GDB。如果尚未安装,并且你使用的是基于 Debian 的系统(如 Ubuntu),可以使用以下命令轻松安装该工具:
sudo apt install gdb
若要在其他发行版上安装,请前往此处查看。
现在,一旦你以可调试的方式编译了程序,并且系统中已安装 GDB,就可以使用以下命令以调试模式运行程序:
gdb [prog-executable-name]
虽然这会启动 GDB 调试器,但此时你的程序可执行文件并不会被运行。这正是你可以定义调试相关设置的时机。例如,你可以设置一个 breakpoint(断点),告诉 GDB 在某个特定的行号或函数处暂停程序执行。
继续,要启动程序,你需要执行以下 gdb 命令:
run
值得一提的是,如果你的程序需要传入一些命令行参数,可以在此处指定。例如:
run [arguments]
GDB 提供了许多在调试时非常有用的命令。我们将在下一节的示例中讨论其中一些。
GDB 使用示例
现在我们对 GDB 及其用法有了基本的认识。那么让我们通过一个示例来应用这些知识。以下是示例代码:
#include <stdio.h>
int main()
{
int out = 0, tot = 0, cnt = 0;
int val[] = {5, 54, 76, 91, 35, 27, 45, 15, 99, 0};
while(cnt < 10)
{
out = val[cnt];
tot = tot + 0xffffffff/out;
cnt++;
}
printf("\n Total = [%d]\n", tot);
return 0;
}
简单来说,这段代码的作用是:取出 'val' 数组中的每个值,将其赋给 'out' 整型变量,然后通过将该变量的前一个值与 '0xffffffff/out' 的结果相加来计算 'tot'。
这里的问题是,当运行该代码时,会产生以下错误:
$ ./gdb-test
Floating point exception (core dumped)
因此,要调试这段代码,第一步是使用 -g 选项编译程序。命令如下:
gcc -g -Wall gdb-test.c -o gdb-test
接下来,运行 GDB 并告知它我们要调试的可执行文件。命令如下:
gdb ./gdb-test
现在,我遇到的错误是 'floating point exception'(浮点异常),正如你们大多数人可能已经知道的,它是由 n % x 在 x 为 0 时引起的。因此,考虑到这一点,我在进行除法运算的第 11 行设置了一个断点。操作方式如下:
(gdb)
break 11
注意,'(gdb)' 是调试器的提示符。我只需输入 'break' 命令。
现在,我让 GDB 开始执行程序:
run
于是,当断点第一次被命中时,GDB 在输出中显示了以下内容:
Breakpoint 1, main () at gdb-test.c:11 11 tot = tot + 0xffffffff/out; (gdb)
如上方输出所示,调试器显示了设置断点所在的行。现在,让我们打印 'out' 的当前值。方式如下:
(gdb)
print out
$1 = 5
(gdb)
如你所见,打印的值是 '5'。所以目前一切正常。我让调试器继续执行程序,直到下一个断点,这可以通过 'c' 命令完成。
c
我不断重复这一操作,直到发现 'out' 的值为零。
... ... ... Breakpoint 1, main () at gdb-test.c:11 11 tot = tot + 0xffffffff/out; (gdb) print out $2 = 99 (gdb) c Continuing. Breakpoint 1, main () at gdb-test.c:11 11 tot = tot + 0xffffffff/out; (gdb) print out $3 = 0 (gdb)
现在,为了确认这就是确切的问题,我这次使用了 GDB 的 's'(即 'step')命令,而不是 'c'。我只是想让程序执行当前暂停所在的第 11 行,看看是否会在该处发生崩溃。
发生了以下情况:
(gdb) s Program received signal SIGFPE, Arithmetic exception. 0x080484aa in main () at gdb-test.c:11 11 tot = tot + 0xffffffff/out;
是的,正如上方高亮输出所确认的,异常正是在此处抛出的。当我再次尝试运行 's' 命令时,得到了最终的确认:
(gdb) s Program terminated with signal SIGFPE, Arithmetic exception. The program no longer exists.
通过这种方式,你就可以使用 GDB 调试你的程序了。
总结
我们在这里仅仅触及了皮毛,因为 GDB 提供了许多供用户探索和使用功能。请阅读 GDB 的 man 手册页,以了解更多关于该工具的信息,并在调试代码中的任何问题时尝试使用它。这个调试器有一定的学习曲线,但付出努力是值得的。