Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

コンパイラのデバッグ

この章では、コンパイラをデバッグするためのヒントをいくつか紹介します。 これらのヒントは、何に取り組んでいるかに関係なく役立つことを目指しています。 他の章の中には、 コンパイラの特定の部分に関するアドバイスがあります(例: クエリのデバッグと テストの章LLVM デバッグの 章)。

コンパイラの設定

デフォルトでは、rustc はほとんどのデバッグ情報なしでビルドされます。 デバッグ情報を有効にするには、 bootstrap.toml で rust.debug = true を設定してください。

rust.debug = true を設定すると、多くの異なるデバッグオプション(例: debug-assertionsdebug-logging など)が有効になります。必要であれば個別に調整できますが、多くの人は 単に rust.debug = true を設定します。

GDB を使用して rustc をデバッグしたい場合は、bootstrap.toml に次のオプションを設定してください。

rust.debug = true
rust.debuginfo-level = 2

注: これは大量のディスク容量を使用します ( 35GB 以上)し、 コンパイル時間も大幅に長くなります。 debuginfo-level = 1debug = true のときのデフォルト)では、 実行パスを追跡できますが、 デバッグ用のシンボル情報は失われます。

デフォルト設定では、symbol-mangling-version v0 が有効になります。 これには少なくとも GDB v10.2 が必要です。 そうでない場合は、bootstrap.toml で新しい symbol-mangling-version を無効にする必要があります。

rust.new-symbol-mangling = false

詳細については、bootstrap.example.toml のコメントを参照してください。

設定オプションを変更した後は、コンパイラをリビルドする必要があります。

ICE ファイルの抑制

デフォルトでは、rustc が Internal Compiler Error(ICE)に遭遇すると、ICE の内容を rustc-ice-<timestamp>-<pid>.txt という名前の ICE ファイルとして現在の作業ディレクトリにダンプします。 これが望ましくない場合は、RUSTC_ICE=0 を使用して ICE ファイルが作成されないようにできます。

バックトレースの取得

ICE(コンパイラ内の panic)が発生した場合、通常の Rust プログラムと同様に RUST_BACKTRACE=1 を設定して panic! のスタックトレースを取得できます。 記憶が正しければ、MinGW ではバックトレースは 機能しません。申し訳ありません。 問題がある場合やバックトレースが unknown だらけの場合は、 Linux、Mac、または Windows 上の MSVC を使用する方法を探した方がよいかもしれません。

デフォルト設定(debugtrue に設定されていない場合)では、行番号が 有効になっていないため、バックトレースは次のようになります。

stack backtrace:
   0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace
   1: std::sys_common::backtrace::_print
   2: std::panicking::default_hook::{{closure}}
   3: std::panicking::default_hook
   4: std::panicking::rust_panic_with_hook
   5: std::panicking::begin_panic
   (~~~~ LINES REMOVED BY ME FOR BREVITY ~~~~)
  32: rustc_typeck::check_crate
  33: <std::thread::local::LocalKey<T>>::with
  34: <std::thread::local::LocalKey<T>>::with
  35: rustc::ty::context::TyCtxt::create_and_enter
  36: rustc_driver::driver::compile_input
  37: rustc_driver::run_compiler

debug = true を設定すると、スタックトレースに行番号が表示されます。 その場合、バックトレースは次のようになります。

stack backtrace:
   (~~~~ LINES REMOVED BY ME FOR BREVITY ~~~~)
             at /home/user/rust/compiler/rustc_typeck/src/check/cast.rs:110
   7: rustc_typeck::check::cast::CastCheck::check
             at /home/user/rust/compiler/rustc_typeck/src/check/cast.rs:572
             at /home/user/rust/compiler/rustc_typeck/src/check/cast.rs:460
             at /home/user/rust/compiler/rustc_typeck/src/check/cast.rs:370
   (~~~~ LINES REMOVED BY ME FOR BREVITY ~~~~)
  33: rustc_driver::driver::compile_input
             at /home/user/rust/compiler/rustc_driver/src/driver.rs:1010
             at /home/user/rust/compiler/rustc_driver/src/driver.rs:212
  34: rustc_driver::run_compiler
             at /home/user/rust/compiler/rustc_driver/src/lib.rs:253

-Z フラグ

コンパイラには多数の -Z * フラグがあります。 これらは nightly でのみ有効になる不安定なフラグです。 その多くはデバッグに役立ちます。 -Z フラグの完全な一覧を取得するには、-Z help を使用してください。

便利なフラグの 1 つに -Z verbose-internals があります。これは一般に、デバッグに役立つ可能性がある 情報をより多く出力できるようにします。

すぐ下に、選択したいくつかのフラグについて詳しい解説があります。

エラーのバックトレースの取得

コンパイラがエラーメッセージを出力する箇所までのバックトレースを取得したい場合は、 -Z treat-err-as-bug=n を渡すことができます。これにより、 コンパイラは n 番目のエラーで panic します。 =n を省略した場合、コンパイラは n として 1 を想定するため、最初に遭遇したエラーで panic します。

