Rust in Everything
基于 embassy-net 0.9.1

packet-trace 加 log:[u8]: LowerHex is not satisfied(上游 bug)

主线位置:网络驱动收发帧的那一层,Runner 把驱动的 RxToken / TxToken 包成 smoltcp 的令牌。DriverAdapter 和令牌适配器见驱动接口。

flowchart TB
  f["packet-trace"] --> t["trace 打印 buf"]
  t --> which{"日志后端"}
  which -->|defmt| ok1["02x 支持切片"]
  which -->|log| err["core fmt 不支持"]
  which -->|都没有| ok2["不展开"]
  err --> e["E0277 LowerHex"]

实验 crate exp-embassy/pitb-ptrace,bin p27_trace(platform-std,release):两个 embassy-net 栈用进程内的假驱动接起来,A 给 B 发一个 UDP 包。M2 Max、macOS 27、rustc 1.99.0。

现象

想看网卡收发的每一帧,在 std 上调试,日志走 log + env_logger:

embassy-net = { version = "=0.9.1", features = ["std", "log", "packet-trace", "udp", "medium-ethernet", "proto-ipv4"] }
error[E0277]: the trait bound `[u8]: LowerHex` is not satisfied
  --> ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/embassy-net-0.9.1/src/driver_util.rs:92:49
   |
92 |             trace!("embassy device rx: {:02x}", buf);
   |                                        ------   ^^^ the trait `LowerHex` is not implemented for `[u8]`
   |                                        |
   |                                        required by this formatting parameter
   |
   = help: the following other types implement trait `LowerHex`:
...
   = note: required for `&mut [u8]` to implement `LowerHex`
   = note: this error originates in the macro `$crate::__private_api::format_args` which comes from the expansion of the macro `trace` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `[u8]: LowerHex` is not satisfied
   --> ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/embassy-net-0.9.1/src/driver_util.rs:113:49
...
error: could not compile `embassy-net` (lib) due to 2 previous errors

报错在依赖的源码里,自己的代码一行没动。

出处

driver_util.rs 的接收令牌适配器(consume)和发送令牌适配器(consume)各有一行:

            #[cfg(feature = "packet-trace")]
            trace!("embassy device rx: {:02x}", buf);

trace! 是 embassy-net 自己的 fmt.rs 里的宏:开了 log 就展开成 ::log::trace!,开了 defmt 就展开成 ::defmt::trace!,两者都没开就只是 let _ = (&buf);。{:02x} 对字节切片只在 defmt 里成立(defmt 的格式化把它当成「每个元素按十六进制」),core::fmt 的 LowerHex 没有为 [u8] 实现。所以:

日志后端packet-trace
defmt编译,按十六进制打印
logE0277
都不开编译,什么也不打印

embassy-net 0.9.1 自己的 CI 配置(Cargo.toml 的 [package.metadata] 构建矩阵)里,带 packet-trace 的两行都配的是 defmt,所以没有发现。

📝NOTE
冷知识:一次「改进日志」把它弄坏了

packet-trace 是提交 01b0af5a8「net: add packet-trace feature.」(2024-01,首个包含它的 tag 是 embassy-net-v0.4.0)加的,当时那两行是 trace!("rx: {:?}", buf) 和 trace!("tx: {:?}", buf):{:?} 对 log 和 defmt 都成立。PR #3488 里的提交 af694d233「chore: improve some log msgs」(2024-11,首个包含它的 tag 是 embassy-net-v0.5.0)把它们改成了现在的 "embassy device rx: {:02x}",十六进制在 defmt 下更好读,log 这条路从此编不过。之后的 0.6 到 0.9.1 都没有改。main 上 embassy-net 换到 xarxa 之后(daac2c6ab「net: port to new xarxa」,未发版)packet-trace 这个 feature 已经不存在了。

修法

本地补丁:把 embassy-net 0.9.1 复制到项目里,两行的 {:02x} 改成 {:02x?}(Debug 加十六进制,对切片成立),用 [patch.crates-io] 指过去:

92c92
<             trace!("embassy device rx: {:02x}", buf);
---
>             trace!("embassy device rx: {:02x?}", buf); // pitb: {:02x} -> {:02x?}
113c113
<             trace!("embassy device tx: {:02x}", buf);
---
>             trace!("embassy device tx: {:02x?}", buf); // pitb: {:02x} -> {:02x?}
[patch.crates-io]
embassy-net = { path = "embassy-net-patched" }

RUST_LOG=embassy_net=trace 运行:

[INFO  embassy_net] link_up = true
[TRACE embassy_net::driver_util] embassy device tx: [ff, ff, ff, ff, ff, ff, 02, 00, 00, 00, 00, 0a, 08, 06, 00, 01, 08, 00, 06, 04, 00, 01, 02, 00, 00, 00, 00, 0a, 0a, 00, 00, 01, ff, ff, ff, ff, ff, ff, 0a, 00, 00, 02]
...
[TRACE embassy_net::driver_util] embassy device rx: [02, 00, 00, 00, 00, 0b, 02, 00, 00, 00, 00, 0a, 08, 00, 45, 00, 00, 1e, 00, 00, 40, 00, 40, 11, 26, cd, 0a, 00, 00, 01, 0a, 00, 00, 02, 00, 09, 00, 09, 00, 0a, 83, 5c, 68, 69]
B got [104, 105] from 10.0.0.1:9

第一帧是 A 的 ARP 请求(目的 MAC 全 ff,类型 08 06),最后一帧是那个 UDP 包,末尾的 68 69 就是 "hi"。

不打补丁的两个办法:

  • 去掉 embassy-net 的 log feature(保留 packet-trace)能编过,但 trace! 什么也不展开,实测 0 行帧日志;只适合「先让它编过」。注意 log 也可能被别的依赖经 feature 合并打开。
  • 不用 packet-trace,在自己的驱动里打印:RxToken::consume / TxToken::consume 拿得到同一块 buf,在那里 log::trace!("{:02x?}", buf)(驱动接口里手写驱动的那一节就是这两个方法)。

怎么确认没中招

  • 报错位置在 embassy-net-0.9.1/src/driver_util.rs:92 和 :113,trait 是 LowerHex:就是这个。
  • cargo tree -e features -i embassy-net --offline 看 log 和 packet-trace 是否同时打开,以及是谁打开的。
  • 嵌入式目标上用 defmt 时这个组合是好的,不用改。

小结

  • embassy-net 0.9.1 的 packet-trace 用 {:02x} 打印 &[u8],只有 defmt 支持;和 log 一起开就是 E0277(上游 bug,自 0.5.0 起,main 上这个 feature 已随 xarxa 移植去掉,未发版)。
  • 修法:本地补丁改成 {:02x?};或去掉 log(但什么也不打印);或在自己的驱动令牌里打印帧。

💬 关联讨论

发起讨论
加载中...