https://blog.csdn.net/NickDeCode/article/details/133053291

1.什么是GDB

  • gdb是GNU开源组织发布的一个强大的Linux下的程序调试工具。

一般来说,GDB主要帮助你完成下面四个方面的功能:

  1. 启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
  2. 可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
  3. 当程序被停住时,可以检查此时你的程序中所发生的事。
  4. 你可以改变你的程序,将一个BUG产生的影响修正从而测试其他BUG。


  • 为何需要调试工具

程序中的错误主要分为 2 类,分别为语法错误和逻辑错误:

  1. 程序中的语法错误几乎都可以由编译器诊断出来,很容易就能发现并解决;
  2. 逻辑错误指的是代码思路或者设计上的缺陷,程序出现逻辑错误的症状是:代码能够编译通过,没有语法错误,但是运行结果不对。对于这类错误,只能靠我们自己去发现和纠正。

因此,程序中出现的语法错误可以借助编译器解决;但是逻辑错误只能借助调试工具对程序进行调试(Debug)

  • 常见调试工作

调试器名称

特点

Remote Debugger

Remote Debugger 是 VC/VS 自带的调试器,与整个IDE无缝衔接,使用非常方便。

WinDbg

大名鼎鼎的 Windows 下的调试器,它的功能甚至超越了 Remote Debugger,它还有一个命令行版本(cdb.exe),但是这个命令行版本的调试器指令比较复杂,不建议初学者使用。

LLDB

XCode 自带的调试器,Mac OS X 下开发必备调试器。

GDB

Linux 下使用最多的一款调试器,也有 Windows 的移植版。

  • GDB是什么

GDB 全称“GNU symbolic debugger”,从名称上不难看出,它诞生于 GNU 计划(同时诞生的还有 GCC、Emacs 等),是 Linux 下常用的程序调试器。发展至今,GDB 已经迭代了诸多个版本,当下的 GDB 支持调试多种编程语言编写的程序,包括 C、C++、Go、Objective-C、OpenCL、Ada 等。实际场景中,GDB 更常用来调试 C 和 C++ 程序。

Windows 操作系统中,人们更习惯使用一些已经集成好的开发环境(IDE),如 VS、VC、Dev-C++ 等,它们的内部已经嵌套了相应的调试器。

正如从事 Windows C/C++ 开发的一定要熟悉 Visual Studio、从事 Java 开发的要熟悉 Eclipse 或 IntelliJ IDEA、从事 Android 开发的要熟悉 Android Studio、从事 iOS 开发的要熟悉 XCode 一样,从事 Linux C/C++ 开发要熟悉 GDB。

另外,虽然 Linux 系统下读者编写 C/C++ 代码的 IDE 可以自由选择,但调试生成的 C/C++ 程序一定是直接或者间接使用 GDB。可以毫不夸张地说,我所做那些 C/C++ 项目的开发和调试包括故障排查都是利用 GDB 完成的,调试是开发流程中一个非常重要的环节,因此对于从事 Linux C/C++ 的开发人员熟练使用 GDB 调试是一项基本要求。

2.GDB的安装教程

基于 Linux 系统的免费、开源,衍生出了多个不同的 Linux 版本,比如 Redhat、CentOS、Ubuntu、Debian 等。这些 Linux 发行版中,有些默认安装有 GDB 调试器,但有些默认不安装。

判断当前 Linux 发行版是否安装有 GDB 的方法也很简单,就是在命令行窗口中执行 gdb -v 命令

parallels@ubuntu:~$ gdb -v
bash: gdb: command not found

如上所示,执行结果为“command not found”,表明当前系统中未安装 GDB 调试器。反之,若执行结果为:

parallels@ubuntu:~$ gdb -v
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

则表明当前系统安装了 GDB 调试器

3.GDB调试的对象

GDB主要来调试C/C++语言写的程序,当然也就可以调试其他语言程序。这里我们只说C/C++
GDB调试一定要是可执行文件而不是.c/.cpp文件
要用gcc进行编译,具体的命令如下:

gcc -g 源文件.c -o 输出的目标文件

-g,-o参数缺一不可

4. GDB调试C/C++程序

通过前面的学习,读者已经了解了什么是 GDB,以及如何下载并安装它。从本节开始,我们将正式学习使用 GDB 调试 C、C++ 程序。

