Rust in Everything
基于 probe-rs 0.32.0

No available hardware breakpoints

主线位置:调试主线前半段的「FPB 设硬件断点」。probe-rs 在 Cortex-M 上不写软件断点,每个断点占一个 FPB 比较器;比较器怎么编码见 Cortex-M 内核。本页讲比较器用完时三个前端各自的反应。

flowchart TB
  req["下断点:gdb 的 Z1、DAP 的 setBreakpoints、库的 set_hw_breakpoint"] --> hw["Core::hw_breakpoints:读 FP_CTRL 和每个 FP_COMPn"]
  hw --> free{"有空闲或同地址的比较器"}
  free -->|有| set["写 FP_COMPn"]
  free -->|没有| err["Error::Other:No available hardware breakpoints"]
  err --> gdb["gdb server:TargetError::Fatal,会话退出"]
  err --> dap["DAP:这一个断点 verified false"]
  err --> lib["库:返回 Err"]

现象

本卷的软件模拟目标设了 4 个比较器(真实芯片的个数待实测),连下 5 个断点:

breakpoint units = 4; set 5 breakpoints: [Ok(()), Ok(()), Ok(()), Ok(()), Err("No available hardware breakpoints")]

probe-rs gdb --dry-run 背后的 FakeProbe 读 FP_CTRL 得 0,一个都没有,lldb 的第一个断点就撞上,而且 lldb 那头的表现是 Resume timed out,服务端的最后几行是(gdb server):

Write 0xe0002000 = 0x00000003
Read 0xe0002000 = 0
During the execution of GDB an error was encountered:
Target threw a fatal error: No available hardware breakpoints

服务端进程随之结束。

从哪来

Core::set_hw_breakpoint 先确保 FPB 全局打开,再读出每个比较器的当前地址:已有同地址的就复用,否则找第一个空的,找不到:

// probe-rs/src/core.rs
        let breakpoint_comparator_index =
            match breakpoints.iter().position(|&bp| bp == Some(address)) {
                Some(breakpoint_comparator_index) => breakpoint_comparator_index,
                None => breakpoints
                    .iter()
                    .position(|bp| bp.is_none())
                    .ok_or_else(|| Error::Other("No available hardware breakpoints".to_string()))?,
            };

比较器个数是 FP_CTRL 的 NUM_CODE,由芯片实现决定。这里没有软件断点的退路:Cortex-M 的代码多半在 flash 里,写 BKPT 要擦写扇区,probe-rs 不做(gdb server)。

还有一种相近的失败:FPB rev 1(FP_CTRL.REV = 0,Armv7-M 上常见)的比较器只能匹配 0x2000_0000 以下的地址,断在 RAM 里的代码上会得到 UnsupportedBreakpointAddress:「Unable to create a breakpoint at address … Hardware breakpoints are only supported at addresses < 0x2000_0000.」。

三个前端的反应

前端处理用户看到的
probe-rs gdbadd_hw_breakpoint 对组内每个核设断点,错误走 into_target_result:除「核未启用」外一律 TargetError::Fatalgdbstub 状态机退出,服务端打印上面那两行后结束,GDB 失去连接
dap-serverset_breakpoints 逐个设,失败的那个在响应里 verified: false、message 是错误原文、reason 是 failedVS Code 里那个断点显示为未验证(灰色),会话照常
库Err(Error::Other(..))自己处理

gdb server 这一行最伤人:GDB 用户习惯随手下很多断点,第 N+1 个断点不是失败一次,而是让整个调试会话断掉。原因是 into_target_result 把错误分得很粗,只有 CoreDisabled 映射成 EIO。

另有两处会悄悄占用比较器:

  • gdb 的断点对组内每个核都设。同类型的多核芯片上,每个核各占一个,但各核的 FPB 是独立的,个数不会互相挤占。
  • 语句级单步要借一个。step over / into / out 用「在目标地址下临时断点再 run」实现,全占满时借 0 号,跑完写回原值(变量与单步)。所以占满了断点仍能单步,不会因此报这个错。

怎么办

  • 先看芯片有几个:库里 core.available_breakpoint_units()。按 ARM 各内核的技术参考手册,Cortex-M3 / M4 的 FPB 有 6 个指令比较器,Cortex-M0 / M0+ 最多 4 个,芯片厂商还可以配置得更少;实际读出来的数待实测(登记在硬件待测清单的 HW-4)。
  • 用 GDB 时控制断点数,删掉不用的(delete)再下新的;需要大量断点、条件断点时换 VS Code + dap-server,多出来的只是未验证,不会断开会话。
  • 想在 RAM 里运行的代码上下断点,先确认 FPB 版本;rev 1 上只能靠单步或在代码里放 cortex_m::asm::bkpt()。

小结

  • probe-rs 在 Cortex-M 上只有硬件断点,个数等于 FPB 的 NUM_CODE,用完时 Core::set_hw_breakpoint 报「No available hardware breakpoints」。
  • gdb server 把它当致命错误,GDB 会话随之结束;DAP 把那个断点标成 verified: false,会话继续。
  • FPB rev 1 不能断在 0x2000_0000 以上,是另一个错误;单步借 0 号比较器,不受「占满」影响。

💬 关联讨论

发起讨论
加载中...