Rust in Everything
基于 burn 0.22.1

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」位置。

📝NOTE
冷知识:读数据的方法先有了 #[track_caller]

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 落在自己的行上。

💬 关联讨论

发起讨论
加载中...