首页 > 其他分享 >【转载】GDB高级技巧:边Debug边修复BUG,无需修改代码,无需重新编译

【转载】GDB高级技巧:边Debug边修复BUG,无需修改代码,无需重新编译

时间:2024-06-10 13:00:17浏览次数:14  
标签:无需 修改 bubble 命令 GDB Debug 断点 BUG

调试是每个程序员都逃不过的宿命!

程序调试是一件非常考验耐心的事情,因为调试过程中经常会需要反复的修改源码,重新编译、重新部署、重新运行,这个过程通常是非常枯燥和繁琐的。尤其对于大型项目,光是编译可能需要几十分钟,甚至几个小时,部署过程则可能更为复杂漫长!

那么,有没有一种更高效的调试手段,可以避免反复修改代码和编译呢?

这个真的有!本文将介绍一种调试技巧,可以一边调试,一边修复Bug,能够在不修改代码、不重新编译的前提下修复BUG,并且验证解决方案,大幅提高调试效率!

先看下最终效果吧!

本文预期效果

如下图,冒泡排序中,有三个常见的BUG:

img

图中已经把三个BUG都标注了出来。编译运行,结果如下:

img

GDB中执行时:

img

不管是正常方式执行,还是在GDB中执行,程序都异常终止,无法得到正常结果。

但是,利用本文介绍的调试技巧,可以利用GDB给这个程序制作一个“热补丁”,在不修改代码、不重新编译的前提下,解决掉程序中的三个BUG,让程序正常执行,并得到预期结果!

最终效果,如下图所示:

img

所有的黑魔法,都在这个补丁文件bubble.fix中!

是不是很有趣呢?下面开始介绍!

关于GDB

我之前写了几篇文章,专门介绍GDB的一些非常实用却鲜为人知的高阶用法,感兴趣的小伙伴可以去翻看下调试系列专题文章。

GDB的基本用法,相信大家都很熟悉了,就不过多介绍了,直接讲重点吧!

Breakpoint Command Lists

GDB支持在断点触发后,自动执行用户预设的一组调试命令。使用方法:

commands [bp_id...]
  command-list
end

其中:

  • commands是GDB内置关键字。
  • bp_idi(info)命令显示出来的断点ID,可以指定多个,也可以不指定。不指定时,默认只对最近一次设置的那个断点有效。
  • command-list是用户预设的一组命令,当bp_id指定的断点被触发时,GDB会自动执行这些命令。
  • end表示结束。

简单来说,就是当bp_id所表示的断点被触发时,GDB会自动执行command-list中所指定的命令。

这个功能适用于各种类型的断点,如breakpoint、watchpoint、catchpoint等。

适用场景举例

利用GDB的breakpoint commands lists这个特性可以做很多有趣的事情,本文仅列举其中的几个。

随时随地printf,不需修改代码和重新编译

我之前写过一篇文章,详细介绍过GDB的动态打印(Dynamic Printf)功能,可以用dprintf命令在代码的任意地方设置动态打印断点,并自动进行格式化打印。相当于在不修改代码,不重新编译的情况下,可以让你随意添加printf打印日志信息。

利用GDB的breakpoint commands lists,可以实现一样的功能,而且除了格式化打印之外,还可以做其它更多的操作,比如dump内存,dump寄存器等。

修改程序执行逻辑

在GDB中可以做很多有趣的事情,比如修改变量、修改寄存器、调用函数等。结合breakpoint command list功能,可以在调试的同时,修改程序执行逻辑,给程序打上"热补丁"。从而可以在调试过程中,快速修复Bug和验证解决方案,避免重新修改代码和重新编译,大大提高程序调试的效率!

这也是本文重点讲解的场景,稍后会演示如何利用这个功能,在调试的过程中,不修改代码,就能修复掉上文冒泡排序程序中的三个Bug。

进行自动化调试,提高调试效率

很多童鞋可能不知道,GDB支持非常强大的脚本功能,除了GDB自己特定的脚本外,它甚至还支持Python脚本!

有了breakpoint commands lists功能,结合GDB支持的脚本功能,以及自定义命令功能,甚至可以实现调试自动化。

其他还有很多非常有趣且实用的功能场景,限于篇幅,不再展开,有机会再写文章专门介绍吧!

接下来,正式开始解决冒泡排序的三个Bug!

给冒泡排序打上"热补丁"

现在,我们利用GDB的breakpoint command lists功能,给文中的冒泡排序程序打上"热补丁",演示如何在不修改源码、不重新编译的前提下,解决掉程序中的三个BUG。

再看一下示例程序:

img

解决第一个BUG

先解决第22行的BUG:数组arr元素个数是10,但是传递给了bubble_sort()的参数却是sizeof(arr),也就是40。

要解决这个BUG,我们只需要把参数修改成正确的值就行了。

