Back

gdb代码调试工具

GDB是是 Linux 平台下最常用的一款程序调试器,通常服务于终端下,无GUI。Linux下很多IDE的调试能力源于GDB调试器

1. Linux下安装二进制版GDB调试器

centos下命令:

sudo yum -y install gdb

Ubuntu下安装命令:

sudo apt -y install gdb

2. 源码安装下载:

sudo wget https://ftp.gnu.org/gnu/gdb/gdb-13.2.tar.xz

解压:

tar -zxvf gdb-13.2.tar.xz

进入源码目录安装:

./configure
make
sudo make install

3. 调试准备

GDB supports the following languages (in alphabetical order):

  • Ada
  • Assembly
  • C
  • C++
  • D
  • Fortran
  • Go
  • Objective-C
  • OpenCL
  • Modula-2
  • Pascal
  • Rust

只是为了调试代码需要在编译时加入:-g参数,关掉编译器优化参数:-O0,打开所有 warning:-Wall

4. GDB的启动与退出

启动gdb调试 gdb 可执行程序文件名

(gdb) gdb app

退出调试

quit = q
(gdb) q

命令行传参:show args

设置的时机: 启动gdb之后, 在应用程序启动之前

(gdb) set args 参数1 参数2 …. …

查看设置的命令行参数

(gdb) show args

例子: 非gdb调试命令行传参

argc 参数总个数,argv[0] == ./app argv[1] == "11"  argv[2] == "22" argv[3] == "33"  argv[4] == "44" argv[5] == "55"
./app 11 22 33 44 55

使用 gdb 调试

gdb app
(gdb) set args 11 22 33 44 55

查看设置的命令行参数

(gdb) show args
Argument list to give program being debugged when it is started is "11 22 33 44 55".

gdb 中启动程序

run: 缩写为 r,停在第一个断点的位置,如果没有设置断点,程序就执行完了 start: 启动程序,阻塞在 main 函数的第一行,等待输入后续其它 gdb 指令

例子:

># 两种方式
# 方式1: run == r
(gdb) run         (如果有断点会停在断点处)
# 方式2: start    (会停在main函数处)
(gdb) start

继续运行:

# quit == q
(gdb) qui

5. 查看代码

查看代码的命令叫做 list 可以缩写为 l,此命令可以通过行号,函数名查看,也可以查看不同文件的信息

如果不切换文件会停在main函数所在文件

当前文件

从第一行开始显示
(gdb) l


默认只显示10行内容,上下文
(gdb) l 行号

显示这个函数的上下文内容, 默认显示10行
(gdb) l 函数名

切换文件

切到指定文件行号处,默认显示10行
(gdb) l 文件名:行号


切到指定文件函数处,默认显示10行
(gdb) l 文件名:函数名

设置默认显示的行数

# 以下两个命令中的 listsize 都可以写成 list
(gdb) set listsize 行数

# 查看当前list一次显示的行数
(gdb) show listsize

6.断点操作

设置断点

断点设置有两个方式:常规断点:程序只要运行到这个位置就会被阻塞

条件断点:条件被满足了程序才会在断点处阻塞。

当前文件:
# 在当前文件的某一行上设置断点
# break == b
(gdb) b 行号
(gdb) b 函数名		# 停止在函数的第一行
非当前文件:
# 在非当前文件的某一行上设置断点
(gdb) b 文件名:行号
(gdb) b 文件名:函数名		# 停止在函数的第一行
设置条件断点
# 必须要满足某个条件, 程序才会停在这个断点的位置上
# 通常情况下, 在循环中条件断点用的比较多
(gdb) b 行数 if 变量名==某个值

查看断点

# 查看设置的断点信息
(gdb) i b   #info break

# 举例
(gdb) i b
Num     Type           Disp Enb Address            	  What
1       breakpoint     keep y   0x0000000000400cb5 in main() at test.cpp:12
2       breakpoint     keep y   0x0000000000400cbd in main() at test.cpp:13
3       breakpoint     keep y   0x0000000000400cec in main() at test.cpp:18
4       breakpoint     keep y   0x00000000004009a5 in insertionSort(int*, int) 
                                                   at insert.cpp:8
5       breakpoint     keep y   0x0000000000400cdd in main() at test.cpp:16
6       breakpoint     keep y   0x00000000004009e5 in insertionSort(int*, int) 
                                                   at insert.cpp:16

