Linux —— 基础开发工具5

目录

[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:删除所有断点)

n/next:逐过程

s/step:逐语句

[display i(变量名):进行常显示](#display i(变量名):进行常显示)

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

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

continue/c:直到运行结束或者运行到下一个断点处

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 屏
相关推荐
oMcLin2 小时前
如何在SUSE Linux Enterprise Server 15 SP4上通过配置并优化ZFS存储池,提升文件存储与数据备份的效率?
java·linux·运维
charlie1145141913 小时前
嵌入式的现代C++教程——constexpr与设计技巧
开发语言·c++·笔记·单片机·学习·算法·嵌入式
SelectDB3 小时前
驾驭 CPU 与编译器:Apache Doris 实现极致性能的底层逻辑
运维·数据库·apache
❀͜͡傀儡师3 小时前
docker部署Arcane容器可视化管理平台
运维·docker·容器
老姚---老姚3 小时前
docker常用命令
运维·docker·容器
深圳安锐科技有限公司3 小时前
边坡倾斜自动化监测 倾角仪 如何通过安锐云查看监测曲线?
运维·视觉检测·实时监测·自动化监测·结构健康监测·倾斜角度监测·倾角传感器
清木铎4 小时前
leetcode_day4_筑基期_《绝境求生》
算法
清木铎4 小时前
leetcode_day10_筑基期_《绝境求生》
算法
j_jiajia4 小时前
(一)人工智能算法之监督学习——KNN
人工智能·学习·算法