From 55cd85d6778693ad0865ff020be90340a9d7babc Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 28 Jul 2025 16:43:21 +1000 Subject: [PATCH] coverage: Run-make regression test for `#[derive(arbitrary::Arbitrary)]` --- .../src/external_deps/llvm.rs | 26 ++++++++ tests/run-make/coverage-arbitrary/Cargo.lock | 65 +++++++++++++++++++ tests/run-make/coverage-arbitrary/Cargo.toml | 9 +++ tests/run-make/coverage-arbitrary/rmake.rs | 37 +++++++++++ tests/run-make/coverage-arbitrary/src/main.rs | 30 +++++++++ 5 files changed, 167 insertions(+) create mode 100644 tests/run-make/coverage-arbitrary/Cargo.lock create mode 100644 tests/run-make/coverage-arbitrary/Cargo.toml create mode 100644 tests/run-make/coverage-arbitrary/rmake.rs create mode 100644 tests/run-make/coverage-arbitrary/src/main.rs diff --git a/src/tools/run-make-support/src/external_deps/llvm.rs b/src/tools/run-make-support/src/external_deps/llvm.rs index 9a6e35da3fe20..8b7079f50e3e5 100644 --- a/src/tools/run-make-support/src/external_deps/llvm.rs +++ b/src/tools/run-make-support/src/external_deps/llvm.rs @@ -17,6 +17,13 @@ pub fn llvm_profdata() -> LlvmProfdata { LlvmProfdata::new() } +/// Constructs a new `llvm-cov` invocation. +/// This assumes that `llvm-cov` is available at `$LLVM_BIN_DIR/llvm-cov`. +#[track_caller] +pub fn llvm_cov() -> LlvmCov { + LlvmCov::new() +} + /// Construct a new `llvm-filecheck` invocation. This assumes that `llvm-filecheck` is available /// at `$LLVM_FILECHECK`. #[track_caller] @@ -86,6 +93,13 @@ pub struct LlvmProfdata { cmd: Command, } +/// An `llvm-cov` invocation builder. +#[derive(Debug)] +#[must_use] +pub struct LlvmCov { + cmd: Command, +} + /// A `llvm-filecheck` invocation builder. #[derive(Debug)] #[must_use] @@ -151,6 +165,7 @@ pub struct LlvmObjcopy { crate::macros::impl_common_helpers!(LlvmReadobj); crate::macros::impl_common_helpers!(LlvmProfdata); +crate::macros::impl_common_helpers!(LlvmCov); crate::macros::impl_common_helpers!(LlvmFilecheck); crate::macros::impl_common_helpers!(LlvmObjdump); crate::macros::impl_common_helpers!(LlvmAr); @@ -259,6 +274,17 @@ impl LlvmProfdata { } } +impl LlvmCov { + /// Constructs a new `llvm-cov` invocation. + /// This assumes that `llvm-cov` is available at `$LLVM_BIN_DIR/llvm-cov`. + #[track_caller] + pub fn new() -> Self { + let llvm_cov = llvm_bin_dir().join("llvm-cov"); + let cmd = Command::new(llvm_cov); + Self { cmd } + } +} + impl LlvmFilecheck { /// Construct a new `llvm-filecheck` invocation. This assumes that `llvm-filecheck` is available /// at `$LLVM_FILECHECK`. diff --git a/tests/run-make/coverage-arbitrary/Cargo.lock b/tests/run-make/coverage-arbitrary/Cargo.lock new file mode 100644 index 0000000000000..0518cdd7bd748 --- /dev/null +++ b/tests/run-make/coverage-arbitrary/Cargo.lock @@ -0,0 +1,65 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "coverage-arbitrary" +version = "0.0.0" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/tests/run-make/coverage-arbitrary/Cargo.toml b/tests/run-make/coverage-arbitrary/Cargo.toml new file mode 100644 index 0000000000000..6c4060c87fa1b --- /dev/null +++ b/tests/run-make/coverage-arbitrary/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +resolver = "2" + +[package] +name = "coverage-arbitrary-crate" +edition = "2024" + +[dependencies] +arbitrary = { version = "1.4.1", features = ["derive"] } diff --git a/tests/run-make/coverage-arbitrary/rmake.rs b/tests/run-make/coverage-arbitrary/rmake.rs new file mode 100644 index 0000000000000..c0c1cb95d0545 --- /dev/null +++ b/tests/run-make/coverage-arbitrary/rmake.rs @@ -0,0 +1,37 @@ +//! Regression test for edge-case bugs in coverage instrumentation that have +//! historically been triggered by derived `arbitrary::Arbitrary` impls. +//! +//! See +//! for an example of one such bug. + +//@ needs-profiler-runtime + +use run_make_support::{cargo, is_windows, llvm}; + +fn main() { + let profraw_path = "default.profraw"; + let profdata_path = "default.profdata"; + + // Build and run the crate with coverage instrumentation, + // producing a `.profraw` file. + let run_out = cargo() + .args(&["run", "--manifest-path=Cargo.toml", "--release"]) + .env("RUSTFLAGS", "-Cinstrument-coverage") + .env("LLVM_PROFILE_FILE", profraw_path) + .run(); + + // The program prints its own executable path (i.e. args[0]) to stdout. + let exe_path = run_out.stdout_utf8().lines().next().unwrap().to_owned(); + + // Convert `.profraw` output to `.profdata`, as needed by `llvm-cov`. + llvm::llvm_profdata() + .args(&["merge", "--sparse", "--output", profdata_path, profraw_path]) + .run(); + + // The contents of the coverage report are not very important; + // what matters is that `llvm-cov` should not encounter an error + // (e.g. "malformed instrumentation profile data: function name is empty"). + llvm::llvm_cov() + .args(&["show", "-format=text", "-instr-profile", profdata_path, "-object", &exe_path]) + .run(); +} diff --git a/tests/run-make/coverage-arbitrary/src/main.rs b/tests/run-make/coverage-arbitrary/src/main.rs new file mode 100644 index 0000000000000..3861a3648639c --- /dev/null +++ b/tests/run-make/coverage-arbitrary/src/main.rs @@ -0,0 +1,30 @@ +use core::hint::black_box; +use std::env::args; + +use arbitrary::{Arbitrary, Unstructured}; + +#[derive(Debug, Arbitrary)] +struct MyStruct { + _x: u32, +} + +#[derive(Debug, Arbitrary)] +enum MyEnum { + One, + Two, + Three, +} + +fn main() { + // Print the executable path to stdout, so that the rmake script has easy + // access to it. This is easier than trying to interrogate cargo. + println!("{}", args().nth(0).unwrap()); + + dbg!(MyStruct::size_hint(0)); + dbg!(MyEnum::size_hint(0)); + + let mut data = Unstructured::new(black_box(&[0; 1024])); + + dbg!(MyStruct::arbitrary(&mut data)); + dbg!(MyEnum::arbitrary(&mut data)); +}