书接上文,做菜好吃,靠的是火候。调试代码也一样:不只是“能熟”,而是“恰到好处”。
很多Zephyr开发者在刚接触QEMU时,往往停留在“程序跑起来就行”的阶段。但当项目开始复杂起来,HardFault、BusFault、栈溢出、异常重入等问题便会接踵而至。此时,掌握一套高效的调试方法,远比盲目猜测问题原因更加重要。
本文将为大家带来两种方式演示如何在QEMU上调试Zephyr程序:
- 方式A:手动启动**QEMU,自行开启GDB Server**;
- 方式B:使用west -t debugserver一键启用调试环境
两种方式都能完成调试,只不过区别在于:
- 方式A更像自己掌勺控火,灵活自由,适合深入研究底层机制;
- 方式B则像把点火工作交给副厨,省心省力,更适合日常开发和团队协作。
1上灶前的预处理:把信息与安全阀门先打开
正式调试前,建议在prj.conf中启用调试配置:
CONFIG_PRINTK=y
这些配置看似普通,实际价值很高:
-
CONFIG_EXCEPTION_DEBUG
异常发生时自动打印寄存器、PC、LR等关键信息。
-
CONFIG_DEBUG_COREDUMP
支持生成 Core Dump,便于后续分析。
-
CONFIG_INIT_STACKS
初始化栈空间,帮助发现栈使用情况。
-
CONFIG_STACK_SENTINEL
增加栈哨兵检测,能够第一时间发现栈溢出。
-
MAIN/ISR Stack Size
在问题排查阶段,建议直接提升至4096字节,避免被栈不足误导。
很多所谓“神秘HardFault”,最终根因其实只是栈不够大。
2方式A:手动启动QEMU, 自行连接GDB
对于想深入理解调试原理的同学,推荐先掌握这种方式。
第一步,编译目标程序:
west build -b qemu_cortex_m3 D:\path\to\my_app -p always
第二步,手动起QEMU,先停在入口等你:
PowerShell
关键参数说明:
-
-S开机暂停,等你下断点;
-
-s在本地1234端口开GDB Sserver;
-
-nographic把串口输出绑到当前终端,方便看日志。
第三步,连上GDB:
arm-zephyr-eabi-gdb build\zephyr\zephyr.elf
在WSL或类UNIX终端里,还可以开启TUI:
(gdb) layout split
如果怀疑是异常致死,预埋断点在异常入口:
(gdb) break z_arm_hard_fault
命中后记下pc与lr,用addr2line回到源码:
arm-zephyr-eabi-addr2line -e build\zephyr\zephyr.elf 0x08001234
3方式B: 让west帮你把火点好
当你不想记QEMU那一长串参数时,把点火交给west。
第一步,编译依旧:
west build -b qemu_cortex_m3 D:\path\to\my_app -p always
第二步,开一个终端运行:
west -t debugserver
此时west会代你启动QEMU,并以暂停方式等待连接,默认端口仍是:1234。
第三步,另开一个终端上GDB:
arm-zephyr-eabi-gdb build\zephyr\zephyr.elf
后续使用的GDB命令与方式A完全一致。
4真实案例:QEMU报HardFault/Lockup怎么办?
典型症状:刚起步就看到类似 “Lockup: can't escalate … to HardFault”。
先不要慌。按照下面顺序排查,通常都能快速定位问题。
快速处置顺序如下:
1. MAIN_STACK_SIZE、ISR_STACK_SIZE,先4096起步;
2. 打开CONFIG_EXCEPTION_DEBUG 和CONFIG_STACK_SENTINEL,重建运行;
3. 在z_arm_hard_fault、z_arm_fault下断点,命中后info registers;
4. x/10i $pc看故障点附近指令,是非法地址、未对齐访问、还是异常重入;
5. addr2line把pc、lr定位到源码行,沿调用链找可疑入口;
6. 如初始化了QEMU不支持的外设,先在prj.conf里关闭相关驱动,验证是否是某外设引发的BusFault.
5两种方式如何选择?
-
观察更底层现象、需要特殊QEMU参数、或做原理实验:用“手动gdbserver”
-
想快速稳定、流程一致、适合团队协作:west -t debugserver
-
两者可以随时切换:日常用west,疑难杂症切到手动
6收尾与加餐
当你能在Windows+MSYS2+QEMU的组合里熟练起锅、控火、下菜,就已经具备了“无板推进”的生产力。建议把以下流程固化进团队工程手册或CI或者按照现在大模型的开发流程生成一个Skills:
-
west build-b qemu_cortex_m3构建;
-
west build-t run;
-
west-t debugserver或手动-s -S开启调试;
-
打包一份.gdbinit:开启set disassemble-next-line on、display/i $pc、常用断点与打印别名;
-
将关键模块配套ztest用QEMU走回归。
当你学会使用GDB查看寄存器、分析调用栈、定位HardFault,并把这些能力融入日常开发流程之后,调试将不再依赖运气,而是一套可复用、可复制的方法论。
其实HardFault不可怕,可怕的是找不到它。
有了QEMU、GDB和Zephyr提供的调试能力,我们完全可以做到:问题出现时第一时间发现它,定位它,最终消灭它。
END
评论区
登录后即可参与讨论
立即登录