probe-rs run 跑起来了,一行日志也没有
主线位置:命令主线第五步「轮询」里的「读 RTT」。控制块怎么找、怎么校验在 RTT 控制块与通道 里讲过;本页讲找不到时命令行的反应:它不报错。
flowchart TB
elf["parse_metadata:ELF 里有 _SEGGER_RTT 吗"] -->|有| exact["ScanRegion::Exact(地址)"]
elf -->|没有| arg{"给了 --scan-region 吗"}
arg -->|"没给:默认空串"| empty["Ranges(空)"]
arg -->|给了| scan["Ram 或 Ranges:整段读出来找魔数"]
empty --> nl["NoControlBlockLocation:不再尝试"]
exact --> at["attach_at:读 24 字节头"]
scan --> at
at -->|"魔数不对"| nf["ControlBlockNotFound:下一轮再试"]
at -->|对| ok["连上,按通道名选解码器"]
nl --> silent["终端:没有任何 RTT 输出,也没有错误"]
nf --> silent
现象
固件跑起来了(烧录成功、probe-rs run 没有退出),终端上却什么都没有;没有报错,Ctrl-C 后正常退出。命令行把 RTT 的两种「找不到」都当成正常状态,只打 debug 级别的日志:
// probe-rs-tools/src/bin/probe-rs/util/rtt/client.rs,try_attach_impl
let location = match Rtt::find_control_block(core, &self.scan_region) {
Ok(location) => location,
Err(Error::ControlBlockNotFound) => {
tracing::debug!("Failed to attach - control block not found");
return Ok(false);
}
Err(Error::NoControlBlockLocation) => {
tracing::debug!("Failed to attach - control block location not specified");
self.try_attaching = false;
return Ok(false);
}
Err(error) => return Err(error),
};
(client.rs:80)后面 attach_at 返回 ControlBlockNotFound 或 ControlBlockCorrupted 时同样只打 debug 日志。要看到它们,设 RUST_LOG=debug(命令行没给日志级别时读这个环境变量)。
同样两种错误交给库的调用方时是这样(FakeProbe 上 attach STM32L475VGTx,cargo runner 与 defmt 三件套里的固件;FakeProbe 读 RAM 得 0):
== find_rtt_control_block_in_raw_file(fw.elf) -> Some(0x20000008)
== find_rtt_control_block_in_raw_file(fw-stripped.elf) -> None
== Rtt::attach_region(Ranges(vec![])) -> Err("There is no control block location given. This usually means RTT is not present in the
firmware.")
== Rtt::attach_region(Exact(0x20000008)) -> Err("RTT control block not found in target memory.
- Make sure RTT is initialized on the target, AND that there are NO target breakpoints before RTT initialization.
- For VSCode and probe-rs-debugger users, using `halt_after_reset:true` in your `launch.json` file will prevent RTT
initialization from happening on time.
- Depending on the target, sleep modes can interfere with RTT.")
两条路
没有位置:NoControlBlockLocation,从此放弃。 parse_metadata 在 ELF 符号表里找不到 _SEGGER_RTT,就用 --scan-region 的值,而它的默认值是空串,parse_scan_region 把空串解析成空的范围列表。find_control_block 对空列表直接返回 NoControlBlockLocation,RttClient 把 try_attaching 置假,整个 run 期间不再读 RTT。--help 里写着这条规则:「If no region is specified, probe-rs will not scan and will not poll RTT.」
什么时候会没有符号:
- ELF 被 strip 了。上面实验里
rust-objcopy --strip-all之后find_rtt_control_block_in_raw_file返回None;同时.defmt段也没了,就算扫到控制块,defmt 也解不了码。 - 传给
run的不是 ELF(--binary-format hex/bin),没有符号表可读。 - 固件里根本没有 RTT:没依赖
defmt-rtt或rtt-target,或者日志走的是 semihosting、串口。
有位置但魔数不对:ControlBlockNotFound,每轮重试。 有了地址,每一轮轮询都 attach_at 一次,读到的 16 字节不是 "SEGGER RTT" 就算没找到,下一轮再来(RTT 控制块与通道)。所以这种情况会自愈:控制块一出现就连上。持续不出现的原因:
| 原因 | 说明 |
|---|---|
| 固件停在 RTT 初始化之前 | 报错原文的第一条。defmt-rtt 的控制块在 .data 里,启动代码拷完 .data 就有;rtt-target 0.6 的控制块是没有初值的 MaybeUninit,要等 rtt_init! 执行时才写进去。断点下在这之前、或固件在初始化前就 HardFault,都看不到 |
VS Code 里 haltAfterReset: true | 报错原文的第二条:核停在复位处,.data 还没拷(VS Code 调试配置) |
| 芯片睡眠影响调试访问 | 报错原文的第三条;哪些芯片会这样待实测 |
| 读到的是上一次运行的旧控制块 | 反过来的情况:命令行在监控开始时会先清掉控制块(RTT 控制块与通道),避免连上旧的、通道配置不对的那一份 |
还有一种不是「没有输出」而是「输出是乱码」:通道名叫 defmt 而 ELF 里读不出 defmt 表时,原样按二进制打印(defmt 与 semihosting)。
DAP 那边多一个开关
VS Code 里还有一层:rttEnabled 默认是 false,不打开它 dap-server 根本不碰 RTT(VS Code 调试配置)。
怎么办
- 确认 ELF 带符号:
nm <elf> | grep _SEGGER_RTT。有它,命令行用确切地址,不需要扫描。 - 只能用没有符号的镜像时,显式给
--scan-region ram(扫所有 RAM)或一个范围。扫描就是把整段 RAM 读出来找魔数,模拟目标上 20 KiB 是 5149 次访问(RTT 控制块与通道),范围越小越好。 --list-rtt列出通道后退出,能确认连没连上、有哪些通道。- 设
RUST_LOG=debug看是哪一种「找不到」。 - 固件里尽早初始化 RTT;用
defmt-rtt的话这一条天然满足。
小结
- RTT 找不到时命令行不报错:
NoControlBlockLocation让它整场不再尝试,ControlBlockNotFound让它每轮重试,两者都只写 debug 日志。 - 没有
_SEGGER_RTT符号(strip、非 ELF、固件没有 RTT)又没给--scan-region时,属于前者;控制块还没初始化(断点、haltAfterReset、早期 HardFault)属于后者。 - 带着符号运行、必要时
--scan-region,用--list-rtt和RUST_LOG=debug确认;VS Code 还要打开rttEnabled。