[Rust]macOSでpanicが発生した箇所のファイル名と行番号を調べる

# macOS版の`Rust`でpanic時のスタックトレースに行番号を出す
macの環境でrustのプログラムを実行して`panic`したときに、スタックトレースを見てもどこでおちたのかわからなかったので、調べてみました。

## 試したバージョン
- macos

```
% cargo --version
cargo-0.18.0-nightly (fdfdb5f 2017-02-16)
```

- linux

```
% docker run --rm -v $(pwd):/source jimmycuadra/rust cargo --version
cargo 0.16.0-nightly (6e0c18c 2017-01-27)
```

## panic時にどこで落ちたか分からない？？

例えば下記のようなプログラムがあったとします。

```main.rs
01: fn main() {
02:    let test: Option<i32> = None;
03:
04:    println!("xxx");
05:    println!("yyy");
06:    println!("test is {}", test.unwrap());
07:    println!("zzz");
08: }

```

変数`test`が`None`なので、6行目の`unwrap()`で`panic`するのですが、 macOS版の`cargo run`で`RUST_BACKTRACE=1`をつけて実行すると下記のようになります。

```
% RUST_BACKTRACE=1 cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/rust-better-stacktrace`
xxx
yyy
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/libcore/option.rs:323
stack backtrace:
   1:        0x10dbefd59 - std::sys::imp::backtrace::tracing::imp::write::h9559bd7cb15d72ad
   2:        0x10dbf122e - std::panicking::default_hook::{{closure}}::h4b3f2b69c9ce844d
   3:        0x10dbf0edb - std::panicking::default_hook::h61d415f2381a7336
   4:        0x10dbf15e7 - std::panicking::rust_panic_with_hook::h8e6300d8e8aca457
   5:        0x10dbf1484 - std::panicking::begin_panic::h08622fbe5a379aac
   6:        0x10dbf13f2 - std::panicking::begin_panic_fmt::ha00d3aa9db91f578
   7:        0x10dbf1357 - rust_begin_unwind
   8:        0x10dc11a20 - core::panicking::panic_fmt::h58d018e87f211baf
   9:        0x10dc11924 - core::panicking::panic::h4e2dc358c3b23d48
  10:        0x10dbe9634 - <core::option::Option<T>>::unwrap::hd19ecad304e85b2f
  11:        0x10dbe9808 - rust_better_stacktrace::main::hd6e610e90450cf55
  12:        0x10dbf305a - __rust_maybe_catch_panic
  13:        0x10dbf18a6 - std::rt::lang_start::he28fbf70e98a2b18
  14:        0x10dbe98f9 - main
```

11行目に`rust_better_stacktrace::main::hd6e610e90450cf55`があるので、関数`main`の中で起きたことは分かっても具体的に、どのファイルの、どの行なのかわかりませんね。

で、調べてみると、[本家](https://github.com/rust-lang/rust)のissueに

[Backtrace does not include file and line number on non-Linux platforms #24346](https://github.com/rust-lang/rust/issues/24346)

というのが立っていまして、linux以外のOSでは現状スタックトレースにファイル名と行番号がでないとのこと。(https://github.com/rust-lang/rust/issues/24346#issuecomment-170711555)


試しにlinux環境のrustで試してみると下記のようにとてもわかり易くバックトレースが出ます。

```
% RUST_BACKTRACE=1 cargo run
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/rust-better-stacktrace`
xxx
yyy
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libcore/option.rs:323
stack backtrace:

linux/build/src/libcore/macros.rs:21
   --- 長いので省略 ---
  11:     0x556838c49beb - rust_better_stacktrace::main::hfdf401c58b6d709f
                        at /source/src/main.rs:6
   --- 長いので省略 ---
```

`main.rs`の`6行目`で発生したのがぱっと分かっていいですね。

## macでもなんとかファイルと行番号を知る

