比GDB方便n倍的调试工具——CGDB
CGDB 是GDB
的前端,在终端窗口中意图形化的形式来调试代码(基于ncurse
),非常方便。相对于GDB
来说,可以很大的提高效率。
这篇文章就来分享一下CGDB
的最基本使用方法,如果是第一次听说,强烈建议您体验一下,一定会爱上它的!
有 bug 的示例代码
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>typedef 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 (argc=1, argv=0x7fffffffdc58) 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 (data=0x7fffffffdb40 "n\333\377\377\377\177", len=0x7fffffffdb60) 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 (argc=1, argv=0x7fffffffdc58) 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 (data=0x7fffffffdb40 "n\333\377\377\377\177", len=0x7fffffffdb60) 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物联网小镇
比GDB方便n倍的调试工具——CGDB相关推荐
- 性能工具之调试工具 GDB(你以为性能分析中用不到吗?)
文章目录 一.前言 二.环境依赖 三.Helloword 示例 四.调试 Redis 示例 1.下载 Redis 源码并解压 2.确认编译选项 3.检查编译 4.GDB 调用 redis-server ...
- linux子系统gdp调试,Linux系统中GDB功能汇总
在Linux系统操作中,GDB是一款程序调试工具,且拥有多种功能,下面小编将针对GDB的功能给大家做个详细介绍,以便你对GDB有个详细的了解. 或许,各位比较喜欢那种图形界面方式的,像VC.BCB等I ...
- 【Linux】基础开发工具的简单使用——yum/vim/gcc/gdb/make/git
文章目录 yum -- 软件包管理器 yum list -- 查看软件包 yum install -- 安装软件 yum remove -- 卸载软件 vim -- 文本编辑器 正常模式 底行模式 v ...
- gdb 如何调用函数?
在这周,我发现我可以从 gdb 上调用 C 函数.这看起来很酷,因为在过去我认为 gdb 最多只是一个只读调试工具. 我对 gdb 能够调用函数感到很吃惊.正如往常所做的那样,我在 Twitter 上 ...
- linux内核调试指南
Hunnad的专栏 * 条新通知 * 登录 * 注册 * 欢迎 * 退出 * 我的博客 * 配置 * 写文章 * 文章管理 * 博客首页 * * * * 空间 * 博客 * 好友 * 相册 * 留言 ...
- linux内核调试指南 1
大海里的鱼有很多,而我们需要的是鱼钩一只 一些前言 作者前言 知识从哪里来 为什么撰写本文档 为什么需要汇编级调试 ***第一部分:基础知识*** 总纲:内核世界的陷阱 源码阅读的陷阱 代码调试的陷阱 ...
- linux 内核调试指南
大海里的鱼有很多,而我们需要的是鱼钩一只 本文档由大家一起自由编写,修改和扩充,sniper负责维护.引用外来的文章要注明作者和来处.本文档所有命令都是在ubuntu/debian下的操作.选取的内核 ...
- Linux Kernel - Debug Guide (Linux内核调试指南 )
linux内核调试指南 一些前言 作者前言 知识从哪里来 为什么撰写本文档 为什么需要汇编级调试 ***第一部分:基础知识*** 总纲:内核世界的陷阱 源码阅读的陷阱 代码调试的陷阱 原理理解的陷阱 ...
- Golang 汇编入门知识总结
作者:ivansli,腾讯 IEG 运营开发工程师 在深入学习 Golang 的 runtime 和标准库实现的时候发现,如果对 Golang 汇编没有一定了解的话,很难深入了解其底层实现机制.在这里 ...
最新文章
- web服务器(IIS)的操作步骤
- C# 参考之访问关键字:base、this
- c 添加mysql表单的一行数据类型_MySQL数据库基础
- 【Python CheckiO 题解】House Password
- JSTL标签使用说明
- Win10 Build9926 更新问题解决
- ios 添加导航栏视图_iOS进度栏(进度视图)
- Android中关于Task的一些认识
- Nutch1.2二次开发详细攻略(一)【图文】------Windows平台下Cygwin环境的搭建
- FP-growth算法原理解析
- GB35114---基于pjsip协议库开发问题
- 钉钉考勤报表生成工具
- 【鸿蒙】鸿蒙App应用-《记账软件》开发步骤
- 2020年运营版双端直播盒子APP带引导安装 QQ微信一键登录+多级分销+粉色系列
- adb模拟器安装xposed
- 台式机电源相关参数说明
- 华为朗读屏幕怎么关闭
- IT数据中心第三方运维服务市场概况
- 【2021-03-17】JS逆向之某实时票房榜数据解密
- matlab数组保存envi,MATLAB 读取envi img格式
热门文章
- 细数垃圾邮箱客户端 Live Mail 的BUG
- 【建造者设计模式详解】Java/JS/Go/Python/TS不同语言实现
- 在iis服务上发布asp.net网站操作步骤,以及iis注册以及简单配置
- 基于android的流动人口管理移动APP-计算机毕业设计
- java为什么要实例化?实例化的对象和引用...
- 各路由协议的协议号_离婚协议范本!
- 程序员接单必看的五个平台,最后一个赚麻了
- word里面表格显示不全解决办法,随手记
- 考导游证需要什么条件?报考导游领队需要什么条件?
- AttributeError: 'dict' object has no attribute 'iteritems'