GDB 基础调试:启动、连接与断点管理

调试嵌入式控制算法,说白了就是跟芯片“对话”。而 GDB,就是我们手里最趁手的工具。我刚开始做电机控制时,总觉得代码跑起来就完事了,直到有一次 PWM 波形死活不对,才老老实实把 GDB 捡起来。嗯,从那以后,我再也不敢跳过调试这一步了。

GDB 启动与连接:两种常见姿势

GDB 的启动方式,取决于你的目标环境。我个人习惯把它们分成两类:本地调试和远程调试。

调试模式 适用场景 启动命令
本地调试 在 PC 上模拟运行,验证算法逻辑 gdb ./motor_ctrl.elf
远程调试 连接开发板,调试真实硬件 gdb 后执行 target remote :2331

远程调试时,我建议你先确认一下 GDB Server 是否已经跑起来。曾经有一次我连了半天连不上,最后发现是 OpenOCD 的配置文件里少写了一个 transport select swd。你想想看,这种低级错误多耽误时间。

小技巧: 启动 GDB 后,先敲 info target 看看当前连接的目标信息。如果显示 Remote target 且地址正确,说明连接成功。

断点管理:硬件断点 vs 软件断点

断点是调试的灵魂。但很多人不知道,断点其实分两种:硬件断点和软件断点。它们的区别,直接决定了你能在什么地方停下来。

特性 硬件断点 软件断点
原理 利用 CPU 调试寄存器 在指令处插入 BKPT 指令
数量限制 通常 2-6 个(取决于芯片) 无限制
适用位置 RAM、Flash、ROM 均可 仅限 RAM(可写区域)
典型命令 hbreak break

我在调试 Flash 中的 Bootloader 时,就吃过软件断点的亏。Bootloader 代码在 Flash 里,软件断点根本插不进去。后来换成 hbreak,才顺利在启动代码里停下来。记住:Flash 里的代码,只能用硬件断点。

注意: 硬件断点数量有限。如果你设了 4 个硬件断点还想再加,GDB 会报错。这时候可以先用 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 的十六进制值打印出来。这对于观察电流采样值的变化特别有用。

实战经验: 我在调试 FOC 电流环时,经常用 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 调试会话 启动与连接 断点管理 单步与变量 调用栈回溯 本地 / 远程 硬件 / 软件 step / next display / print backtrace / frame

这张图里,四个分支覆盖了 GDB 调试的四个核心环节。从启动连接开始,到设置断点,再到单步跟踪变量,最后通过栈回溯定位问题。每一步都环环相扣。

核心要点:
  • 远程调试时,先确认 GDB Server 配置正确
  • Flash 代码用硬件断点,RAM 代码用软件断点
  • 单步时用 display 自动跟踪关键变量
  • 崩溃后先 bt,再 frame 切换查看上下文