例:

cat error.rs
fn main() {
    1 + ();
}
$ rustc +stage1 error.rs
error[E0277]: cannot add `()` to `{integer}`
 --> error.rs:2:7
  |
2 |       1 + ();
  |         ^ no implementation for `{integer} + ()`
  |
  = help: the trait `Add<()>` is not implemented for `{integer}`

error: aborting due to previous error

では、上記のエラーはどこから発生しているのでしょうか?

$ RUST_BACKTRACE=1 rustc +stage1 error.rs -Z treat-err-as-bug
error[E0277]: the trait bound `{integer}: std::ops::Add<()>` is not satisfied
 --> error.rs:2:7
  |
2 |     1 + ();
  |       ^ no implementation for `{integer} + ()`
  |
  = help: the trait `std::ops::Add<()>` is not implemented for `{integer}`

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/HEAD/CONTRIBUTING.md#bug-reports

note: rustc 1.24.0-dev running on x86_64-unknown-linux-gnu

note: run with `RUST_BACKTRACE=1` for a backtrace

thread 'rustc' panicked at 'encountered error with `-Z treat_err_as_bug',
/home/user/rust/compiler/rustc_errors/src/lib.rs:411:12
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose
backtrace.
stack backtrace:
  (~~~ IRRELEVANT PART OF BACKTRACE REMOVED BY ME ~~~)
   7: rustc::traits::error_reporting::<impl rustc::infer::InferCtxt<'a, 'tcx>>
             ::report_selection_error
             at /home/user/rust/compiler/rustc_middle/src/traits/error_reporting.rs:823
   8: rustc::traits::error_reporting::<impl rustc::infer::InferCtxt<'a, 'tcx>>
             ::report_fulfillment_errors
             at /home/user/rust/compiler/rustc_middle/src/traits/error_reporting.rs:160
             at /home/user/rust/compiler/rustc_middle/src/traits/error_reporting.rs:112
   9: rustc_typeck::check::FnCtxt::select_obligations_where_possible
             at /home/user/rust/compiler/rustc_typeck/src/check/mod.rs:2192
  (~~~ IRRELEVANT PART OF BACKTRACE REMOVED BY ME ~~~)
  36: rustc_driver::run_compiler
             at /home/user/rust/compiler/rustc_driver/src/lib.rs:253

よし、これでエラーのバックトレースを取得できました!

delayed bug のデバッグ

-Z eagerly-emit-delayed-bugs オプションを使うと、delayed bug を簡単にデバッグできます。 これは delayed bug を通常のエラーに変換します。つまり、可視化します。これは -Z treat-err-as-bug と組み合わせて使用することで、特定の delayed bug で停止し、バックトレースを取得できます。

エラー作成場所を取得する

-Z track-diagnostics は、エラーがどこで出力されたかを特定するのに役立ちます。 これはそのために #[track_caller] を使用し、エラーとともにその場所を出力します。

$ RUST_BACKTRACE=1 rustc +stage1 error.rs -Z track-diagnostics
error[E0277]: cannot add `()` to `{integer}`
 --> src\error.rs:2:7
  |
2 |     1 + ();
  |       ^ no implementation for `{integer} + ()`
-Ztrack-diagnostics: created at compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs:638:39
  |
  = help: the trait `Add<()>` is not implemented for `{integer}`
  = help: the following other types implement trait `Add<Rhs>`:
            <&'a f32 as Add<f32>>
            <&'a f64 as Add<f64>>
            <&'a i128 as Add<i128>>
            <&'a i16 as Add<i16>>
            <&'a i32 as Add<i32>>
            <&'a i64 as Add<i64>>
            <&'a i8 as Add<i8>>
            <&'a isize as Add<isize>>
          and 48 others

For more information about this error, try `rustc --explain E0277`.

これは -Z treat-err-as-bug と似ていますが、異なります。

  • 出力されたすべてのエラーについて場所を出力します
  • デバッグシンボル付きでビルドされたコンパイラを必要としません
  • 大きなスタックトレースを読み解く必要がありません。

ロギング出力を取得する

コンパイラはロギングに tracing クレートを使用します。

詳細については、tracing に関するガイドのセクションを参照してください

リグレッションの絞り込み(二分探索)

cargo-bisect-rustc ツールは、rustc の挙動の変化を引き起こした PR を正確に見つけるための、手早く簡単な方法として使用できます。 これは rustc の PR アーティファクトを自動的にダウンロードし、リグレッションが見つかるまで、あなたが提供したプロジェクトに対してそれらをテストします。 その後、その PR を確認して、なぜ 変更されたのかについてより多くの文脈を得ることができます。 使用方法については、このチュートリアルを参照してください。

Rust の CI からアーティファクトをダウンロードする

