panic 的位置在 burn-tensor 里,不在我的代码里
主线位置:算子主线第一步的第一行。
Tensor::matmul先check!(TensorCheck::matmul(..)),形状不对就在这里 panic,调用不会进入Dispatch(检查与报错)。panic 报的位置是这一行,而不是调用它的那一行。
flowchart TB user["用户代码 project"] --> mm["Tensor::matmul"] mm --> chk["check! 宏"] chk --> pnc["panic 位置 numeric.rs:842"] pnc --> bt["RUST_BACKTRACE=1"] bt --> frame["找第一个自己的帧"] user --> ash["先 assert_shape!"] ash --> own["panic 落在自己的行"]
本页实验 crate pa-dev(模板的 feature,debug 构建),平台 M2 Max、macOS 27、rustc 1.99.0。
现象
形状错误发生在一个辅助函数里,辅助函数又被 main 调用:
// A helper in user code, two calls away from main.
fn project(x: Tensor<2>, w: Tensor<2>) -> Tensor<2> {
x.matmul(w)
}
// main 里:
let x = Tensor::<2>::ones([4, 3], &Device::flex());
let w = Tensor::<2>::ones([4, 2], &Device::flex());
let _ = project(x, w);
thread 'main' (3527862) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/numeric.rs:842:9:
=== Tensor Operation Error ===
Operation: 'Matmul'
Reason:
1. The inner dimension of matmul should be the same, but got 3 and 4. Lhs shape Shape { dims: [4, 3] }, rhs shape Shape { dims: [4, 2] }.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101
位置是 cargo 缓存里的 numeric.rs:842,消息里有形状,没有你的文件名和行号。模型里有几十个 matmul 时,这行信息不够用。别的检查更偏:reshape 的 panic 落在 check.rs 里一个公共函数上,sum_dim 的落在 numeric.rs 里,两个都和你的调用点无关:
### reshape
thread 'main' (3528863) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/check.rs:1394:13:
=== Tensor Operation Error ===
Operation: 'Reshape'
Reason:
1. The given shape doesn't have the same number of elements as the current shape. Current shape: [2, 3], target shape: [4, 2].
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101
### dim
thread 'main' (3528877) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/numeric.rs:419:19:
=== Tensor Operation Error ===
Operation: 'Sum Dim'
Reason:
1. BoundsError: dimension 2 out of bounds: 0..2
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101
出处
check! 宏定义在 check.rs 里,注释说明了为什么用宏:
/// We use a macro for all checks, since the panic message file and line number will match the
/// function that does the check instead of a generic error.rs crate private unrelated file
/// and line number.
macro_rules! check {
($check:expr) => {
if let TensorCheck::Failed(check) = $check {
core::panic!("{}", check.format());
}
};
}
宏展开在哪个函数里,panic! 的位置就是哪一行,所以 matmul 的报错停在 Tensor::matmul 的第一行(842)。这比停在一个公共的报错文件里好,但只好到 burn 自己的源码为止:把位置带到调用方要靠 #[track_caller],而 matmul、add、reshape 这些公开方法都没有它。整个 burn-tensor 里带 #[track_caller] 的只有读数据的七个方法(into_data、to_data、into_scalar 等)和一个内部函数 unwrap_dim_index。后者的位置会传给调用它的 sum_dim,可 sum_dim 自己没有这个属性,于是停在 numeric.rs:419。
reshape 走的是 unwrap_shape_reshape:形状计算返回错误后,在这个公共函数里再用 check! panic,位置就成了 check.rs:1394,正是注释想避免的「generic」位置。
check! 宏和上面那段注释来自 #283(「Feat/add tensor checks」,2023-04,首次发布于 v0.7.0),这个 PR 新增了整个 check.rs。三年后的 #5316(「feat(tensor)!: expand TensorData conversion and access APIs」,首次发布于 v0.22.0-pre.3)把 into_data、to_data 从直接调用改成 self.try_into_data().expect("Error while reading data: use `try_into_data` instead to catch the error at runtime"),同时给它们加上 #[track_caller],提交说明里写的是「replace panics in into_data, to_data, and scalar methods with try_* alternatives, add clearer error messages」。所以 0.22.1 里读数据失败的 panic 指向你的代码行,形状检查失败的 panic 仍指向 burn 内部。
修法
第一种,打开回溯,找第一个属于自己 crate 的帧:
thread 'main' (3527464) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/numeric.rs:842:9:
=== Tensor Operation Error ===
Operation: 'Matmul'
Reason:
1. The inner dimension of matmul should be the same, but got 3 and 4. Lhs shape Shape { dims: [4, 3] }, rhs shape Shape { dims: [4, 2] }.
stack backtrace:
0: __rustc::rust_begin_unwind
at /rustc/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/std/src/panicking.rs:679:5
1: core::panicking::panic_fmt
at /rustc/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/core/src/panicking.rs:80:14
2: core::panicking::panic_display::<alloc::string::String>
at ~/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/panicking.rs:259:5
3: <burn_tensor::tensor::api::base::Tensor<2>>::matmul
at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/check.rs:1382:17
4: pa_dev::project
at ./src/main.rs:13:5
5: pa_dev::main
at ./src/main.rs:163:15
6: <fn() as core::ops::function::FnOnce<()>>::call_once
at ~/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
exit=101
帧 4 是 project 里的 x.matmul(w)(main.rs:13),帧 5 是 main 调 project 的地方。帧 3 的行号写成 check.rs:1382,是 check! 宏里 panic! 所在的行:宏展开的代码在回溯里按宏定义处记行号。
第二种,在自己的函数入口先断言形状。assert_shape! 是宏,展开在你的代码里,panic 就落在你的行上:
let [_, k] = x.dims();
burn::tensor::assert_shape!(w, [k, 2]);
let _ = project(x, w);
thread 'main' (3527864) panicked at src/main.rs:169:39:
assert_shape!(w, [k, 2]): axis 0 expected 3, got 4 (dims [4, 2])
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101
消息里还带着表达式原文 assert_shape!(w, [k, 2])。只想在调试构建里检查,用 debug_assert_shape!。
怎么确认没中招
- panic 位置在
~/.cargo/registry/.../burn-tensor-*/src/tensor/api/下:这是TensorCheck的 panic,消息开头是=== Tensor Operation Error ===,Operation:一行告诉你是哪个 op。 - 开发时在 shell 里常设
RUST_BACKTRACE=1,回溯里第一个不在burn_*/core/std里的帧就是调用点。 - 在每个
forward的入口给输入加一行assert_shape!或debug_assert_shape!,形状问题会在你自己的层里报出来。
小结
TensorCheck的 panic 位置停在burn-tensor里(numeric.rs:842、check.rs:1394、numeric.rs:419),公开的张量方法没有#[track_caller]。- 读数据的
into_data、to_data、into_scalar等有#[track_caller](#5316),它们的 panic 指向你的代码。 - 修法:
RUST_BACKTRACE=1找第一个自己的帧;或在入口用assert_shape!,让 panic 落在自己的行上。