需要关注的点:

  • Num: 断点的编号,删除断点或者设置断点状态的时候都需要使用
  • Enb: 当前断点的状态,y 表示断点可用,n 表示断点不可用
  • What: 描述断点被设置在了哪个文件的哪一行或者哪个函数上

删除断点

# 需要 info b 查看断点的信息, 第一列就是编号
(gdb) d 断点的编号1 [断点编号2 ...]
# 举例: 
(gdb) d 1          # 删除第1个断点
(gdb) d 2 4 6      # 删除第2,4,6个断点

# 删除一个范围, 断点编号 num1 - numN 是一个连续区间
(gdb) d num1-numN

# 举例, 删除第1到第5个断点
(gdb) d 1-5

设置断点状态

断点无效化

dis 断点编号 dis 断点1编号-断点n编号 断点无效化例子:

# 查看断点信息
(gdb) i b
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x0000000000400cce in main() at test.cpp:14
4       breakpoint     keep y   0x0000000000400cdd in main() at test.cpp:16
5       breakpoint     keep y   0x0000000000400d46 in main() at test.cpp:23
6       breakpoint     keep y   0x0000000000400d4e in main() at test.cpp:25
7       breakpoint     keep y   0x0000000000400d6e in main() at test.cpp:28
8       breakpoint     keep y   0x0000000000400d7d in main() at test.cpp:30

# 设置第2, 第4 个断点无效
(gdb) dis 2 4

# 查看断点信息
(gdb) i b
Num     Type           Disp Enb Address            What
2       breakpoint     keep n   0x0000000000400cce in main() at test.cpp:14
4       breakpoint     keep n   0x0000000000400cdd in main() at test.cpp:16
5       breakpoint     keep y   0x0000000000400d46 in main() at test.cpp:23
6       breakpoint     keep y   0x0000000000400d4e in main() at test.cpp:25
7       breakpoint     keep y   0x0000000000400d6e in main() at test.cpp:28
8       breakpoint     keep y   0x0000000000400d7d in main() at test.cpp:30

# 设置 第5,6,7,8个 断点无效
(gdb) dis 5-8

# 查看断点信息
(gdb) i b
Num     Type           Disp Enb Address            What
2       breakpoint     keep n   0x0000000000400cce in main() at test.cpp:14
4       breakpoint     keep n   0x0000000000400cdd in main() at test.cpp:16
5       breakpoint     keep n   0x0000000000400d46 in main() at test.cpp:23
6       breakpoint     keep n   0x0000000000400d4e in main() at test.cpp:25
7       breakpoint     keep n   0x0000000000400d6e in main() at test.cpp:28
8       breakpoint     keep n   0x0000000000400d7d in main() at test.cpp:30
让无效的断点生效
# enable == ena
# 设置某一个或者某几个断点有效
(gdb) ena 断点1的编号 [断点2的编号 ...]

# 设置某个区间断点有效
(gdb) ena 断点1编号-断点n编号

有效化例子:

# 查看断点信息
(gdb) i b
Num     Type           Disp Enb Address            What
2       breakpoint     keep n   0x0000000000400cce in main() at test.cpp:14
4       breakpoint     keep n   0x0000000000400cdd in main() at test.cpp:16
5       breakpoint     keep n   0x0000000000400d46 in main() at test.cpp:23
6       breakpoint     keep n   0x0000000000400d4e in main() at test.cpp:25
7       breakpoint     keep n   0x0000000000400d6e in main() at test.cpp:28
8       breakpoint     keep n   0x0000000000400d7d in main() at test.cpp:30

# 设置第2, 第4个断点有效
(gdb) ena 2 4

# 查看断点信息
(gdb) i b
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x0000000000400cce in main() at test.cpp:14
4       breakpoint     keep y   0x0000000000400cdd in main() at test.cpp:16
5       breakpoint     keep n   0x0000000000400d46 in main() at test.cpp:23
6       breakpoint     keep n   0x0000000000400d4e in main() at test.cpp:25
7       breakpoint     keep n   0x0000000000400d6e in main() at test.cpp:28
8       breakpoint     keep n   0x0000000000400d7d in main() at test.cpp:30

# 设置第5,6,7个断点有效
(gdb) ena 5-7

# 查看断点信息
(gdb) i b
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x0000000000400cce in main() at test.cpp:14
4       breakpoint     keep y   0x0000000000400cdd in main() at test.cpp:16
5       breakpoint     keep y   0x0000000000400d46 in main() at test.cpp:23
6       breakpoint     keep y   0x0000000000400d4e in main() at test.cpp:25
7       breakpoint     keep y   0x0000000000400d6e in main() at test.cpp:28
8       breakpoint     keep n   0x0000000000400d7d in main() at test.cpp:30

