GDB 基础调试:启动、连接与断点管理
调试嵌入式控制算法,说白了就是跟芯片“对话”。而 GDB,就是我们手里最趁手的工具。我刚开始做电机控制时,总觉得代码跑起来就完事了,直到有一次 PWM 波形死活不对,才老老实实把 GDB 捡起来。嗯,从那以后,我再也不敢跳过调试这一步了。
GDB 启动与连接:两种常见姿势
GDB 的启动方式,取决于你的目标环境。我个人习惯把它们分成两类:本地调试和远程调试。
| 调试模式 | 适用场景 | 启动命令 |
|---|---|---|
| 本地调试 | 在 PC 上模拟运行,验证算法逻辑 | gdb ./motor_ctrl.elf |
| 远程调试 | 连接开发板,调试真实硬件 | gdb 后执行 target remote :2331 |
远程调试时,我建议你先确认一下 GDB Server 是否已经跑起来。曾经有一次我连了半天连不上,最后发现是 OpenOCD 的配置文件里少写了一个 transport select swd。你想想看,这种低级错误多耽误时间。
info target 看看当前连接的目标信息。如果显示 Remote target 且地址正确,说明连接成功。
断点管理:硬件断点 vs 软件断点
断点是调试的灵魂。但很多人不知道,断点其实分两种:硬件断点和软件断点。它们的区别,直接决定了你能在什么地方停下来。
| 特性 | 硬件断点 | 软件断点 |
|---|---|---|
| 原理 | 利用 CPU 调试寄存器 | 在指令处插入 BKPT 指令 |
| 数量限制 | 通常 2-6 个(取决于芯片) | 无限制 |
| 适用位置 | RAM、Flash、ROM 均可 | 仅限 RAM(可写区域) |
| 典型命令 | hbreak |
break |
我在调试 Flash 中的 Bootloader 时,就吃过软件断点的亏。Bootloader 代码在 Flash 里,软件断点根本插不进去。后来换成 hbreak,才顺利在启动代码里停下来。记住:Flash 里的代码,只能用硬件断点。
info breakpoints 查看当前断点列表,删掉不需要的。
单步执行与变量查看
单步执行,是理解代码执行路径最直接的方式。GDB 提供了几个常用的单步命令:
step(或s):进入函数内部next(或n):不进入函数,直接执行完当前行finish:执行到当前函数返回until:执行到指定行号
变量查看,我推荐用 display 命令。它会在每次单步后自动打印你关心的变量值,省得你一遍遍敲 print。比如:
(gdb) display /x motor_current
1: /x motor_current = 0x1a2b
(gdb) n
2: /x motor_current = 0x1a2c
你看,每次单步后,GDB 自动把 motor_current 的十六进制值打印出来。这对于观察电流采样值的变化特别有用。
display /f motor_current 查看浮点格式的电流值。配合 step 进入 PI 控制器函数,能直观看到积分项是怎么累加的。
调用栈回溯:找到“罪魁祸首”
程序崩溃时,调用栈是你最好的朋友。它能告诉你:程序是从哪里一路跑到这里的。命令很简单:backtrace(或 bt)。
(gdb) bt
#0 HardFault_Handler () at startup_stm32f4.s:98
#1 0x08001234 in PID_Update (pid=0x20000100, setpoint=1000, feedback=999) at pid.c:45
#2 0x08001567 in Motor_Control_Loop () at motor.c:120
#3 0x08001678 in main () at main.c:55
看到这个栈了吗?#0 是当前停下的位置——HardFault 处理函数。往上翻,#1 是 PID_Update 函数,#2 是 Motor_Control_Loop。这说明,问题出在 PID 更新函数里,导致触发了硬件错误。
我曾经遇到过一个诡异的 HardFault,栈回溯显示是在 memcpy 里崩的。但 memcpy 本身没问题啊?后来用 frame 1 切换到上一层,再 info locals 一看,发现传入的指针是 0x00000000。嗯,空指针解引用,典型的低级错误。
frame N 切换到第 N 层,然后用 info args 和 info locals 查看该层的参数和局部变量。这比直接看 #0 层的信息更有用。
知识结构总览
下面这张图,把 GDB 基础调试的核心知识点串了起来。你可以把它当作调试时的“路线图”。
这张图里,四个分支覆盖了 GDB 调试的四个核心环节。从启动连接开始,到设置断点,再到单步跟踪变量,最后通过栈回溯定位问题。每一步都环环相扣。
- 远程调试时,先确认 GDB Server 配置正确
- Flash 代码用硬件断点,RAM 代码用软件断点
- 单步时用
display自动跟踪关键变量 - 崩溃后先
bt,再frame切换查看上下文