kennytm による rustup-toolchain-install-master ツールは、特定の SHA1 に対して Rust の CI によって生成されたアーティファクトをダウンロードするために使用できます。これは基本的には、ある PR が正常にマージされたことに対応します。そして、それらをローカルで使用できるようにセットアップします。 これは @bors try によって生成されたアーティファクトに対しても機能します。 これは、自分でビルドせずに PR の結果として生成されたビルドを調べたい場合に便利です。

#[rustc_*] TEST 属性

コンパイラは、大量の内部(永続的に unstable な)属性を定義しており、その一部はコンパイラ内部の追加情報をダンプすることでデバッグに役立ちます。 これらには rustc_ というプレフィックスが付けられており、内部 feature である rustc_attrs によってゲートされています(例: #![feature(rustc_attrs)] で有効化)。

完全かつ最新の一覧については、builtin_attrs を参照してください。 より具体的には、TEST とマークされているものです。 注目すべきものをいくつか示します。

属性説明
rustc_dump_def_parents特定の定義の DefId 親の連鎖をダンプします。
rustc_dump_def_pathアイテムの def_path_str をダンプします。
rustc_dump_hidden_type_of_opaquesクレート内の各 opaque type の隠された型をダンプします。
rustc_dump_inferred_outlivesアイテムの暗黙の境界をダンプします。より正確には、アイテムの inferred_outlives_of です。
rustc_dump_item_boundsアイテムの item_bounds をダンプします。
rustc_dump_layoutこのセクションを参照してください
rustc_dump_object_lifetime_defaultsアイテムの object lifetime defaults をダンプします。
rustc_dump_predicatesアイテムの predicates_of をダンプします。
rustc_dump_symbol_nameアイテムのマングル済みおよびデマングル済みの symbol_name をダンプします。
rustc_dump_variancesアイテムの variances をダンプします。
rustc_dump_vtableimpl、または dyn 型の型エイリアスの vtable レイアウトをダンプします。
rustc_regionsNLL クロージャのリージョン要件をダンプします。

すぐ下に、選択したいくつかについての詳しい解説があります。

Graphviz 出力(.dot ファイル)の整形

特定の機能をデバッグするためのコンパイラオプションの中には、graphviz グラフを生成するものがあります。たとえば、関数に付与された #[rustc_mir(borrowck_graphviz_postflow="suffix.dot")] 属性は、-Zdump-mir-dataflow と組み合わせることで、さまざまな borrow-checker のデータフローグラフをダンプします。

これらはすべて .dot ファイルを生成します。これらのファイルを表示するには、graphviz をインストールし(例: apt-get install graphviz)、次のコマンドを実行します。

dot -T pdf maybe_init_suffix.dot > maybe_init_suffix.pdf
firefox maybe_init_suffix.pdf # またはお好みの pdf ビューアー

型レイアウトのデバッグ

内部属性 #[rustc_dump_layout(...)] は、それが付与された型の Layout をダンプするために使用できます。 例:

#![allow(unused)]
#![feature(rustc_attrs)]

fn main() {
#[rustc_dump_layout(debug)]
type T<'a> = &'a u32;
}

次の内容が出力されます。

error: layout_of(&u32) = Layout {
           size: Size(8 bytes),
           align: AbiAlign {
               abi: Align(8 bytes),
           },
           backend_repr: Scalar(
               Initialized {
                   value: Pointer(
                       AddressSpace(
                           0,
                       ),
                   ),
                   valid_range: 1..=18446744073709551615,
               },
           ),
           fields: Primitive,
           largest_niche: Some(
               Niche {
                   offset: Size(0 bytes),
                   value: Pointer(
                       AddressSpace(
                           0,
                       ),
                   ),
                   valid_range: 1..=18446744073709551615,
               },
           ),
           uninhabited: false,
           variants: Single {
               index: 0,
           },
           max_repr_align: None,
           unadjusted_abi_align: Align(8 bytes),
           randomization_seed: 281492156579847,
       }
 --> src/lib.rs:4:1
  |
4 | type T<'a> = &'a u32;
  | ^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

rustc をデバッグするための CodeLLDB の設定

VSCode を使用しており、関心のあるコード部分についてデバッグレベル 1 または 2 を要求するように bootstrap.toml を編集している場合は、VSCode の CodeLLDB 拡張機能を使ってデバッグできるはずです。

以下は、stage 1 コンパイラをビルドされたディレクトリから直接実行するために使用している launch.json ファイルのサンプルです(“インストール“されている必要はありません)。

// .vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
      {
        "type": "lldb",
        "request": "launch",
        "name": "Launch",
        "args": [],  // コンパイラに渡す文字列のコマンドライン引数の配列
        "program": "${workspaceFolder}/build/host/stage1/bin/rustc",
        "windows": {  // windows を使用している場合に適用可能
            "program": "${workspaceFolder}/build/host/stage1/bin/rustc.exe"
        },
        "cwd": "${workspaceFolder}",  // プログラム開始時の現在の作業ディレクトリ
        "stopOnEntry": false,
        "sourceLanguages": ["rust"]
      }
    ]
  }