单次指令执行模式(Single-Step Mode),俗称"单步调试",是让程序每执行一条机器指令就立即暂停的控制方式。这种模式是定位疑难Bug的"显微镜",能让你看清程序运行的每一个细节。
4.2.1 核心概念
在此模式下,CPU执行完当前指令后自动触发调试异常(Debug Exception),将控制权交还给调试器。与断点调试不同,单步不需要预先设置断点地址,而是逐条"爬行"通过代码。
4.2.2 何时必须使用
- 验证汇编逻辑:确认编译器是否把
i++优化成了错误的寄存器操作 - 排查硬故障:程序跑飞(Hard Fault)时,单步定位触发异常的精确指令
- 观察硬件时序:调试SPI/I2C通信时,确认每个寄存器写入顺序是否符合芯片手册要求
- 逆向工程:分析没有源码的库函数执行流程
4.2.3 实际操作示例(GDB/LLDB)
(gdb) break main
(gdb) run
# 进入单步模式(汇编级)
(gdb) stepi # 步入,会进入函数内部
或
(gdb) nexti # 步过,把CALL指令当作一步执行
# 查看执行结果
(gdb) info registers # 查看寄存器变化
(gdb) x/4wx $esp # 查看栈顶内存
嵌入式开发(JTAG/OpenOCD)示例:
# 连接目标板后
> reset halt
> step # 执行一条ARM/Thumb指令
> mdw 0x40021000 1 # 查看RCC寄存器实际值
4.2.4 实用技巧与避坑指南
1. 优化级别陷阱
编译时若开启 -O2 优化,单步执行会出现"乱跳"现象(代码行号与实际执行顺序不符)。调试阶段建议始终使用:
gcc -O0 -g # 零优化,保留调试信息
2. 中断干扰处理
单步执行定时器中断服务程序时,建议先关闭中断或提高定时器阈值,否则刚单步跳出中断又立即进入,无法观察主程序流程。
3. 系统调用处理
遇到 syscall 或 int 0x80 时,不要用 stepi 进入内核(除非调试内核),应使用 nexti 步过,否则陷入内核代码难以退出。
4. 性能边界
单步执行速度约为正常执行的 1/100 ~ 1/1000。调试大型循环时,应在循环外设置断点,而非按F1000次。
4.2.5 典型应用案例
场景:STM32程序死机,Hard Fault Handler被触发。
解决过程:
- 连接调试器,复位后停在
Reset_Handler - 连续按
stepi20次,观察跳转到SystemInit - 继续单步,发现执行到
RCC->CR |= 0x00010000;时崩溃 - 检查map文件,发现RCC地址因头文件版本错误被定义为0x20000000(RAM区)而非0x40023800
- 修正地址定义,问题解决
关键提醒:单步模式最耗时的不是执行本身,而是每次暂停后人工检查状态。建议配合 观察窗口(Watch Window)自动监控关键变量,减少手动输入命令的时间。