rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.76k stars 12.76k forks source link

Make cargo `-Zbuild-std` work with `-Cinstrument-coverage` #133300

Open Enselic opened 23 hours ago

Enselic commented 23 hours ago

By not trying to inject a profiler runtime when only building an rlib. This logic already exists for the panic runtime.

This makes

RUSTFLAGS="-Cinstrument-coverage" cargo build -Zbuild-std=std,profiler_builtins

work. Note that you probably also need RUST_COMPILER_RT_FOR_PROFILER=$src/llvm-project/compiler-rt in your environment.

Fixes #79401

TODO

Demonstration

Before this fix you get these errors:

$ rm -rf target ; RUST_COMPILER_RT_FOR_PROFILER=/home/martin/src/llvm-project/compiler-rt RUSTFLAGS="-Cinstrument-coverage" cargo +nightly build --release -Zbuild-std=std,profiler_builtins
error: `profiler_builtins` crate (required by compiler options) is not compatible with crate attribute `#![no_core]`
error[E0152]: found duplicate lang item `manually_drop`
    = note: first definition in `core` loaded from /home/martin/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-d453bab70303062c.rlib
    = note: second definition in the local crate (`core`)

With the fix the build succeeds:

$ rm -rf target ; RUST_COMPILER_RT_FOR_PROFILER=/home/martin/src/llvm-project/compiler-rt RUSTFLAGS="-Cinstrument-coverage" cargo +stage1 build --release -Zbuild-std=std,profiler_builtins
    Finished `release` profile [optimized] target(s) in 45.57s

And we can check code coverage. My example program looks like this:

fn main() {
    if std::env::args_os().nth(1) == Some("write-file".into()) {
        std::fs::write("hello.txt", "Hello, world!").unwrap();
    } else {
        println!("Hello, world!");
    }
}

when the program prints to stdout:

$ LLVM_PROFILE_FILE=stdout.profraw ./target/release/hello-world
Hello, world!

we can see that fs::write() is not being used (note the 0's):

$ /home/martin/src/rust/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-profdata merge -sparse stdout.profraw -o stdout.profdata
$ /home/martin/src/rust/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-cov show target/release/hello-world --sources /home/martin/src/rust/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/src/rust/library/std/src/fs.rs --instr-profile stdout.profdata --color | grep -A 3 'pub fn write(&mut self, write: b
ool) -> &mut Self {'
 1357|      0|    pub fn write(&mut self, write: bool) -> &mut Self {
 1358|      0|        self.0.write(write);
 1359|      0|        self
 1360|      0|    }

but when we print to a file:

$ LLVM_PROFILE_FILE=file.profraw ./target/release/hello-world write-file

the code coverage shows fs::write() as being used (note the 1's):

$ /home/martin/src/rust/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-profdata merge -sparse file.profraw -o file.profdata
$ /home/martin/src/rust/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-cov show target/release/hello-world --sources /home/martin/src/rust/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/src/rust/library/std/src/fs.rs --instr-profile file.profdata --color | grep -A 3 'pub fn write(&mut self, write: bool) -> &mut Self {'
 1357|      1|    pub fn write(&mut self, write: bool) -> &mut Self {
 1358|      1|        self.0.write(write);
 1359|      1|        self
 1360|      1|    }
rustbot commented 23 hours ago

r? @lcnr

rustbot has assigned @lcnr. They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

lcnr commented 5 hours ago

r? compiler