??gdb是最为通用的,普遍linux会自带gdb工具,使用简单,无额外需求.
??gdbgui需要额外安装,且会占用处理器资源.
??cmake-tools是使用vscode远程ssh设备时在vscode上安装的一个插件,必须用cmake组织管理代码且使用vscode时才可以使用该工具借助于vscode图形化界面进行debug,但相当占用处理器资源.
下面只介绍gdb具体使用方法.
安装方式.
sudo apt-get install gdb
使用方法
// 无参程序调用
gdb ./<exce>
// 有参程序调用
gdb ./<exce> <param>
exce即为代码编译出来的可执行文件.
正常执行后会出现如下界面.
注意,这个时候程序并没有开始运行.
如果程序带有入参,则
运行常用相关指令见下表.
命令 | 简写形式 | 说明 |
---|---|---|
run | r | 开始执行程序直到遇到 结束或者遇到断点等待下一个命令; |
start | st | 开始执行程序,在main函数中的第一条语句前停下 |
continue | c | 继续程序的运行,直到遇到下一个断点 |
next | n | 执行下一条语句,如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句) |
step | s | 执行下一条语句,如果该语句为函数调用,则进入函数执行第一条语句 |
finish | 直接执行完当前函数,返回到调用该函数的位置 | |
quit | q | 推出gdb调试环境 |
该部分比较简单,且都比较常用,不再复述.
命令 | 简写形式 | 说明 |
---|---|---|
set args | 设定运行时的参数 | |
show args | 查看设定的运行参数 |
gdb支持如下几种断点,且断点可以在程序运行前设置.
观察点是运行中设置,而且只能是变量。
命令 | 简写形式 | 说明 |
---|---|---|
break | b | 设置断点 |
break if | b if | 条件断点,满足if后的条件后停止 |
tbreak | 临时断点,只生效一次 |
断点有如下几种设置方法.
// 在指定文件的指定行号设定断点
b <file_name>:<file_line>
// 在指定文件的指定函数设定断点
b <file_name>:<fun_name>
// 根据条件设定断点
b <file_name>:<file_line> if <cond> 例如:x==0
临时断点和上述用法一样.
命令 | 简写形式 | 说明 |
---|---|---|
info b | 显示当前所有断点 | |
d break | 删除指定断点 | |
delete | d | 删除所有断点 |
disable b | 禁用指定断点 | |
enable b | 使能制定断点 |
想要禁用或者删除断点,需要先知道当前共有哪些断点.如下所示.
从左到右,分别是断点号,类型,使能状态,后面是断点具体位置.
禁用断点示例如下.对应的使能断点不在演示.
可以看到禁用断点2后,后面的Enb
变为了n
.代表断点被禁用不生效,但依旧存在.
使能断点,删除所有断点如下所示.
删除指定断点示例如下.
观察点是当变量变化即停止的一种调试手段.
命令 | 简写形式 | 说明 |
---|---|---|
info b | 利用此方式也可以查看watch信息,也可以使用info watch | |
watch | 只有当被监控变量(表达式)的值发生改变,程序才会停止运行 | |
rwatch | 只要程序中出现读取目标变量(表达式)的值的操作,程序就会停止运行 | |
awatch | 只要程序中出现读取目标变量(表达式)的值或者改变值的操作,程序就会停止运行 |
cond可以是变量也可以是表达式.
我只用过变量.以变量说明
struct test{
char name[16];
uint32_t age;
};
struct test data = {0};
struct *p = &test_data;
watch data
当data里任意一个数据发生改变即刻停止
watch data.age
当data内的age发生改变时停止
watch *p
同watch data
watch p
当p指向内容发生变化时即刻停止
watch
设定观察点的方式有两种,默认为1.
以RK3568举例,实际使用中发现最多只能建立2个硬件观察点,后续在使用watch后,会出现如下提示.
Hardware watchpoint num: Could not insert watchpoint
使用如下指令强制GDB调试器建立软件观察点.
set can-use-hw-watchpoints 0
awatch 和 rwatch 命令只能设置硬件观察点,如果系统不支持或者借助如上命令禁用,则 GDB 调试器会打印如下信息:
Expression cannot be implemented with read/access watchpoint.
备注:软件观察点会导致程序执行效率变低
命令 | 简写形式 | 说明 |
---|---|---|
set print elements | 设定打印长度 | |
show print elements | 显示打印长度 | |
set print pretty on | 打开换行打印 |
打印信息过长时,信息显示不全,可以使用set print elements
进行设定显示长度.
打印结构体时,默认不换行,使用set print pretty on
可以将结构体成员变量换行显示如下所示.
其中红框内的是默认打印方式,下面的是打开换行打印后的显示效果.
命令 | 简写形式 | 说明 |
---|---|---|
print/ | p | 打印数据 |
ptype <var_name> | 查看变量数据类型 |
简单介绍如下几种简单的使用方式.
int a = 1;
int *p_a = &a;
char name[32] = "name";
p a 打印变量a值
p p_a 打印p_a存储的地址,即a的地址
p *p_a 打印p_a存储地址的所存储的数据
p name 打印name中的字符串值
p name[<idx>]@<len> 打印数组name从idx索引处len长度的数据
p ++a 打印++a的值,此时程序内a的值也将发生变化
p <fun(<param>)> 将参数传入函数中,直接调用函数并打印返回值
如果想要数据按照指定的方式打印,则需要用到参数.参数常用值
fmt | 功能 |
---|---|
/x | 十六进制的形式打印 |
/d | 有符号,十进制形式打印证书 |
/u | 无符号,十进制形式打印证书 |
/o | 八进制打印 |
/t | 二进制打印 |
/f | 浮点数打印 |
/c | 字符打印 |
/s | 字符串打印 |
如下所示.
p/x a 以十六进制形式打印a变量
p/s name 以字符串形式打印name数组存储的数据
p/x name[0]@10 以十六进制形式挨个打印name数组从0到len-1索引的数据
如果当前断点在C文件,此时想查看A文件的非局部变量的值,可以通过如下方式.
p <file_name>::<var_name>
可通过下列命令打印变量的类型
ptype <var_name>
上述print
指令需要用户每次都输入才会执行打印功能.如果想要程序一停止就打印数据可以使用display
功能.
命令 | 简写形式 | 说明 |
---|---|---|
display | ||
info display | 查看自动显示的信息,包含信息编号 | |
disable display | 失能自动输出,num代表信息编号 num可以为多个,比如 disable display 2 3 4 也可以为一个范围,比如 disable display 2-4 | |
enable display | 使能自动输出,num代表信息编号 num可以为多个,比如 enable display 2 3 4 也可以为一个范围,比如 enable display 2-4 | |
delete display | d display | 删除输出, num代表信息编号,同undisplay <num> num可以为多个,比如 disable display 2 3 4 也可以为一个范围,比如 disable display 2-4 |
display
所有用法同print
基本一致.
命令 | 简写形式 | 说明 |
---|---|---|
x/<n/f/u> | 按照nfu三个参数的配置打印addr出的数据 |
其中,n、f、u的含义如下.
名称 | 含义 |
---|---|
n | 正整数,从addr开始,打印n个长度的数据 |
f | 打印形式,o是8进制,u是无符号10进制,t是二进制,c是字符,s是字符串等,参考print打印形式 |
u | 表示从当前地址往后请求的字节数,即一个长度的单位,默认为4字节,b表示单字节,h表示双字节,w表示四字 节,g表示八字节。 |
x/16xb
: 打印0地址处16个长度单位的数据,每个单位长度为一个字节,共计16个字节数据以16形式打印.