目录
[7.调试器 --- gdb/cgdb使用](#7.调试器 — gdb/cgdb使用)
[7.1 样例代码](#7.1 样例代码)
[7.2 预备](#7.2 预备)
[7.3 常见使用](#7.3 常见使用)
[list/l 函数名:l main](#list/l 函数名:l main)
[list/l mycmd.c:main](#list/l mycmd.c:main)
[list/l 0:](#list/l 0:)
[break/b 函数名:行号 等价于 break/b 函数名,相当于在函数的开头打断点](#break/b 函数名:行号 等价于 break/b 函数名,相当于在函数的开头打断点)
[break/b [文件名:]行号](#break/b [文件名:]行号)
[用 info b 来查看,此时的断点有以下:](#用 info b 来查看,此时的断点有以下:)
[delete/d + 断点编号(n):](#delete/d + 断点编号(n):)
[delete/d breakpoints:删除所有断点](#delete/d breakpoints:删除所有断点)
[display i(变量名):进行常显示](#display i(变量名):进行常显示)
[对上面的变量或者地址不想查了,可以输入 undisplay + 编号:](#对上面的变量或者地址不想查了,可以输入 undisplay + 编号:)
[直接输入语句:until 12(输入until 11 也可以,因为11行没有代码)](#直接输入语句:until 12(输入until 11 也可以,因为11行没有代码))
finish:一个断点也不打,进入一个函数内部,输入finish,以函数为单位,进行区域式代码的执行。
[info i:查看当前正在debug的程序信息](#info i:查看当前正在debug的程序信息)
[info local:查看临时变量,用的并不是很多](#info local:查看临时变量,用的并不是很多)
[7.4 常见技巧 ------ 加餐](#7.4 常见技巧 —— 加餐)
[7.4.1 watch](#7.4.1 watch)
[7.4.2 set var 确定问题原因](#7.4.2 set var 确定问题原因)
[7.4.3 条件断点](#7.4.3 条件断点)
[7.4.3.1 添加条件断点](#7.4.3.1 添加条件断点)
[7.4.3.2 给已经存在的断点新增条件](#7.4.3.2 给已经存在的断点新增条件)
7.调试器 --- gdb/cgdb使用
7.1 样例代码
cpp
// mycmd.c
#include <stdio.h>
int Sum(int s, int e)
{
int result = 0;
for (int i = s; i <= e; i++)
{
result += i;
}
return result;
}
int main()
{
int start = 1;
int end = 100;
printf("I will begin\n");
int n = Sum(start, end);
printf("running done, result is: [%d-%d]=%d\n", start, end, n);
return 0;
}
编写文件Makefile:
cpp
mycmd:mycmd.c
gcc-o $@ $^ -g
.PHONY:clean
clean:
rm -f mycmd

1-100的相加结果运行起来是5050
readelf -S mycmd :可以读取二进制文件的结构

readelf -S mycmd | grep -i debug:内部是包含了调试信息的

7.2 预备
程序的发布方式有两种:debug 模式和 release 模式 ,Linux gcc/g++ 出来的二进制程序,默认是 release 模式。
要使用 gdb 调试,必须在源代码生成二进制程序的时候,加上 -g 选项,如果没有添加,程序无法被编译。
$ gcc mycmd.c -o mycmd # 默认模式,不⽀持调试
$ file mycmd
mycmd: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically
linked, interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=82f5cbaada10a9987d9f325384861a88d278b160, for GNU/Linux
3.2.0, not stripped
$ gcc mycmd.c -o mycmd -g # debug模式
$ file mycmd
mycmd: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically
linked, interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=3d5a2317809ef86c7827e9199cfefa622e3c187f, for GNU/Linux
3.2.0, with debug_info, not stripped
7.3 常见使用
开始: gdb binFinle
退出:ctrl + d 或 quit 调试命令
|------------------------|------------------------|--------------------------|
| 命令 | 作用 | 样例 |
| list/l | 显示源代码,从上次的位置开始,每次列出10行 | list/l 10 |
| list/l 函数名 | 列出指定函数的源代码 | list/l main |
| list/l 文件名:行号 | 列出指定文件的源代码 | list/l mycmd.c:1 |
| r/run | 从程序开始连续执行 | run |
| n/next | 单步执行,不进入函数内部,逐过程F10 | next |
| s/step | 单步执行,进入函数内部,逐语句 F11 | step |
| break/b [文件名:]行号 | 在指定行号设置断点 | break 10 break test.c:10 |
| break/b 函数名 | 在函数开头设置断点 | break main |
| info break/b | 查看当前所有断点的信息 | info break |
| finish | 执行到当前函数返回,然后停止 | finish |
| print/p 表达式 | 打印表达式的值 | print start+end |
| p 变量 | 打印指定变量的值 | p x |
| set var 变量 = 值 | 修改变量的值 | set var i=10 |
| continue/c | 从当前位置开始连续执行程序 | continue |
| delete/d breakpoints | 删除所有断点 | delete breakpoints |
| delete/d breakpoints n | 删除序号为n的断点 | delete breakpoints 1 |
| disable breakpoints | 禁用所有的断点 | disable breakpoints |
| enable breakpoints | 启用所有的断点 | enable breakpoints |
| info/i breakpoints | 查看当前设置的断点列表 | info breakpoints |
| display 变量名 | 跟踪显示指定变量的值(每次停止时) | display x |
| undisplay 编号 | 取消对指定编号的变量的跟踪显示 | undisplay 1 |
| until X行号 | 执行到指定行号 | until 20 |
| backtrace/bt | 查看当前执行站的各级函数调用及参数 | backtrace |
| info/i locals | 查看当前栈帧的局部变量值 | info locals |
| quit | 退出GDB调试器 | quit |
实际操作:
gdb + 文件名 or cgdb +文件名(推荐后者,更方便观察)
cgdb mycmd:
cgdb:启动 CGDB 调试器
mycmd:要调试的可执行文件
list/l 函数名:l main

list/l mycmd.c:main

list/l 0:

break/b 函数名:行号 等价于 **break/b 函数名,**相当于在函数的开头打断点

break/b [文件名:]行号

用 info b 来查看,此时的断点有以下:

删除断点删除的是前面的编号而不是对应的行号:
delete/d + 断点编号(n):



delete/d breakpoints:删除所有断点

在输入 r 之后,按回车之前,打开另一个终端,并输入查看系统进程的指令 ps axj | grep gdb:

输入 r 之后并按回车:

输入info b,会多出一条消息:
此断点已经被命中了一次:
在输入 r 之后,按回车之后,在另一个终端,并输入查看系统进程的指令 ps axj | grep mycmd:

删除所有的断点再次运行:r,会有什么结果呢?

相当于vs2022的 F5:

n/next:逐过程

s/step:逐语句

想要在gdb中做到像vs2022一样的监视窗口,可以输入 p + 变量名 or p + &变量名

但是上面的变量地址只有我们手动输入才会显示,如何做到像vs2022一样,对变量进行自动常显示呢?
display i(变量名):进行常显示

同样对 result 变量和 i 的地址,也可以进行常显示:

对上面的变量或者地址不想查了,可以输入 undisplay + 编号:

此时的代码一直在sum函数里面进行循环,如何跳出呢?

直接输入语句:until 12(输入until 11 也可以,因为11行没有代码)

此时result的结果就是5050了。
continue/c:直到运行结束或者运行到下一个断点处

finish:一个断点也不打,进入一个函数内部,输入finish,以函数为单位,进行区域式代码的执行。

info i:查看当前正在debug的程序信息

info local:查看临时变量,用的并不是很多

禁用断点:



颜色变为黄色表示为禁用:

以上的些许操作可以同 visual studio2022 的操作进行对比:


7.4 常见技巧 ------ 加餐
安装cgdb:
上面的基本调还是麻烦,虽然是黑屏,但是还是想看到代码调试
推荐安装cgdb:
Ubuntu:sudo apt-get install -y cgdb
Centos:sudo yum install -y cgdb
7.4.1 watch
执行时监视一个表达式(如变量)的值。如果监视的表达式在程序运行期间的值发生变化,GDB会暂停程序的执行,并通知使用者


注意:
- 如果你有一些变量不应该修改,但是你怀疑他修改导致了问题,你可以watch它,如果变化了,就会通知你。
- 监视变量的变化
7.4.2 set var 确定问题原因
更改一下标志位,假设我们想得到+-result

r/run运行结果为0:

发现result是变化的,直接until16行

此时的 result 的结果是对的,此时检查一下flag的值

原来是flag的问题,有可能不是导致result为0的原因,需要进一步的检测。
直接将flag的值改为1:set var flag=1(不是在源代码中对flag的值进行修改)

上述操作是在debug期间找到问题并在debug期间解决问题,后续要做的就是在源代码中改flag的值。

7.4.3 条件断点
7.4.3.1 添加条件断点
条件断点:条件+断点,给某种断点添加条件,条件不满足时该断点不触发,条件满足时才会满足我们的条件断点
(gdb) l main
11
12 return result;
13 }
14
15 int main ()
16 {
17 int start = 1 ;
18 int end = 100 ;
19 printf ( "I will begin\n" );
20 int n = Sum (start, end);
(gdb) b 20
Breakpoint 1 at 0x11c3 : file mycmd.c, line 20.
(gdb) r
Starting program: /home/whb/test/test/mycmd
I will begin
Breakpoint 1 , main () at mycmd.c: 20
20 int n = Sum (start, end);
(gdb) s
Sum (s= 32767 , e= -7136 ) at mycmd.c: 5
5 {
(gdb) n
6 int result = 0 ;
(gdb) n
7 for ( int i = s; i <= e; i++)
(gdb) n
9 result += i;
(gdb) display i
1 : i = 1
(gdb) n
7 for ( int i = s; i <= e; i++)
1 : i = 1
(gdb) n
9 result += i;
1 : i = 2
(gdb) n
7 for ( int i = s; i <= e; i++)
1 : i = 2
(gdb) n
9 result += i;
1 : i = 3
(gdb)
7 for ( int i = s; i <= e; i++)
1 : i = 3
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c: 20
breakpoint already hit 1 time
(gdb) b 9 if i == 30 # 9 是⾏号,表⽰新增断点的位置
Breakpoint 2 at 0x555555555186 : file mycmd.c, line 9.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c: 20
breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555555186 in Sum at mycmd.c: 9
stop only if i == 30
(gdb) finish
Run till exit from # 0 Sum (s= 1 , e= 100 ) at mycmd.c: 7
Breakpoint 2 , Sum (s= 1 , e= 100 ) at mycmd.c: 9
9 result += i;
1 : i = 30
(gdb) finish
Run till exit from # 0 Sum (s= 1 , e= 100 ) at mycmd.c: 9
0x00005555555551d2 in main () at mycmd.c: 20
20 int n = Sum (start, end);
Value returned is $ 1 = 5050


7.4.3.2 给已经存在的断点新增条件
(gdb) l main
11
12 return result;
13 }
14
15 int main ()
16 {
17 int start = 1 ;
18 int end = 100 ;
19 printf ( "I will begin\n" );
20 int n = Sum (start, end);
(gdb) b 20
Breakpoint 1 at 0x11c3 : file mycmd.c, line 20.
(gdb) r
Starting program: /home/whb/test/test/mycmd
I will begin
Breakpoint 1 , main () at mycmd.c: 20
20 int n = Sum (start, end);
(gdb) s
Sum (s= 32767 , e= -7136 ) at mycmd.c: 5
5 {
(gdb) n
6 int result = 0 ;
(gdb) n
7 for ( int i = s; i <= e; i++)
(gdb) n
9 result += i;
(gdb)
7 for ( int i = s; i <= e; i++)
(gdb)
9 result += i;
(gdb)
7 for ( int i = s; i <= e; i++)
(gdb)
9 result += i;
(gdb)
7 for ( int i = s; i <= e; i++)
(gdb) b 9 # 我们在第 9 ⾏新增⼀个断点,⽤来开始测试
Breakpoint 2 at 0x555555555186 : file mycmd.c, line 9.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c: 20
breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555555186 in Sum at mycmd.c: 9
(gdb) n
Breakpoint 2 , Sum (s= 1 , e= 100 ) at mycmd.c: 9
9 result += i;
(gdb) n
7 for ( int i = s; i <= e; i++)
(gdb) n
Breakpoint 2 , Sum (s= 1 , e= 100 ) at mycmd.c: 9
9 result += i;
(gdb) condition 2 i== 30 # 给 2 号断点,新增条件 i== 30
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c: 20
breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555555186 in Sum at mycmd.c: 9
stop only if i== 30
breakpoint already hit 2 times
(gdb) n
7 for ( int i = s; i <= e; i++)
(gdb) n
9 result += i;
(gdb) c
Continuing.
Breakpoint 2 , Sum (s= 1 , e= 100 ) at mycmd.c: 9
9 result += i;
(gdb) p i
1 = 30 (gdb) p result 2 = 435




注意:
- 条件断点添加常见两种方式:1.新增 2.给已有断点的追加
- 注意两者的语法有区别,不要写错了。
- 新增:b 行号/文件名:行号/函数名 if i == 30(条件)
- 给已有断点追加:condition 2 i == 30,其中 2 是已有的断点编号,没有if
- cgdb 分屏操作ESC进入代码屏,i 回到 gdb 屏