Rust in Everything
基于 probe-rs 0.32.0

probe-rs 源码解析

全卷只对着一个版本写:

crate版本tagcommit日期
probe-rs、probe-rs-target、probe-rs-debug、probe-rs-tools、target-gen、probe-rs-espressif、probe-rs-linux0.32.0v0.32.048f5e4d52026-07-22
probe-rs-mi(同一个 tag 里,单独编号)0.2.0同上同上同上

probe-rs 是一个用 Rust 写的嵌入式调试工具链。README 的第一句话是「a modern, embedded debugging toolkit, written in Rust」。它既是库,也是一组命令行工具:probe-rs run 把固件烧进芯片、让它跑起来、把 RTT 和 defmt 日志打到终端,可以直接当 cargo 的 runner;probe-rs dap-server 让 VS Code 这类编辑器单步调试;probe-rs gdb 给 GDB 用。过去做同样的事,常见的组合是 OpenOCD 加 GDB,再加厂商的烧录器。

它和前几卷的关系:embassy 卷讲的是固件这一侧,「embassy 固件 + defmt + probe-rs run 作 cargo runner」是 Rust 嵌入式最常见的组合;embassy 卷的实验跑在 QEMU 上,本卷讲的是另一侧:主机上的程序怎么隔着一根 USB 线和两根信号线,让一颗芯片停下来、读它的寄存器和内存。源码里 qemu 一词零命中,probe-rs 和 QEMU 之间没有桥。命令行工具内部用了 tokio 和一套 RPC,涉及时链到 tokio 卷。

全卷沿三条线展开:

  • 命令主线(probe-rs run fw.elf):注册插件,在本进程里起一个 RPC 服务端,选探针、attach、建 Session,烧录,然后每 100 ms 轮询一次:核停了没有,RTT 缓冲区里有没有新数据;defmt 在客户端解码。
  • 访问主线(一次 32 位读,从 API 到线上):Core::read_word_32 → MEM-AP 的 TAR / DRW → 先写 DP 的 SELECT → 探针驱动:CMSIS-DAP 把寄存器访问攒成一个 USB 包;J-Link、FTDI 这类只会翻转引脚的探针,由 probe-rs 自己拼出 SWD 的每一位。
  • 调试主线(停下来、看得懂):写 DHCSR 让核停下、FPB 设硬件断点、C_STEP 单步、读 DFSR 判断停下的原因;再往上是 probe-rs-debug 用 DWARF 展开调用栈、解析变量,DAP 服务器把 VS Code 的请求映射到这些调用。
flowchart TB
  intro[00 导读] --> overview[01 全景]
  overview --> probe[02 探针层]
  probe --> arm[03 ARM 调试架构]
  arm --> other[04 RISC-V 与 Xtensa]
  arm --> target[05 会话与目标描述]
  target --> flash[06 烧录]
  target --> rtt[07 RTT 与 semihosting]
  arm --> dbg[08 调试信息]
  dbg --> servers[09 调试服务器]
  servers --> withemb[10 和 embassy 连起来]
  withemb --> design[11 设计取舍]
  design --> pit[12 常见坑]
  pit --> hands[13 动手]

实验环境

本机没有调试探针,也没有开发板。全卷的实验只用不需要硬件的办法:

  • 本机安装的 probe-rs 0.32.0(git commit: 48f5e4d)的无硬件命令:chip list / chip info、list、各子命令的 --help,以及 gdb --dry-run,再用 macOS 自带的 lldb 连它;
  • 依赖 probe-rs =0.32.0(开 test feature)的 Rust 实验程序:用库里自带的 FakeProbe,或者自己实现探针 trait,写一个软件模拟的 SWD 目标(一个 SW-DP、一个 AHB-AP、一块内存和 Cortex-M 的调试寄存器),让 probe-rs 自己的代码去驱动它,记下每一次 DP / AP 访问、每一个 SWCLK 周期;
  • 仓库自带的 coredump 夹具,给栈展开和变量解析用。
  • QEMU 加一个自己写的 GDB 远程协议客户端:让 probe-rs-debug 展开 QEMU 里正在跑的固件(13 动手)。

凡是必须有真硬件才能得到的数字(SWD 速度、USB 包数、断点单元的实际数量、attach 的时序),页面上写「待实测」。

各部分

  1. 00 导读:锁定的版本、三条主线、阅读路线、实验方法;工作区各 crate 的分层、插件和 feature。
  2. 01 全景:probe-rs 想解决什么问题,和 OpenOCD、pyOCD、SEGGER 工具的位置;一条 probe-rs run 从命令到日志。
  3. 02 探针层:驱动注册表与 DebugProbe;位级探针怎么由 polyfill 补出 DAP 事务;CMSIS-DAP 驱动的批处理;FakeProbe 能测什么。
  4. 03 ARM 调试架构:本卷重点。DP、AP 与 SELECT;MEM-AP 与 ROM table;Cortex-M 的 halt、寄存器、断点、单步;调试序列与 vendor。
  5. 04 RISC-V 与 Xtensa:RISC-V 调试模块、Xtensa 与 ESP 插件。
  6. 05 会话与目标描述:Session、Core 与 MemoryInterface;目标描述从 YAML 到注册表;target-gen 与 CMSIS-Pack。
  7. 06 烧录:从镜像到烧录计划;flash 算法在目标的 RAM 里跑。
  8. 07 RTT、defmt 与 semihosting:RTT 控制块与通道;defmt 与 semihosting。
  9. 08 调试信息:本卷另一个重点。DebugInfo 与 DWARF;CFI 栈展开与异常帧;变量与单步。
  10. 09 调试服务器:DAP server 与 VS Code;gdb server;命令行的 RPC 架构。
  11. 10 和 embassy 连起来:把 embassy 卷的固件接到 probe-rs 的工作流上;cargo runner 与 defmt 三件套、从 QEMU 换到真板子、VS Code 调试配置。
  12. 11 设计取舍:把各章的取舍汇总到一起。
  13. 12 常见坑:用报错原文做标题,讲它在源码里怎么产生。
  14. 13 动手:写一个最小的「软件探针」;用 QEMU 的 GDB 服务端让 probe-rs-debug 展开活固件。

从「导读」开始。只想会用 probe-rs 的读者,读 01、06、07、10、12;想读懂实现的,按顺序读。

💬 关联讨论

发起讨论
加载中...