我们知道,在x64上,优先采用寄存器传递函数参数。那么,有这几种方式可以选择:

  • • 把断点设置在bubble_sort()入口第一条指令,然后直接修改存放数组长度n的那个寄存器中的值。
  • • 把断点设置在bubble_sort()入口处(不必是第一条指令),在第7行for循环之前,把存放数组长度的变量n的值改掉。
  • • 把断点设置在main()函数第22行,也就是调用bubble_sort()的地方,然后以正确的参数手动调用bubble_sort()函数,并利用GDB的jump命令,跳过第22行代码的执行。

考虑到有些童鞋对x64 CPU不是非常了解,或者对GDB的jump命令不熟悉,我们采用第2种方式。而且,这种方式也更简单通用。

我们先在bubble_sort()函数设置断点,然后利用commands命令预设一条命令,把变量n的值修改为10。命令如下:

b bubble_sort
commands 1
  set var n=10
end

设置完之后,用run命令开始运行程序。结果如下:

img

bubble_sort()处的断点被触发后,程序暂停,用p(print)命令查看变量n的值,已经被修改成了正确的值:10。

可见,我们的设置是有效的。

断点触发后,让程序自动恢复执行

bubble_sort()处断点被触发,程序停了下来,修改完变量n的值后,怎么自动恢复执行呢?

很简单,只需要在预设的命令中添加一个continue命令就可以了。为了证明我们的设置确实是生效的,在修改变量n的前后,各添加一个格式化打印语句,把变量n的值打印出来:

b bubble_sort
commands 1
  printf "The original value of n is %d\n",n
  set var n=10
  printf "Current value of n is %d\n",n
  continue
end

结果如下图:

img

从运行结果可以看出,断点被触发后,我们预设的语句被正确执行,变量n的值被修改为10,然后程序自动恢复执行。虽然最终程序不会发生segfault了,但打印出来的排序结果仍然是错的!不着急,还有两个BUG没解决呢!

到此,第一个BUG已经解决了。

解决第二个BUG

下面,开始解决第7行的数组访问越界BUG:数组的元素个数是n,但是bubble_sort()中第一个for循环的终止条件是i<=n,明显会造成访问越界,正确的条件应该是i<n

要解决这个BUG也很简单,只需要在执行第8行代码之前,判断如果i的值等于n,就跳出循环。对于这个简单的程序,我们直接从bubble_sort()函数return就可以了。

命令如下:

b 8 if i==n
command 2
  printf "Current i = %d, n = %d\n",i,n
  return
  continue
end

在第8行设置条件断点,当i==n时断点被触发,然后自动把in的值打印出来,再行return命令,从bubble_sort()返回,然后continue命令自动恢复程序执行。

执行结果如下图:

img

解决第三个BUG

下面,解决最后一个BUG,第23行数组访问越界错误:数组arr的长度应该是10,不是sizeof(arr)

解决思路与第二个BUG类似,在第24行设置条件断点,当i==10时触发断点,然后用jump命令跳出循环,让程序跳转到第26行继续执行。命令如下:

b 24 if i==10
commands 3
  printf "i=%d, exit from for loop!\n",i
  jump 26
  continue
end

执行结果如下图所示:

img

从图中可以看出,三个断点全部被触发,并且预设的命令都正常执行。最终程序正常结束,我们终于得到了正确的执行结果!

虽然,现在程序可以正常执行了,但每次都要手动输入这么多命令,想想都觉得麻烦!我之前文章介绍过,GDB支持调试脚本,可以从脚本中加载并执行调试命令。

下面,利用GDB脚本,来制作我们的“热补丁”文件。

制作"热补丁"脚本

把上文中用来解决三个BUG的命令保存在一个脚本文件中:

vi bubble.fix

脚本内容如下图:

img

bubble.fix脚本中的命令,与上文在GDB中直接输入的命令有几个区别:

  • • 删除了格式化打印信息。
  • • 删除了commands后面的断点ID。上文讲过,commands后面的断点ID可以省略,表示对最近一次设置的断点有效。为了让脚本更加通用,每个commands都紧跟在break命令之后,因此直接省略了断点ID。

GDB的脚本可以通过两种方式执行:

  • • 启动GDB时,用-x参数指定要执行的脚本文件。
  • • 启动GDB后,执行source命令执行指定的脚本。

下面,我们用第二种方式演示一下,如下图所示:

img

使用source命令加载并执行bubble.fix,然后用run命令执行程序,三个断点均被触发,且预设的命令全部被正确执行,最后程序运行正常,得到期望的结果!

我们现在可以利用我们制作的"热补丁"脚本,在不修改代码、不重新编译和部署的前提下,成功修复程序中的BUG!是不是很有趣呢?

不过,做到这种程度,还是有点瑕疵。虽然得到了正确的结果,但程序执行时,总是会打印断点信息,造成视觉干扰,作为典型的"伪完美主义者",这怎么能忍!

最后,我们来解决这个问题,让我们的"热补丁"更加完美!

优化"热补丁"脚本,隐藏断点信息

在预设的命令中,如果第一条命令是silent,断点被触发的打印信息会被屏蔽掉。

