找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

5956

积分

0

好友

760

主题
发表于 6 天前 | 查看: 8| 回复: 0

书接上文,做菜好吃靠的是火候。调试代码也一样:不只是“能熟”,而是“恰到好处”。

很多 Zephyr 开发者在刚接触 QEMU 时,往往停留在“程序跑起来就行”的阶段。但当项目开始复杂起来,HardFault、BusFault、栈溢出、异常重入等问题便会接踵而至。此时,掌握一套高效的调试方法,远比盲目猜测问题原因更加重要。

本文将为大家带来两种方式演示如何在 QEMU 上调试 Zephyr 程序:

  • 方式 A:手动启动 QEMU,自行开启 GDB Server;
  • 方式 B:使用 west -t debugserver 一键启用调试环境。

两种方式都能完成调试,只不过区别在于:

  • 方式 A 更像自己掌勺控火,灵活自由,适合深入研究底层机制;
  • 方式 B 则像把点火工作交给副厨,省心省力,更适合日常开发和团队协作。

1. 上灶前的预处理:把信息与安全阀门先打开

正式调试前,建议在 prj.conf 中启用调试配置:

CONFIG_PRINTK=y
CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_EXCEPTION_DEBUG=y
CONFIG_DEBUG_COREDUMP=y
CONFIG_INIT_STACKS=y
CONFIG_STACK_SENTINEL=y
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_ISR_STACK_SIZE=4096

这些配置看似普通,实际价值很高:

  • 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
qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -kernel

关键参数说明:

  • -S:开机暂停,等你下断点;
  • -s:在本地 1234 端口开 GDB Server;
  • -nographic:把串口输出绑到当前终端,方便看日志。

第三步,连上 GDB:

arm-zephyr-eabi-gdb build\zephyr\zephyr.elf
(gdb) target remote :1234
(gdb) break main
(gdb) continue

在 WSL 或类 UNIX 终端里,还可以开启 TUI:

(gdb) layout split
看当前 PC 附近的指令: 
(gdb)   x/10i $pc
指令级单步: 
(gdb) si
(gdb) ni
回溯与寄存器: 
(gdb) bt
(gdb) info registers
只反汇编当前函数: 
(gdb) disassemble

如果怀疑是异常致死,预埋断点在异常入口:

(gdb) break z_arm_hard_fault
(gdb) break z_arm_fault
(gdb) continue

命中后记下 pclr,用 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) target remote :1234
(gdb) break main
(gdb) continue

后续使用的 GDB 命令与方式 A 完全一致。

4. 真实案例:QEMU 报 HardFault/Lockup 怎么办?

典型症状:刚起步就看到类似 “Lockup: can't escalate … to HardFault”

先不要慌。按照下面顺序排查,通常都能快速定位问题。

快速处置顺序如下:

  1. MAIN_STACK_SIZEISR_STACK_SIZE,先 4096 起步;
  2. 打开 CONFIG_EXCEPTION_DEBUGCONFIG_STACK_SENTINEL,重建运行;
  3. z_arm_hard_faultz_arm_fault 下断点,命中后 info registers
  4. x/10i $pc 看故障点附近指令,是非法地址、未对齐访问、还是异常重入;
  5. addr2linepclr 定位到源码行,沿调用链找可疑入口;
  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 ondisplay/i $pc、常用断点与打印别名;
  • 将关键模块配套 ztest 用 QEMU 走回归。

当你学会使用 GDB 查看寄存器、分析调用栈、定位 HardFault,并把这些能力融入日常开发流程之后,调试将不再依赖运气,而是一套可复用、可复制的方法论。

其实 HardFault 不可怕,可怕的是找不到它。

有了 QEMU、GDB 和 Zephyr 提供的调试能力,我们完全可以做到:问题出现时第一时间发现它,定位它,最终消灭它。这套方法论也值得在云栈社区与更多嵌入式同行一起打磨。




上一篇:从 Figma 到 GUI Guider:嵌入式 GUI 设计稿转 LVGL 工程实践
下一篇:Steam 8月报告:16GB显存连续两月压过8GB,RTX 3060仍是钉子户
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-9-12 00:38 , Processed in 0.339587 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表