7.调试命令

继续运行 gdb

# cont = continue
(gdb) cont = continue

手动打印信息

当程序被某个断点阻塞之后,可以通过一些命令打印变量的名字或者变量的类型,并且还可以跟踪打印某个变量的值。

打印变量值

print 命令的语法格式如下:

# print == p
(gdb) p 变量名

# 如果变量是一个整形, 默认对应的值是以10进制格式输出, 其他格式请参考上表
(gdb) p/fmt 变量名

例子:

# 举例
(gdb) p i       # 10进制
$5 = 3
(gdb) p/x i     # 16进制
$6 = 0x3
(gdb) p/o i     # 8进制
$7 = 03

打印变量类型

查看某个变量的类型

(gdb) ptype 变量名

例子:

# 打印变量类型
(gdb) ptype i
type = int
(gdb) ptype array[i]
type = int
(gdb) ptype array
type = int [12]

自动打印信息

设置变量名自动显示

频繁查看某个变量或表达式的值从而观察它的变化情况时,使用 display 命令可以一劳永逸。display 命令没有缩写形式

# 在变量的有效取值范围内, 自动打印变量的值(设置一次, 以后就会自动显示)
(gdb) display 变量名
# 以指定的整形格式打印变量的值, 关于 fmt 的取值, 请参考 print 命令

(gdb) display/fmt 变量名
查看自动显示列表
# info == i
(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
1:   y  i
2:   y  array[i]
3:   y  /x array[i]

在展示出的信息中,每个列的含义如下:

  • Num : 变量或表达式的编号,GDB 调试器为每个变量或表达式都分配有唯一的编号
  • Enb : 表示当前变量(表达式)是处于激活状态还是禁用状态,如果处于激活状态(用 y 表示),则每次程序停止执行,该变量的值都会被打印出来;反之,如果处于禁用状态(用 n 表示),则该变量(表达式)的值不会被打印。
  • Expression :被自动打印值的变量或表达式的名字。

取消自动显示

对于不需要再打印值的变量或表达式,可以将其删除或者禁用。

# 命令中的 num 是通过 info display 得到的编号, 编号可以是一个或者多个
(gdb) undisplay num [num1 ...]
# num1 - numN 表示一个范围
(gdb) undisplay num1-numN

(gdb) delete display num [num1 ...]
(gdb) delete display num1-numN</code></pre>

例子:

# 查看显示列表
(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
1:   y  i
2:   y  array&#091;i]
3:   y  /x array&#091;i]

# 删除变量显示, 需要使用 info display 得到的变量/表达式编号

(gdb) undisplay 1 2

# 查看显示列表, 只剩下一个了

(gdb) i display
Auto-display expressions now in effect:
Num Enb Expression
3:   y  /x array&#091;i]

禁用自动显示列表中处于激活状态下的变量或表达式

# 命令中的 num 是通过 info display 得到的编号, 编号可以是一个或者多个
(gdb) disable display num &#091;num1 ...]
# num1 - numN 表示一个范围
(gdb) disable display num1-numN

当需要启用自动显示列表中被禁用的变量或表达式时,可以使用下边的命令

# 命令中的 num 是通过 info display 得到的编号, 编号可以是一个或者多个
(gdb) enable  display num &#091;num1 ...]
# num1 - numN 表示一个范围
(gdb) disable display num1-numN

单步调试

当程序阻塞到某个断点上之后,可以通过以下命令对程序进行单步调试:

step

step 命令可以缩写为 s, 命令被执行一次代码被向下执行一行,如果这一行是一个函数调用,那么程序会进入到函数体内部。

# 从当前代码行位置, 一次调试当前行下的每一行代码
# step == s
# 如果这一行是函数调用, 执行这个命令, 就可以进入到函数体的内部
(gdb) step
finish

作用:跳出函数体,函数体内不能有有效断点

# 如果通过 s 单步调试进入到函数内部, 想要跳出这个函数体
(gdb) finish
next

作用:和step相似,但不会进入函数体内部

# next == n
# 如果这一行是函数调用, 执行这个命令, 不会进入到函数体的内部
(gdb) next
until

until作用:直接跳出某个循环体 循环体内部不能有有效的断点,必须要在循环体的开始 / 结束行执行该命令

(gdb) until

设置变量值

set var 变量名=值

设置某个变量的值

Licensed under CC BY-NC-SA 4.0