GDB 调试

GDB 是一个功能强大的调试器,也是 Linux 系统中默认的调试器。GDB 主要提供以下四种功能,这些功能可以方便我们定位程序的 BUG。

  • 启动程序
  • 设置断点
  • 检查程序运行状态,例如查看变量的值
  • 修改程序运行状态,例如修改变量的值

本文简单讲述 GDB 的调试方法。

我们需要调试的程序如下所示,该程序只是为了演示 GDB 的用法,并没有实际的意义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>

using namespace std;

double f1(int a, double d)
{
int b = (a + 5) * 2;
double ret = d * b;

return ret;
}


double f2(float f)
{
int a = 10;
double d = 3.14;
double ret = f * f1(a, d);

return ret;
}


int main() {
cout << "begin to gdb test" << endl;
float f = 1.1;
double ret = f2(f);
cout << ret << endl;

return 0;
}

我们先对上面的程序进行编译,编译命令为:

g++ -g -o hello hello.cpp

  • -g 选项的含义是生成源代码的调试信息,方便我们在调试过程中查看源代码
  • -o 选项可以指定生成的可执行程序名称

本文的重点不在于介绍 gcc 或者 g++ 编译器的使用,有关 gcc 和 g++ 的使用,请参考其使用手册。

编译好程序后,我们就可以使用 GDB 来调试程序了。

gdb hello

进入 GDB 的提示符界面,如果需要退出,可以使用 quit 命令即可。

(gdb) quit

一、设置断点

使用 break 命令可以设置程序的断点,例如,我们在程序的第 7 行设置一个断点。

(gdb) break 7
Breakpoint 1 at 0x400860: file hello.cpp, line 7.

如果当前需要调试的程序由多个源文件组成,则可以在设置断点时指定文件的名称,例如,下面的命令设置 hello.cpp 源文件中的第 8 行为断点。

(gdb) break hello.cpp:8
Breakpoint 2 at 0x40086b: file hello.cpp, line 8.

如果需要查看断点的信息,可以使用 info break 命令:

(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000400860 in f1(int, double) at hello.cpp:7
2 breakpoint keep y 0x000000000040086b in f1(int, double) at hello.cpp:8

可以看到,程序设置了两个断点,分别是程序第 7 行和第 8 行。

如果需要删除断点,可以使用 clear 命令。

设置好断点后,就可以运行程序了,使用 run 命令运行程序。

(gdb) run
Starting program: /home/lihao/code/cpp/test/hello
begin to gdb test
Breakpoint 1, f1 (a=10, d=3.1400000000000001) at hello.cpp:7
7 int b = (a + 5) * 2;

可以看到程序运行到第 7 行即断点处,停了下来。

二、查看程序运行状态

如果需要查看执行的源代码,可以使用 list 或者 l (小写字母 L)命令:

(gdb) l
2
3 using namespace std;
4
5 double f1(int a, double d)
6 {
7 int b = (a + 5) * 2;
8 double ret = d * b;
9
10 return ret;
11 }

可以看到,GDB 把当前程序执行位置前面和后面的代码都展示了出来。如果需要继续查看下面的代码,可以继续按下回车键。

为了打印变量的值,可以使用 print 命令或者 p 命令:

1
2
3
4
5
6
(gdb) p a
$3 = 10
(gdb) p d
$4 = 3.1400000000000001
(gdb) print a
$5 = 10

如果变量为字符串类型,且值的长度很长,为了完整输出字符串的内容,可以使用以下命令设置打印变量时显示完整的字符串:

set print element 0

如果需要查看程序运行的堆栈,可以使用 bt 命令:

1
2
3
4
(gdb) bt
#0 f1 (a=10, d=3.1400000000000001) at hello.cpp:7
#1 0x00000000004008c7 in f2 (f=1.10000002) at hello.cpp:18
#2 0x0000000000400916 in main () at hello.cpp:27

可以看到,hello 程序的调用链为 main -> f2 -> f1。

三、执行程序

为了便于演示,我们先将所有的断点删除,只保留第 7 行的断点。
如果需要在断点处继续执行程序,可以使用 step 命令或者 s 命令, step 命令的含义是逐语句执行。

(gdb) step
8 double ret = d * b;

程序继续执行到了第 8 行。
step 命令是逐语句执行,遇到子函数的情况,会进入子函数中逐语句继续执行。
next 命令也是逐语句执行,与 step 命令不同的是,遇到子函数,不会进入子函数中执行,只会把子函数当成一条语句执行。

(gdb) next
10 return ret;

程序执行过程中,如果需要让程序一直执行到下一个断点,可以使用 continue 命令或者 c 命令。

(gdb) c
Continuing.
103.62
Program exited normally.

本文只讲述 GDB 调试中涉及的最常用的几个命令,更详细和复杂的使用,可以参考 GDB 的说明文档。

附:如何解决 GDB 运行时,提示 Missing separate debuginfos 异常的问题。
解决步骤

  1. 修改 /etc/yum.repos.d/CentOS-Debuginfo.repo 文件,设置 enable=1
  2. 安装 glibc,yum install glibc
  3. 安装 yum-utils, yum install yum-utils
  4. debuginfo-install glibc-2.12-1.209.el6_9.2.x86_64 libgcc-4.4.7-18.el6.x86_64 libstdc++-4.4.7-18.el6.x86_64

参考资料

  1. http://www.gnu.org/software/gdb/
  2. Linux 程序设计,第四版,人民邮电出版社,Neil Matthew 等著