probe-rs 源码解析
全卷只对着一个版本写:
| crate | 版本 | tag | commit | 日期 |
|---|---|---|---|---|
probe-rs、probe-rs-target、probe-rs-debug、probe-rs-tools、target-gen、probe-rs-espressif、probe-rs-linux | 0.32.0 | v0.32.0 | 48f5e4d5 | 2026-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-rs0.32.0(git commit: 48f5e4d)的无硬件命令:chip list/chip info、list、各子命令的--help,以及gdb --dry-run,再用 macOS 自带的 lldb 连它; - 依赖
probe-rs =0.32.0(开testfeature)的 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 的时序),页面上写「待实测」。
各部分
- 00 导读:锁定的版本、三条主线、阅读路线、实验方法;工作区各 crate 的分层、插件和 feature。
- 01 全景:probe-rs 想解决什么问题,和 OpenOCD、pyOCD、SEGGER 工具的位置;一条
probe-rs run从命令到日志。 - 02 探针层:驱动注册表与
DebugProbe;位级探针怎么由 polyfill 补出 DAP 事务;CMSIS-DAP 驱动的批处理;FakeProbe能测什么。 - 03 ARM 调试架构:本卷重点。DP、AP 与 SELECT;MEM-AP 与 ROM table;Cortex-M 的 halt、寄存器、断点、单步;调试序列与 vendor。
- 04 RISC-V 与 Xtensa:RISC-V 调试模块、Xtensa 与 ESP 插件。
- 05 会话与目标描述:
Session、Core与MemoryInterface;目标描述从 YAML 到注册表;target-gen 与 CMSIS-Pack。 - 06 烧录:从镜像到烧录计划;flash 算法在目标的 RAM 里跑。
- 07 RTT、defmt 与 semihosting:RTT 控制块与通道;defmt 与 semihosting。
- 08 调试信息:本卷另一个重点。DebugInfo 与 DWARF;CFI 栈展开与异常帧;变量与单步。
- 09 调试服务器:DAP server 与 VS Code;gdb server;命令行的 RPC 架构。
- 10 和 embassy 连起来:把 embassy 卷的固件接到 probe-rs 的工作流上;cargo runner 与 defmt 三件套、从 QEMU 换到真板子、VS Code 调试配置。
- 11 设计取舍:把各章的取舍汇总到一起。
- 12 常见坑:用报错原文做标题,讲它在源码里怎么产生。
- 13 动手:写一个最小的「软件探针」;用 QEMU 的 GDB 服务端让 probe-rs-debug 展开活固件。
💬 关联讨论
发起讨论加载中...