Rust 1.60.0で安定化されたSource-based Code Coverageを試してみる

本日リリースの Rust 1.60.0 の目玉機能の一つであるSource-based Code Coverage(バイナリ実行時に行レベルでの実行回数を記録する機能)を動かしてみました。 手順を覚えておくと手元で簡単に実行時のカバレッジ計測ができてデバッグに役立ちそうだと感じました。

必要なモジュールのインストール

必要なツール群をインストールします。

# Rust 1.60.0 へのアップデート
rustup update

# カバレッジ表示用のツールをインストール
rustup component add llvm-tools-preview

# デマングラー(demangler)をインストール
cargo install rustfilt

サンプルプログラム

行ごとの実行回数を見やすいようにゴテゴテとしたFizzBuzzを書いてみます。cargo new fizzbuzz から main.rs に実装していきます。

fn fizz(n: usize) -> &'static str {
    if n == 0 {
        unreachable!("I don't expect n == 0.")
    }
    if n % 3 == 0 {
        return "Fizz";
    } else {
        return "";
    }
}
fn buzz(n: usize) -> &'static str {
    if n % 5 == 0 {
        return "Buzz";
    } else {
        return "";
    }
}
fn fizz_buzz(n: usize) -> String {
    let mut tmp = vec![];
    tmp.push(fizz(n));
    tmp.push(buzz(n));
    let ret = tmp.join("");
    if ret.len() > 0 {
        return ret;
    }
    n.to_string()
}
fn main() {
    const N: usize = 20;
    for i in 1..N {
        println!("{}: {}", i, fizz_buzz(i));
    }
}

カバレッジの計測

コンパイル

コンパイル時にフラグを付けます。

RUSTFLAGS="-C instrument-coverage" cargo build

バイナリ実行

作られたバイナリを実行します。

./target/debug/fizzbuzz

今回はFizzBuzzの結果が標準出力に表示されます。 また今いるディレクトリに default.profraw というファイルが作られます。(手順通りに行っていればプロジェクトルートに作られるので ls ./ で確認できるはず。)

カバレッジの表示

default.profraw は各行の実行回数が保存されていますが、人間が見れる形になっていないので、見れる形にする処理を行います。

# ※先頭の `$` から入力が必要です。
$(rustc --print sysroot)/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-profdata merge -sparse default.profraw -o default.profdata
$(rustc --print sysroot)/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-cov show -Xdemangler=rustfilt target/debug/fizzbuzz \
    -instr-profile=default.profdata \
    -show-line-counts-or-regions \
    -show-instantiations

f:id:matsu7874:20220408023656p:plain
llvm-cov showの実行結果

各行の実行回数が正しく表示されており、実行されていない箇所や最も実行されている箇所も分かりやすく表示されています。 ちなみに今回はdebugビルドで行いましたが、releaseビルドでも llvm-cov の引数にとる実行バイナリを変えてやると同じように結果が表示されました。