LLVM のソースベースのコードカバレッジ
rustc は、コンパイル時に Rust ライブラリとバイナリへ追加の命令とデータをインストルメントするコマンドラインオプション(-C instrument-coverage)により、詳細なソースベースのコードおよびテストカバレッジ解析をサポートします。
カバレッジインストルメンテーションは、コード分岐(MIR ベースの制御フロー解析に基づく)に LLVM 組み込み命令 llvm.instrprof.increment の呼び出しを注入し、LLVM はこれらを、実行時に静的カウンターをインクリメントする命令へ変換します。
LLVM のカバレッジインストルメンテーションには、ソースメタデータをエンコードする Coverage Map も必要です。これは、カウンター ID をファイル内の位置(開始および終了の行と列)へ、直接および間接的にマッピングします。
カバレッジインストルメンテーションの有無にかかわらず、Rust ライブラリはインストルメントされたバイナリへリンクできます。
プログラムが実行され、正常に終了すると、LLVM ライブラリは最終的なカウンター値をファイル(default.profraw、または環境変数 LLVM_PROFILE_FILE で設定されたカスタムファイル)へ書き込みます。
開発者は既存の LLVM カバレッジ解析ツールを使用して .profraw ファイルをデコードし、それに対応する Coverage Map(それらを生成した一致するバイナリから得られるもの)とともに、解析用のさまざまなレポートを生成します。例:

詳細な手順と例は、rustc book に記載されています。
推奨される bootstrap.toml 設定
カバレッジインストルメンテーションコードに取り組む場合、通常は [build] で profiler = true を設定して、プロファイラーランタイムを有効にする必要があります。
これにより、コンパイラがインストルメントされたバイナリを生成できるようになり、カバレッジテストスイート全体を実行できるようになります。
コンパイラと LLVM でデバッグアサーションを有効にすることが推奨されますが、必須ではありません。
# "compiler" プロファイルに似ていますが、LLVM のデバッグアサーションも有効にします。
# これらのアサーションは、一部のケースで不正な形式のカバレッジマッピングを検出できます。
profile = "codegen"
# 重要: これは、LLVM プロファイラーランタイムをビルドするようビルドシステムに指示します。
# これがないと、コンパイラはカバレッジインストルメントされたバイナリを生成できず、
# 多くのカバレッジテストがスキップされます。
build.profiler = true
# コンパイラのデバッグアサーションを有効にします。
rust.debug-assertions = true
Rust シンボルマングリング
-C instrument-coverage は、一貫性があり可逆的な名前マングリングを保証するために、(ユーザーが rustc を起動するときに -C symbol-mangling-version=v0 オプションを指定した場合と同様に)Rust シンボルマングリング v0 を自動的に有効にします。
これには 2 つの重要な利点があります。
- LLVM カバレッジツールは、ソースコードへの一部の変更を含め、複数回の実行にわたってカバレッジを解析できます。そのため、マングルされた名前はコンパイル間で一貫していなければなりません。
- LLVM カバレッジレポートは関数単位でカバレッジを報告でき、複数の型置換バリエーションで呼び出された場合は、ジェネリック関数の一意なインスタンス化ごとのカバレッジカウントを分けて出力することさえできます。
LLVM プロファイラーランタイム
カバレッジデータは、実行可能な Rust プログラムを実行することによってのみ生成されます。
rustc は、カバレッジインストルメントされたバイナリを、カウンター値を .profraw ファイルへ書き込むためのプログラムフック(exit フックなど)を実装する LLVM ランタイムコード(compiler-rt)と静的にリンクします。
rustc ソースツリーでは、library/profiler_builtins が LLVM の compiler-rt コードを Rust ライブラリクレートにバンドルしています。
rustc をビルドする際、profiler_builtins は bootstrap.toml で build.profiler = true が設定されている場合にのみ含まれることに注意してください。
-C instrument-coverage でコンパイルする場合、CStore::postprocess() は inject_profiler_runtime() を呼び出すことで profiler_builtins を動的に読み込みます。
カバレッジインストルメンテーションのテスト
(tests/coverage テストスイートに関する compiletest ドキュメントも参照してください。)
MIR におけるカバレッジインストルメンテーションは、mir-opt テストによって検証されます:
tests/mir-opt/coverage/instrument_coverage.rs。
LLVM IR におけるカバレッジインストルメンテーションは、coverage-map モードの tests/coverage テストスイートによって検証されます。
これらのテストは、テストプログラムを LLVM IR アセンブリへコンパイルし、その後 src/tools/coverage-dump ツールを使用して、最終的なバイナリに埋め込まれるカバレッジマッピングを抽出し、整形して出力します。
カバレッジインストルメンテーションとカバレッジレポートのエンドツーエンドテストは、coverage-run モードの tests/coverage テストスイート、および tests/coverage-run-rustdoc テストスイートによって実行されます。
これらのテストは、カバレッジインストルメンテーション付きでテストプログラムをコンパイルして実行し、その後 LLVM ツールを使用してカバレッジデータを人間が読めるカバレッジレポートへ変換します。
coverage-runモードのテストには暗黙の//@ needs-profiler-runtimeディレクティブがあるため、プロファイラーランタイムがbootstrap.tomlで有効化されていない場合はスキップされます。
最後に、tests/codegen-llvm/instrument-coverage/testprog.rs テストは、-C instrument-coverage を使用して単純な Rust プログラムをコンパイルし、コンパイルされたプログラムの LLVM IR を、カバレッジ対応プログラムに期待される LLVM IR 命令および構造化データと比較します。これには、Coverage Map 関連のメタデータおよびランタイムカウンターをインクリメントする LLVM 組み込み呼び出しに対する各種チェックが含まれます。
coverage、coverage-run-rustdoc、および mir-opt テストの期待結果は、次を実行することで更新できます。
./x test coverage --bless
./x test coverage-run-rustdoc --bless
./x test tests/mir-opt --bless