如下是一段能够正常编译运行的 C 语言程序:

#include <stdio.h>

int main() {    
	unsigned long long int n, sum;    
	n = 1;    
	sum = 0;    
	
	while (n <= 100) {        
		sum = sum + n;        
		n = n + 1;    
	} 
    
	return 0;
}

使用GDB的前期准备

通过前面的学习我们知道,GDB 的主要功能就是监控程序的执行流程。这也就意味着,只有当源程序文件编译为可执行文件并执行时,GDB 才会派上用场。

Linux 发行版中,经常使用 GCC 编译 C、C++ 程序(有关 GCC 编译器,读者可猛击《GCC编译器》系统学习)。但需要注意的是,仅使用 gcc(或 g++)命令编译生成的可执行文件,是无法借助 GDB 进行调试的。

以 test.c 源文件为例,正常情况下,使用 GCC 编译该源代码的指令如下:

[root@DV-Test liqing]# gcc test.c -o test.exe
[root@DV-Test liqing]# ll
total 16
-rw-r--r-- 1 root root  133 Mar 25 14:33 test.c
-rwxr-xr-x 1 root root 8504 Mar 25 14:34 test.exe

可以看到,这里已经生成了 test.c 对应的执行文件 test.exe,但值得一提的是,此文件不支持使用 GDB 进行调试。原因很简单,使用 GDB 调试某个可执行文件,该文件中必须包含必要的调试信息(比如各行代码所在的行号、包含程序中所有变量名称的列表(又称为符号表)等),而上面生成的 main.exe 则没有。

那么,如何生成符合 GDB 调试要求的可执行文件呢?很简单,只需要使用 gcc -g 选项编译源文件,即可生成满足 GDB 要求的可执行文件。仍以 test.c 源程序文件为例:

[root@DV-Test liqing]# gcc test.c -o test.exe -g
[root@DV-Test liqing]# ll
total 16
-rw-r--r-- 1 root root  133 Mar 25 14:33 test.c
-rwxr-xr-x 1 root root 9568 Mar 25 14:41 test.exe

由此生成的 test.exe,即可使用 GDB 进行调试。

值得一提的是,GCC 编译器支持 -O(等于同 -O1,优化生成的目标文件)和 -g 一起参与编译。GCC 编译过程对进行优化的程度可分为 5 个等级,分别为 O0~O4,O0 表示不优化(默认选项),从 O1 ~ O4 优化级别越来越高,O4 最高。

所谓优化,例如省略掉代码中从未使用过的变量、直接将常量表达式用结果值代替等等,这些操作会缩减目标文件所包含的代码量,提高最终生成的可执行文件的运行效率。

而相对于 -O -g 选项,对 GDB 调试器更友好的是 -Og 选项,-Og 对代码所做的优化程序介于 O0 ~ O1 之间,真正可做到“在保持快速编译和良好调试体验的同时,提供较为合理的优化级别”。

解决了如何生成满足 GDB 调试器要求的可执行文件,接下来正式学习 GDB 调试器的使用。

启动GDB调试器

在生成包含调试信息的 main.exe 可执行文件的基础上,启动 GDB 调试器的指令如下:

[root@DV-Test liqing]# gdb test.exe
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-94.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/liqing/test.exe...done.
(gdb)

注意,该指令在启动 GDB 的同时,会打印出一堆免责条款。通过添加 --silent(或者 -q、–quiet)选项,可将比部分信息屏蔽掉:

[root@DV-DPS-AP01 liqing]# gdb test.exe --silent
Reading symbols from /home/liqing/test.exe...done.
(gdb)

无论使用以上哪种方式,最终都可以启动 GDB 调试器,启动成功的标志就是最终输出的(gdb)。通过在 (gdb) 后面输入指令,即可调用 GDB 调试进行对应的调试工作。

GDB 调试器提供有大量的调试选项,可满足大部分场景中调试代码的需要。如表 1 所示,罗列了几个最常用的调试指令及各自的作用:

调试指令

作 用

(gdb) break xxx

(gdb) b xxx

在源代码指定的某一行设置断点,其中 xxx 用于指定具体打断点的位置。

(gdb) run

(gdb) r

执行被调试的程序,其会自动在第一个断点处暂停执行。

(gdb) continue

(gdb) c