じゃ、macでは無理なのかと思っていましたが、上記のissueでデバッガー(`lldb`)で実行すれば分かる、というコメントがありました。

https://github.com/rust-lang/rust/issues/24346#issuecomment-221375810

どうも`panic`のときに実行される`rust_begin_unwind`にブレイクポイントを貼っておいて、そこまでのバックトレースをデバッガで見る、ということのようでした。試してみます。


```
# デバッグビルドの方の実行ファイルでlldbを起動
% lldb target/debug/rust-better-stacktrace

# 以下lldbのプロンプト
# rust_begin_unwindにブレイクポイントを貼ります。
(lldb) b rust_begin_unwind
Breakpoint 1: where = rust-better-stacktrace`std::panicking::rust_begin_panic + 4 at panicking.rs:464, address = 0x0000000100009324

# で、実行
(lldb) run

# すると想定通りブレイクポイントで止まります
Process 48841 launched: '/Users/pocari/dev/tmp/20170221/rust-better-stacktrace/rust-better-stacktrace/target/debug/rust-better-stacktrace' (x86_64)
xxx
yyy
rust-better-stacktrace was compiled with optimization - stepping may behave oddly; variables may not be available.
Process 48841 stopped
* thread #1: tid = 0x9be70, 0x0000000100009324 rust-better-stacktrace`std::panicking::rust_begin_panic + 4 at panicking.rs:464, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100009324 rust-better-stacktrace`std::panicking::rust_begin_panic + 4 at panicking.rs:464 [opt]
```

で、ここで`bt`でバックトレースを表示させます。

```
(lldb) bt
* thread #1: tid = 0x9be70, 0x0000000100009324 rust-better-stacktrace`std::panicking::rust_begin_panic + 4 at panicking.rs:464, queue = 'com.apple.main-thread', stop reason = b
  * frame #0: 0x0000000100009324 rust-better-stacktrace`std::panicking::rust_begin_panic + 4 at panicking.rs:464 [opt]
    frame #1: 0x0000000100029a21 rust-better-stacktrace`core::panicking::panic_fmt + 129 at panicking.rs:69 [opt]
    frame #2: 0x0000000100029925 rust-better-stacktrace`core::panicking::panic + 101 at panicking.rs:49 [opt]
    frame #3: 0x0000000100001635 rust-better-stacktrace`core::option::{{impl}}::unwrap<i32>(self=Option<i32> @ 0x00007fff5fbff020) + 85 at macros.rs:21
    frame #4: 0x0000000100001809 rust-better-stacktrace`rust_better_stacktrace::main + 169 at main.rs:6
    frame #5: 0x000000010000b05b rust-better-stacktrace`panic_unwind::__rust_maybe_catch_panic + 27 at lib.rs:98 [opt]
    frame #6: 0x00000001000098a7 rust-better-stacktrace`std::rt::lang_start [inlined] std::panicking::try<(),fn()> + 44 at panicking.rs:429 [opt]
    frame #7: 0x000000010000987b rust-better-stacktrace`std::rt::lang_start [inlined] std::panic::catch_unwind<fn(),()> at panic.rs:361 [opt]
    frame #8: 0x000000010000987b rust-better-stacktrace`std::rt::lang_start + 347 at rt.rs:57 [opt]
    frame #9: 0x00000001000018fa rust-better-stacktrace`main + 42
    frame #10: 0x00007fff8b64e5ad libdyld.dylib`start + 1
```

すると`frame #4`のフレーム情報から

```
frame #4: 0x0000000100001809 rust-better-stacktrace`rust_better_stacktrace::main + 169 at main.rs:6
```
`main.rs`の`6行目`という情報が見えますね！


## まとめ

macでもファイル名と行番号を見ようと思えば見られるようですが、やっぱ面倒ですよね。
最近結構頻繁に目にするようになってきた`Rust`ですが、そのうちlinux以外のOSでもこの辺整備されてくれるといいなーと思います。