嵌入式都能用上的gdb调试进阶--上

文摘   2024-11-24 21:23   广东  


正文


大家好,我是bug菌~
gdb应该大家并不陌生,不过对于一些IDE依赖的同志,估计也并不是特别熟,今天要讲的主题是GDB(GNU Debugger),它是GUN项目的一部分,是一个非常强大的调试器。

GDB 本身主要提供软件层面的调试功能,如控制程序执行、设置断点、查看变量等。但GDB 需要通过一定的接口来与目标设备进行通信,以便将这些调试指令传递给目标设备并获取设备的状态信息。像我们平时常用的J - Link 就充当了这个通信接口的硬件支持部分。

好了,今天不多说GDB的原理性内容,有机会再聊,而主要是总结下使用GDB调试过程中能够加快调试效率的一些gdb调试命令的总结:

下面的命令和方法都可以在MCU调试中找到一一对应关系,所以只要是嵌入式开发MCU和MPU都用得上,学起来~

1

常规命令

常规命令是一定要记住的,也是基础中的基础,不过多解释了,如下如所示直接列出来:

2

修改变量值

有时候程序初始化了,相关变量需要等很久才会达到想要的值,那么我们可以修改相应的变量来复现之前的状态,可以通过gdb来修改相应变量的值:

在GDB调试中,使用 set 命令来设置变量的值格式通常是 set var <variable_name>= 。

需要注意的是,变量的类型必须和你赋予它的值类型相匹配,并且这个变量必须在当前的作用域中是可见的,才能成功设置其值。

3

不同文件相同名字断点

可能很多伙计调试单路径单目录比较多,在调试大型工程代码的多文件多路径时断点就不一定打得上去了。

在 GDB 中,通常设置断点的方式是使用break(缩写为b)命令加上文件名和行号。如main.c存放在项目的src目录下,并且你想要在main.c的第 10 行设置断点,可以在 GDB 中输入break src/main.c:10

不过这里有个前提就是需要告诉gdb你的源文件在哪里,gdb才能够根据你的输入进行正确的断点设置,如果断点打不上,可以使用"dir <目录路径>"来添加源文件的搜索路径。

如果源文件存放在/home/user/project/src目录下,“ dir /home/user/project/src”命令来告诉 GDB 在这个目录下搜索源文件,当然也可以使用相对路径。例如GDB 当前工作目录是/home/user/project,用“dir src"来将src目录添加,等价于添加了/home/user/project/src路径,查看断点的时候再确认下断点位置。

4

条件断点

有时候我们想在某个循环打个断点,复杂的分支打个断点,或者是满足什么条件打个断点等,那么gdb中的条件断点一定要会,它允许程序在满足特定条件时才暂停执行。

其实就是break的加强用法 : break <位置> if <条件表达式>。其中的条件表达式>” 是一个布尔表达式。

比如: "break main.c:6 if i == 3"(假设这段代码在main.c文件的第 6 行),这样程序在循环过程中只有当i等于3时才会暂停。

值得注意的是:条件表达式是在程序执行到断点位置所在的上下文环境中进行求值的。这意味着它可以访问断点位置可见的所有变量,包括局部变量、全局变量和函数参数。但是,要确保表达式中使用的变量在断点位置是有定义的,否则 GDB 可能会报错。

同时它也会影响被调试程序的运行效率,由于 GDB 需要在每次到达断点位置时对条件表达式进行求值,设置过多复杂的条件断点可能会影响调试性能。特别是在循环内部等频繁执行的代码段中,如果条件表达式计算复杂,可能会导致程序运行速度明显变慢。因此,在设置条件断点时,要权衡调试的便利性和程序性能。

5

打印表达式

打印主要有两个命令,一个是手动打印--print,另外一个是自动打印--display,手动打印就是每次调试暂停下来进行输入print打印,而display则是每次调试过程暂停下来自动进行用户所设置的打印。

可能很多伙计最开始使用print就仅仅只是打印某个变量,其实它们还可以打印表达式,甚至是函数调用。

比如打印包含变量和运算符的表达式的值。例如,在一个包含变量a和b的程序中,在断点处可以打印a + b、a * b等表达式的值,以检查程序中的计算是否正确。当被调试程序中有一个函数int square(int x),在断点处可以使用print square(5)来查看函数调用square(5)的结果,不过稍微要注意下,被调用的函数如果有一些全局的变量内容,就可能被修改影响调试过程。

6

调试正在运行的程序

没错,这个也是我常用的一种调试手段,因为一般的调试基本上靠经验和简单的printf就可以搞定,针对一些bug要复现很长时间才能出现,而且一旦出现就会一直存在,那么我们可以侵入到程序内部,看看程序到底在干些啥,而无需复位整个程序,重新使用gdb来介入,之前单片机的调试也跟大家分享过类似方法,大同小异。<不复位MCU直接调试运行程序,让bug闻风丧胆>

对于gdb主要有两种办法:

第一种使用 attach 命令将GDB附加到正在运行的程序进程上。例如,如果程序的PID是1234,就输入 attach 1234 ,接下来就后面就跟普通的调试一样了,比如查看调用栈输入bt命令等。

第二种办法,在GDB中 -p 选项用于指定要调试的进程ID(PID),这是一种快速附加到正在运行进程进行调试的方式。

例如目标进程的PID是1234,输入 gdb -p 1234 ,GDB就会直接附加到这个进程上,然后你就可以开始调试,如设置断点、查看变量的值等操作,这样就不需要先进入GDB再使用 attach 命令来附加到进程,从而简化了调试正在运行程序的步骤。

下篇见~~~~~~~~~

最后

      好了,今天就跟大家分享这么多了,如果你觉得有所收获,一定记得点个~

唯一、永久、免费嵌入式技术知识分享平台

推荐专辑  点击蓝色字体即可跳转

☞  MCU进阶专辑 

☞  嵌入式C语言进阶专辑 

☞  “bug说”专辑 

☞ 专辑|Linux应用程序编程大全

☞ 专辑|学点网络知识

☞ 专辑|手撕C语言

☞ 专辑|手撕C++语言

☞ 专辑|经验分享

☞ 专辑|电能控制技术

☞ 专辑 | 从单片机到Linux

最后一个bug
一个嵌入式技术进阶公众号,定期分享C语言,C++、MCU(如stm32等)、DSP、ARM、嵌入式Linux等“独门”软件设计技巧和知识归纳总结,同时分享应用程序设计、物联网、滤波及控制算法推导和仿真设计等嵌入式硬核知识技巧!欢迎大家关注!
 最新文章