Why does kcov calculate incorrect code coverage statistics for Rust programs?

2k views Asked by At

I've tried to use kcov to get code coverage for a Rust library. I've followed this tutorial to build and use kcov. The coverage seems to work, however I'm facing a strange high coverage. Some files in the project gets a 100% coverage, even if they are actually not covered at all!

This is a minimal project reproducing the problem:

Cargo.toml

[package]
name = "mypackage"
version = "0.1.0"
authors = ["mbrt"]

src/lib.rs

pub mod subm;

pub fn coverage1(i : bool) -> bool {
    if i {
        true
    }
    else {
        false
    }
}

#[cfg(test)]
mod test {
    use super::coverage1;

    #[test]
    fn test_coverage1() {
        assert!(coverage1(true));
    }
}

src/subm.rs

pub fn coverage2(i : bool) -> bool {
    if i {
        true
    }
    else {
        false
    }
}

#[cfg(test)]
mod test {
    #[test]
    fn test_coverage2() {
    }
}

There are two identical functions, one in the root of the crate, and another in a submodule. The only difference is that the first test stimulates one function, and the other does nothing at all. In this case I'd expect a coverage not greater than 50%.

However kcov reports this:

coverage summary

The coverage for lib.rs is correct:

coverage1

But the coverage for subm.rs is wrong! Note that the function is public, so it cannot be optimized out from the library:

coverage2

Here we can verify that kcov is working, because it is able to compute code coverage for one file, but it is not able to see that the second file is not covered at all.

What is the problem here? Maybe test binaries strip down unused functions and kcov cannot see them?

2

There are 2 answers

7
huon On BEST ANSWER

You're correct: totally unused functions are stripped at the moment, so coverage tools like kcov are only good for branch coverage within used functions (at least, the summary functionality of such tools). There is some discussion about making this not happen by default for test/debug builds.

0
mbrt On

There is a workaround: the RUSTFLAGS='-C link-dead-code' environment variable. Use it while building and the Rust compiler will link dead code as well:

RUSTFLAGS='-C link-dead-code' cargo test