我们把bubble.fix做些修改,把silent命令加进去,如下图所示:

img

此外,在最后面加了一个run命令,这样就不用每次手动执行了。

然后,我们换一种方式来执行:

img

这样,看起来,清爽多了!

到此,我们终于实现了最终的目标:一边debug,一边修复BUG,并验证解决方案,避免反复修改代码、重新编译和部署、提高调试效率!

原文链接:https://zhuanlan.zhihu.com/p/698084327?utm_campaign=shareopn&utm_medium=social&utm_psn=1782817623383240705&utm_source=wechat_session

标签:无需,修改,bubble,命令,GDB,Debug,断点,BUG
From: https://www.cnblogs.com/dongxb/p/18240597

相关文章

  • SMS - 基于阿里云实现手机短信验证码登录(无需备案,非测试)
    目录SMS环境调试从阿里云云市场中购买第三方短信服务调试短信验证码功能实战开发 封装组件对外接口调用演示SMS环境调试从阿里云云市场中购买第三方短信服务a)进入阿里云首页,然后从云市场中找到“短信” (一定要从云市场去找短信服务,否则需要企业证明,备案) ......
  • Debug-015-找出两个列表中不重复的元素
    constsetA=newSet(A.map((item)=>item.deviceName))constres=B.filter(item=>!setA.has(item.deviceName))console.log('两个列表中不重复的元素',res)这段代码主要实现了从一个列表中筛选出不在另一个集合中的元素。首先,通过map方法将A列表中的......
  • 串联式固定测斜仪无需钢丝绳、安装方便、可回收利用边坡基坑矿山地灾常用
    一、固定式测斜仪的简介固定测斜仪是一种用于长期自动监测各种结构物的深层水平位移的设备,获取土体内部的位移变化趋势,监测数据上传至安锐测控云平台,用户即可实时查看结构深层水平位移数据,实时预警,保障结构的安全。主要用于测量不同深度的钻孔、基坑、地基基础、墙体、坝......
  • 终于搞懂了!原来vue3中template使用ref无需.value是因为这个
    前言众所周知,vue3的template中使用ref变量无需使用.value。还可以在事件处理器中进行赋值操作时,无需使用.value就可以直接修改ref变量的值,比如:<button@click="msg='HelloVue3'">changemsg</button>。你猜vue是在编译时就已经在代码中生成了.value,还是运行时使用Proxy拦截的......
  • 我想使用一个宏来控制是否打印debug信息,debug函数的具体内容是往文件中打印函数名和
    你可以定义一个宏,让它根据是否启用调试模式来控制是否打印调试信息,并且将调试信息输出到文件中。下面是一个示例代码,其中定义了一个名为DEBUG_PRINT的宏,它会在启用调试模式时打印函数名、时间、行数以及自定义的调试信息到文件中:#include<iostream>#include<fstream>#inclu......
  • 星海算力云:【ChatTTS】 无需部署,一键云启动
    镜像介绍ChatTTS:革新对话式文本转语音技术ChatTTS是由2noise团队开发的一款专为对话场景设计的文本转语音(TTS)模型。它不仅支持英文和中文两种语言,而且经过了超过10万小时的中英文数据训练,表现出色。ChatTTS的亮点在于其对话式TTS的优化,它能够生成自然流畅的语音并支持多......
  • 无需搭建环境,零门槛带你体验Open-Sora文生视频应用
    本文分享自华为云社区《Open-Sora文生视频原来在AIGallery上也能体验了》,作者:码上开花_Lancer。体验链接:Open-Sora文生视频案例体验不久前,OpenAISora凭借其惊人的视频生成效果迅速走红,在一堆文本转视频模型中脱颖而出,成为全球关注的焦点。之后,Colossal-AI团队又推出了新的......
  • VSCode(Visual Studio Code) C/C++ 开发环境 | MinGW无需输入代码 — 99.99%成功率(超详
    VisualStudioCodeC++六一马不停蹄更新中……参考资料:visualstudio|cppuninstall|vscode———————————————————————————————————————————————卸载VSCode【如没下跳转】-先下载下准备zip:MinGW32.zipMinGW真......
  • AI菜鸟向前飞 — LangChain系列之十六 - Agent系列:从现象看机制(下篇)一款“无需传递中
    前言    AI菜鸟向前飞—LangChain系列之十四-Agent系列:从现象看机制(上篇)    AI菜鸟向前飞—LangChain系列之十五-Agent系列:从现象看机制(中篇)一个Agent的“旅行”    回顾前两篇文章,大家会发现一个问题    为什么每次Agent在invoke的时候需要多加......
  • QT笔记:重定向qDebug到控件
    QT笔记:重定向qDebug到控件作为log输出的qDebug可以将调试信息打印到调试终端中,但是有时候实际使用将其输出到UI界面也是很有用的,这里记录下如何将qDebug进行控件重定向。测试版本为QT6.6.2代码mainwindow.h//只要能正常编译过qDebug的就行#include<QMainWindow>QT_BEGIN_......