2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

18日目の記事の続きということで、 std::backtrace::Backtrace についてもう少し掘り下げたいと思います!

バックトレースの元になる情報の場所 (ELFの場合)

バイナリコード中のどこにデバッグ情報があるか・どうやってバックトレースを生成しているのか知りたく、色々調べたのですが結局よくわかってないです!:bow::bow::bow: ChatGPTから聞いたことをまとめました。

Linuxの実行ファイル形式の一つであるELFの場合、 .symtab というセクションに関数名が入っているそうです。

そしてファイル名や行番号は別なところに入っていて、デバッグ情報が必要らしいですね。デバッグビルドを行うとDWARFという規格1により記述されたデバッグ情報が埋め込まれるそうで、ここの情報を元にして得られるようです。

デバッグ情報が埋め込まれているかは file コマンドで確かめることが可能です。

$ file target/debug/backtrace_pg
target/debug/backtrace_pg: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d74c675df3fc991f62d74bc4b143ed5d1452ed28, with debug_info, not stripped

後ろの方に(Qiita上だとスクロールが必要かもしれません) with debug_info と書かれていればバイナリにデバッグ情報が含まれています。

デバッグ情報については、 readelf というコマンドを叩き、 .debug_* みたいな形式の名前になっているセクションがあればその中に入っているらしいです。

$ readelf -WS target/debug/backtrace_pg | grep -E '\.(debug_|symtab|dynsym|eh_frame)'
  [ 4] .dynsym           DYNSYM          0000000000000340 000340 0006a8 18   A  8   1  8
  [13] .debug_gdb_scripts PROGBITS        000000000000e238 00e238 000022 01 AMS  0   0  1
  [14] .eh_frame_hdr     PROGBITS        000000000000e25c 00e25c 00125c 00   A  0   0  4
  [15] .eh_frame         PROGBITS        000000000000f4b8 00f4b8 005a4c 00   A  0   0  8
  [33] .debug_abbrev     PROGBITS        0000000000000000 05ae18 001899 00      0   0  1
  [34] .debug_info       PROGBITS        0000000000000000 05c6b1 106f48 00      0   0  1
  [35] .debug_aranges    PROGBITS        0000000000000000 1635f9 007b80 00      0   0  1
  [36] .debug_ranges     PROGBITS        0000000000000000 16b179 06f580 00      0   0  1
  [37] .debug_str        PROGBITS        0000000000000000 1da6f9 1641bc 01  MS  0   0  1
  [38] .debug_line       PROGBITS        0000000000000000 33e8b5 06ed63 00      0   0  1
  [39] .symtab           SYMTAB          0000000000000000 3ad618 006a20 18     41 801  8

行番号は .debug_line セクションになるんですかね?本当はもっと詳細に踏み込みたかったのですが今の自分の知識ではこれ以上踏み込めなかったので、一旦保留にします 🫠

release時もバックトレースを得たい場合: カスタムプロファイル

デバッグビルドでは詳細なバックトレースを出力できますが、リリースビルドではバックトレースの情報がかなり少なくなってしまいます。

src/main.rs
use std::backtrace::Backtrace;

fn func1() -> Backtrace {
    Backtrace::capture()
}

fn func2() -> Backtrace {
    func1()
}

fn func3() -> Backtrace {
    func2()
}