当程序在某一断点处停止运行后,使用该指令可以继续执行,直至遇到下一个断点或者程序结束。

(gdb) next

(gdb) n

令程序一行代码一行代码的执行。

(gdb) list

(gdb) l

显示源程序代码的内容,包括各行代码所在的行号。

(gdb) print xxx

(gdb) p xxx

令程序一行代码一行代码的执行。

(gdb) quit

(gdb) q

终止调试。

如上所示,每一个指令既可以使用全拼,也可以使用其首字母表示。另外,表 1 中罗列的指令仅是冰山一角,GDB 还提供有大量的选项,可以通过 help 选项来查看。有关 help 选项的具体用法,读者可阅读《GDB查看命令》一节,这里不再做具体赘述。

仍以 test.exe 可执行程序为例,接下来为读者演示表 1 中部分选项的功能和用法:

gdb调试工具_调试器

[root@DV-Test liqing]# gdb test.exe --silent
Reading symbols from /home/liqing/test.exe...done.
(gdb) l
1       #include <stdio.h>
2       int main(){
3
4       unsigned long long int n,sum;
5       n =1;
6       sum = 0;
7
8       while (n<=100) {
9       sum = sum +n;
10      n = n+1;
(gdb)    <-- 默认情况下,l 选项只显示 10 行源代码,如果查看后续代码,按Enter 回车即可    
11      }
12      return 0;
13      }
14
(gdb) b 8  <-- 在第8行源代码处打断点
Breakpoint 1 at 0x400501: file test.c, line 8.
(gdb) r     <-- 运行程序,遇到断点停止
Starting program: /home/liqing/test.exe

Breakpoint 1, main () at test.c:8
8       while (n<=100) {
Missing separate debuginfos, use: debuginfo-install glibc-2.17-157.el7.x86_64
(gdb) p n  <-- 查看代码中变量 n 的值
$1 = 1  <-- 当前 n 的值为 1,$1 表示该变量所在存储区的名称
(gdb) b 12  <-- 在程序第 12 行处打断点
Breakpoint 2 at 0x400517: file test.c, line 12.
(gdb) c   <-- 继续执行程序
Continuing.

Breakpoint 2, main () at test.c:12
12      return 0;
(gdb) p n  <-- 查看当前 n 变量的值
$2 = 101  <-- 当前 n 的值为 101
(gdb) p sum     <-- 查看当前 sum 变量的值
$3 = 5050  <-- 当前 sum 的值为 5050
(gdb) q    <-- 退出调试
A debugging session is active.

        Inferior 1 [process 4186] will be killed.

Quit anyway? (y or n) y  <-- 确实是否退出调试,y 为退出,n 为不退出

5. GDB常用的调试命令

(gdb)help:查看命令帮助,具体命令查询在gdb中输入help + 命令,简写h

(gdb)run:重新开始运行文件(run-text:加载文本文件,run-bin:加载二进制文件),简写r

(gdb)start:单步执行,运行程序,停在第一执行语句

(gdb)list:查看原代码(list-n,从第n行开始查看代码。list+ 函数名:查看具体函数),简写l

(gdb)set:设置变量的值

(gdb)next:单步调试(逐过程,函数直接执行),简写n

(gdb)step:单步调试(逐语句:跳入自定义函数内部执行),简写s

(gdb)backtrace:查看函数的调用的栈帧和层级关系,简写bt

(gdb)frame:切换函数的栈帧,简写f

(gdb)info:查看函数内部局部变量的数值,简写i

(gdb)finish:结束当前函数,返回到函数调用点

(gdb)continue:继续运行,简写c

(gdb)print:打印值及地址,简写p

(gdb)quit:退出gdb,简写q

(gdb)break+num:在第num行设置断点,简写b

(gdb)info breakpoints:查看当前设置的所有断点

(gdb)delete breakpoints num:删除第num个断点,简写d

(gdb)display:追踪查看具体变量值

(gdb)undisplay:取消追踪观察变量

(gdb)watch:被设置观察点的变量发生修改时,打印显示

(gdb)i watch:显示观察点

(gdb)enable breakpoints:启用断点

(gdb)disable breakpoints:禁用断点

(gdb)x:查看内存x/20xw 显示20个单元,16进制,4字节每单元

(gdb)run argv[1] argv[2]:调试时命令行传参