做网站建设价格,抖音自媒体平台注册入口,中国建设银行对公网站首页,建立自己网站免费CGDB 是GDB的前端#xff0c;在终端窗口中意图形化的形式来调试代码(基于ncurse)#xff0c;非常方便。相对于GDB来说#xff0c;可以很大的提高效率。这篇文章就来分享一下CGDB的最基本使用方法#xff0c;如果是第一次听说#xff0c;强烈建议您体验一下#xff0c;一定…CGDB 是GDB的前端在终端窗口中意图形化的形式来调试代码(基于ncurse)非常方便。相对于GDB来说可以很大的提高效率。这篇文章就来分享一下CGDB的最基本使用方法如果是第一次听说强烈建议您体验一下一定会爱上它的有 bug 的示例代码#include unistd.h
#include stdlib.h
#include stdio.h
#include string.h
#include assert.htypedef struct USER_DATA{char data[32];unsigned short data_len;unsigned int flag;
}__attribute((packed))__;const unsigned char * g_data hello;/*
功能加载一段数据
参数1: data[OUT]: 数据被加载的缓冲区
参数2: len [OUT]实际被加载的数据的长度
返回值: 0-成功else-失败
*/
static int get_data(unsigned char *data, unsigned int *len)
{assert(data len);memcpy((void *)data, (void *)g_data, strlen(g_data));*len strlen(g_data);return 0;
}int main(int argc, char *argv[])
{// 创建结构体变量struct USER_DATA user_data;user_data.flag 0xA5;// 往结构体变量中加载数据if (0 get_data(user_data.data, user_data.data_len)){printf(get_data ok! \n);printf(data_len %d, data %s \n, user_data.data_len, user_data.data);printf(user_data.flag 0x%x \n, user_data.flag); // 期望值0xA5}else{printf(get_data failed! \n);}return 0;
}在编译之前先看一下代码你能发现其中的bug吗当然了在编译的时候编译器以Warning的方式给出了风险提示。因为示例代码很简单所以很容易发现。但是在一个项目中如果不喜欢消除编译Warning警告的话这个bug还是比较隐蔽的。编译测试代码gcc -g test.c -o test因为要使用GDB调试所以别忘了加上-g选项。GDB 调试操作$ gdb ./test
(gdb) r // 直接全速执行一次
(gdb) r
Starting program: /home/captain/demos_2022/cgdb/test
test start...
get_data ok!
data_len 5, data hello
user_data.flag 0x0
[Inferior 1 (process 9933) exited normally]发现user_data.flag的值不对决定在调用get_data之前的那行下一个断点然后从头开始执行查看代码行号(gdb) l main
18 *len strlen(g_data);
19 return 0;
20 }
21
22 int main(int argc, char *argv[])
23 {
24 struct USER_DATA user_data;
25 user_data.flag 0xA5;
26 if (0 get_data(user_data.data, user_data.data_len))
27 {下断点在25行(gdb) b 25
Breakpoint 1 at 0x400771: file test.c, line 25.开始运行(gdb) r
Starting program: /home/captain/demos_2022/cgdb/test Breakpoint 1, main (argc1, argv0x7fffffffdc58) at test.c:25
25 user_data.flag 0xA5;在断点处停了下来此时该赋值语句还没有执行所以先单步执行一次(gdb) step
26 if (0 get_data(user_data.data, user_data.data_len))此时打印一下这个变量user_data.flag的值和地址因为待会进入被调用函数这个变量就不可见了所以需要通过地址来打印。(gdb) print user_data.flag
$1 (unsigned int *) 0x7fffffffdb62
(gdb) print/x user_data.flag
$2 0xa5此时赋值是正确的再接着往下执行进入被调用函数get_data()了(gdb) step
get_data (data0x7fffffffdb40 n\333\377\377\377\177, len0x7fffffffdb60) at test.c:16
16 assert(data len);这个函数一共就4行代码我们每单步执行一句就打印一下user_data.flag变量的内容。单步执行下一行memcpy处并且看一下user_data.flag变量地址处的内容是否仍然为0xa5:(gdb) step
17 memcpy((void *)data, (void *)g_data, strlen(g_data));
(gdb) print/x *0x7fffffffdb62
$3 0xa5继续单步执行(因为不需要跟进memcpy、strlen的内部所以使用next命令)并打印(gdb) next
18 *len strlen(g_data); // 这一句即将被执行
(gdb) print/x *0x7fffffffdb62
$4 0xa5
(gdb) next
19 return 0;
(gdb) print/x *0x7fffffffdb62
$5 0x0发现问题了在执行*len strlen(g_data)语句之后变量user_data.flag地址中的内容就被改变了。再仔细检查一下代码就可以诊断出是数据类型使用错了。解决bug: get_data()函数的最后一个参数应该是unsigned short型指针才正确。问题是解决了但是回过头来看一下gdb的调试过程还是比较繁琐的调试指令和代码显示夹杂在一起需要敲很多指令。CGDB 调试操作启动CGDB之后终端窗口被评分为上下两部分上面是代码窗口下面是调试窗口。按下ESC键进入代码窗口此时可以上下浏览代码并且可以进行一系列的操作空格键设置或者取消断点;o查看代码所在的文件;/ 或者 ?在代码中搜索字符串;。。。还有很多方便的快捷键-缩小代码窗口;扩大代码窗口;gg: 光标移动到文件头部;GG光标移动到文件尾部;ctrl b代码向上翻一页;ctrl u代码向上翻半页;ctrl f代码向下翻一页;ctrl d代码向下翻半页;按下i键回到调试窗口进入调试模式使用的调试指令与GDB几乎一样也就是说可以在实时查看代码的情况下进行调试操作大大提高了效率。我们按照上面GDB的调试过程走一遍按下ESC键进入代码窗口此时代码前面的行号如果是白色的表示所在的当前行。按下j键向下移动高亮的当前行。当移动到25行时如下按下空格键表示在此行设置一个断点此时行号变成红色的并且在调试窗口打印一行信息:(gdb)
Breakpoint 1 at 0x400771: file test.c, line 25.按下i键回到调试操作窗口然后输入运行指令r会在第25行停下来的如下绿色的箭头所示当然了调试窗口也会打印出相关信息(gdb) r
Starting program: /home/captain/demos_2022/cgdb/test Breakpoint 1, main (argc1, argv0x7fffffffdc58) at test.c:25单步step执行这条赋值语句然后打印一下user_data.flag的值和地址(gdb) print/x user_data.flag
1: /x user_data.flag 0xa5
(gdb) print user_data.flag
2: user_data.flag (unsigned int *) 0x7fffffffdb62此时赋值语句正确执行打印的值也是符合预期的。再执行单步指令进入函数get_data()内部(gdb) step
get_data (data0x7fffffffdb40 n\333\377\377\377\177, len0x7fffffffdb60) at test.c:16此时上面的代码窗口自动进入get_data()相关的代码如下所示继续单步在执行赋值语句*len strlen(g_data);之前打印一下变量user_data.flag地址中的内容(gdb) print/x *0x7fffffffdb62
$2 0xa5正确然后执行赋值语句之后再次打印(gdb) next
(gdb) print/x *0x7fffffffdb62
$3 0x0发现问题在执行*len strlen(g_data)语句之后变量user_data.flag地址中的内容就被改变了。小结CGDB的操作过程虽然我写的比较啰嗦但是实际使用起来真的是非常的丝滑就像巧克力一样------ End ------既然看到这里了如果觉得不错请您随手点个【赞】和【在看】吧!转自微信公众号IOT物联网小镇