fn main() {
    let bt = func3();
    println!("Status: {:?}\n---\n{}", bt.status(), bt);
}
デバッグビルド
$ RUST_BACKTRACE=1 cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/backtrace_pg`
Status: Captured
---
   0: backtrace_pg::func1
             at ./src/main.rs:4:5
   1: backtrace_pg::func2
             at ./src/main.rs:8:5
   2: backtrace_pg::func3
             at ./src/main.rs:12:5
   3: backtrace_pg::main
             at ./src/main.rs:16:14
   4: core::ops::function::FnOnce::call_once
             at /home/namn/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
   5: std::sys::backtrace::__rust_begin_short_backtrace
             at /home/namn/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:158:18
   6: std::rt::lang_start::{{closure}}
             at /home/namn/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:206:18
   7: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/core/src/ops/function.rs:287:21
   8: std::panicking::catch_unwind::do_call
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/panicking.rs:590:40
   9: std::panicking::catch_unwind
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/panicking.rs:553:19
  10: std::panic::catch_unwind
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/panic.rs:359:14
  11: std::rt::lang_start_internal::{{closure}}
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/rt.rs:175:24
  12: std::panicking::catch_unwind::do_call
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/panicking.rs:590:40
  13: std::panicking::catch_unwind
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/panicking.rs:553:19
  14: std::panic::catch_unwind
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/panic.rs:359:14
  15: std::rt::lang_start_internal
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/rt.rs:171:5
  16: std::rt::lang_start
             at /home/namn/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:205:5
  17: main
  18: <unknown>
  19: __libc_start_main
  20: _start
リリースビルド
$ RUST_BACKTRACE=1 cargo run --release
    Finished `release` profile [optimized] target(s) in 0.00s
     Running `target/release/backtrace_pg`
Status: Captured
---
   0: backtrace_pg::main
   1: std::sys::backtrace::__rust_begin_short_backtrace
   2: std::rt::lang_start::{{closure}}
   3: std::rt::lang_start_internal
   4: main
   5: <unknown>
   6: __libc_start_main
   7: _start

関数名も行番号もわからなくなりました...

ですが安心してください。 Cargo.toml にて debug = true を指定したプロファイルを作成すれば、引き続きリリースビルドでもデバッグ情報を出せます。そこまでして出したい...?

引用元: https://stackoverflow.com/questions/38803760/how-to-get-a-release-build-with-debugging-information-when-using-cargo

Cargo.toml
[profile.release-with-debug]
inherits = "release"
debug = true
デバッグ情報付きリリースビルド
$ RUST_BACKTRACE=1 cargo run --profile=release-with-debug
   Compiling backtrace_pg v0.1.0 (/home/namn/workspace/qiita_adv_articles_2025/programs/adv18/backtrace_pg)
    Finished `release-with-debug` profile [optimized + debuginfo] target(s) in 0.50s
     Running `target/release-with-debug/backtrace_pg`
Status: Captured
---
   0: backtrace_pg::func1
             at ./src/main.rs:4:5
   1: backtrace_pg::func2
             at ./src/main.rs:8:5
   2: backtrace_pg::func3
             at ./src/main.rs:12:5
   3: backtrace_pg::main
             at ./src/main.rs:16:14
   4: core::ops::function::FnOnce::call_once
             at /home/namn/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
   5: std::sys::backtrace::__rust_begin_short_backtrace
             at /home/namn/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:158:18
   6: std::rt::lang_start::{{closure}}
             at /home/namn/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:206:18
   7: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/core/src/ops/function.rs:287:21
   8: std::panicking::catch_unwind::do_call
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/panicking.rs:590:40
   9: std::panicking::catch_unwind
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/panicking.rs:553:19
  10: std::panic::catch_unwind
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/panic.rs:359:14
  11: std::rt::lang_start_internal::{{closure}}
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/rt.rs:175:24
  12: std::panicking::catch_unwind::do_call
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/panicking.rs:590:40
  13: std::panicking::catch_unwind
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/panicking.rs:553:19
  14: std::panic::catch_unwind
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/panic.rs:359:14
  15: std::rt::lang_start_internal
             at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library/std/src/rt.rs:171:5
  16: main
  17: <unknown>
  18: __libc_start_main
  19: _start

ちなみにダイマなんですけどhooqマクロならプロファイルに関係なくエラートレースもどきを付けられますし、プロファイル次第で細かくトレース内容を決められます!

詳しくは今日までのアドカレ記事を見ていただけると幸いです! :bow:

WebAssemblyでBacktrace::statusを見てみる

「プラットフォームによってはサポートされない」とのことですが、サポートされないプラットフォームが実際にあるのか気になりますね...?

というわけで...あ、 Wasm君ちょうど良いところに 。君ちょっとバックトレース見せなさい!

src/lib.rs
use wasm_bindgen::prelude::*;

use std::backtrace::Backtrace;

fn func1() -> Backtrace {
    Backtrace::force_capture()
}

fn func2() -> Backtrace {
    func1()
}

fn func3() -> Backtrace {
    func2()
}

#[wasm_bindgen]
pub fn show_trace() -> String {
    let bt = func3();
    format!("Status: {:?}\n---\n{}", bt.status(), bt)
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="output"></div>
    <script type="module">
        import init, { show_trace } from "./pkg/backtrace_wasm_pg.js";

        init().then(() => {
            const output = document.getElementById("output");
            output.textContent = show_trace();
        });
    </script>
</body>
</html>
Cargo.toml
Cargo.toml
[package]
name = "backtrace_wasm_pg"
version = "0.1.0"
edition = "2024"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2.106"
$ wasm-pack build --target web --dev
[INFO]: 🎯  Checking for the Wasm target...
[INFO]: 🌀  Compiling to Wasm...
   Compiling backtrace_wasm_pg v0.1.0 (/home/namn/workspace/qiita_adv_articles_2025/programs/adv18/backtrace_wasm_pg)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.52s
[INFO]: ⬇️  Installing wasm-bindgen...
[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
[INFO]: ✨   Done in 0.81s
[INFO]: 📦   Your wasm pkg is ready to publish at /home/namn/workspace/qiita_adv_articles_2025/programs/adv18/backtrace_wasm_pg/pkg.
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

wasm-unsupported.png

_人人人人人人人_
> Unsupported <
 ̄Y^Y^Y^Y^Y^Y^Y^ ̄

デバッグビルドでしたがデバッグ情報を取得できなかったみたいです...この辺りが環境依存ということみたいですね。

Wasmでバックトレースを見る方法 (パニック時限定)

console_error_panic_hookを使うことでパニック時にバックトレース(にあたるもの)をデバッグコンソールで見れます!

以下を初期化の最初に置いておく必要があります。

Rust
console_error_panic_hook::set_once();

ただ、 std::backtrace::Backtrace が有効になるというわけではなく、 anyhow::Error などとの相性は悪そうです...(せっかくanyhowはWasmでも使えるのに...)

多分他のランタイムを使ったりすればWebAssemblyでも std::backtrace::Backtrace の詳細を見られるかもしれませんが、筆者があまり詳しくないのと今回の目的はサポートされていないプラットフォームの確認なのでこれ以上の検証は省略します :bow:

それとまたまたダイマなのですがhooqマクロならWebAssemblyでもエラートレースもどきを取得できます!

記事としてはまとめていませんが、動くことは実証済みなのでぜひ使ってみてほしいです!

まとめ・所感

というわけで、 hooqアドベントカレンダー 19日目の記事でした!

hooqはメソッドを ? 演算子にフックする属性マクロです。本アドカレではhooqの使い方を始め、hooqマクロを作成するにあたり得た知識、Rustのエラーハンドリング・エラーロギング周りの話をまとめています。

昨日と本日の記事を通して示した通り std::backtrace::Backtrace はある意味hooqのライバルであるところのバックトレース系列技術の根幹の一つであるため、この機会に色々知ることができてよかったです!

ここまで読んでいただきありがとうございました!

  1. ELF(エルフ)だからDWARF(ドワーフ)という洒落のようです。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?