计算 rustc 的代码覆盖率
最近,笔者需要统计 rustc 在编译特定程序时的代码覆盖率。由于这一过程涉及构建带有插桩(instrumentation)的自定义 rustc,步骤较为繁琐,容易出错。为方便日后回顾,也为了给有类似需求的读者提供参考,特将完整流程整理成文。
构建插桩 rustc
首先安装系统依赖:
sudo apt install ninja-build pkg-config libssl-dev libzstd-dev llvm lld clang cmake
然后安装覆盖率插件:
cargo install grcov
接着,下载最新版本的 rustc 源代码并解压,笔者这里下载的是 1.87.0 版本:
wget https://static.rust-lang.org/dist/rustc-1.87.0-src.tar.xz
tar -xf rustc-1.87.0-src.tar.xz
进入源代码目录,运行 ./configure
指令生成默认构建配置 bootstrap.toml
:
cd rustc-1.87.0-src
./configure
打开 bootstrap.toml
文件,开启 profiler 选项,其他内容保持默认:
[build]
profiler = true
[target.x86_64-unknown-linux-gnu]
profiler = true
然后需要修改 rustc 的 builder 源码,添加插桩命令。具体地,在 src/bootstrap/src/core/builder/cargo.rs
文件的 580 行左右(可以全局搜索 rust_new_symbol_mangling
关键字,就在它附近)添加以下插桩的命令:
if mode != Mode::Std {
rustflags.arg("-Cinstrument-coverage");
}
需要注意的是,对于标准库的编译不能插桩,否则会报错提示缺少 profiler 库。
最后,运行以下命令开始编译:
./x build && ./x install
根据机器配置的不同,编译过程大约耗时 1 小时,最终构建出的 rustc 安装在 /usr/local/bin/rustc
目录下。
计算代码覆盖率
编译程序
使用刚才构建的插桩 rustc 编译给定程序:
LLVM_PROFILE_FILE="coverage/%p-%m.profraw" /usr/local/bin/rustc t1.rs
LLVM_PROFILE_FILE="coverage/%p-%m.profraw" /usr/local/bin/rustc t2.rs
对于多个 rust 文件,使用以上命令依次编译即可,所有的结果都会存在 coverage 文件夹中。
如果是 Cargo 项目,使用我们构建的 cargo 二进制文件即可,它会自动调用插桩的 rustc:
/usr/local/bin/cargo build
查看覆盖率
编译完成后可以通过以下命令查看 rustc 的代码覆盖率:
grcov coverage/*.profraw \
-b /root/Desktop/rustc-1.87.0-src/build/x86_64-unknown-linux-gnu/stage1 \
-s /root/Desktop/rustc-1.87.0-src/compiler \
-t html \
--branch \
--ignore-not-existing \
--llvm-path /root/rustc-1.87.0-src/build/x86_64-unknown-linux-gnu/llvm/bin \
-o coverage/
对于多个 .profraw 文件,也可以使用以下命令合并后再导出覆盖率报告:
/root/rustc-1.87.0-src/build/x86_64-unknown-linux-gnu/llvm/bin/llvm-profdata merge -sparse *.profraw -o merged.profdata
[!NOTE]
- 上述指令中
/root/Desktop/rustc-1.87.0-src
是构建插桩 rustc 的源码路径,需要根据实际情况修改- 开启 profiler 之后 rustc 的运行效率将受到较大影响,因此不建议在计算覆盖率的同时观测和性能有关的数据
- .profraw 文件体积较大,大规模的任务极易耗尽存储空间,建议另起一个脚本监控 coverage 目录,滚动合并 rustc 生成的 .profraw 文件
参考资料
计算 rustc 的代码覆盖率
https://blog.jjl9807.com/archives/40/