Skip to content

Commit 2681dcb

Browse files
authored
Rollup merge of #132849 - RalfJung:miri-sync, r=RalfJung
Miri subtree update r? `@ghost`
2 parents b95232d + 881f2ec commit 2681dcb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+963
-378
lines changed

Diff for: src/tools/miri/.github/workflows/ci.yml

+11-2
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,20 @@ jobs:
5858
- name: rustdoc
5959
run: RUSTDOCFLAGS="-Dwarnings" ./miri doc --document-private-items
6060

61+
coverage:
62+
name: coverage report
63+
runs-on: ubuntu-latest
64+
steps:
65+
- uses: actions/checkout@v4
66+
- uses: ./.github/workflows/setup
67+
- name: coverage
68+
run: ./miri test --coverage
69+
6170
# Summary job for the merge queue.
6271
# ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
6372
# And they should be added below in `cron-fail-notify` as well.
6473
conclusion:
65-
needs: [build, style]
74+
needs: [build, style, coverage]
6675
# We need to ensure this job does *not* get skipped if its dependencies fail,
6776
# because a skipped job is considered a success by GitHub. So we have to
6877
# overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
@@ -86,7 +95,7 @@ jobs:
8695
contents: write
8796
# ... and create a PR.
8897
pull-requests: write
89-
needs: [build, style]
98+
needs: [build, style, coverage]
9099
if: ${{ github.event_name == 'schedule' && failure() }}
91100
steps:
92101
# Send a Zulip notification

Diff for: src/tools/miri/CONTRIBUTING.md

+19-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,25 @@ for a list of Miri maintainers.
1313

1414
[Rust Zulip]: https://rust-lang.zulipchat.com
1515

16+
### Pull review process
17+
18+
When you get a review, please take care of the requested changes in new commits. Do not amend
19+
existing commits. Generally avoid force-pushing. The only time you should force push is when there
20+
is a conflict with the master branch (in that case you should rebase across master, not merge), and
21+
all the way at the end of the review process when the reviewer tells you that the PR is done and you
22+
should squash the commits. For the latter case, use `git rebase --keep-base ...` to squash without
23+
changing the base commit your PR branches off of. Use your own judgment and the reviewer's guidance
24+
to decide whether the PR should be squashed into a single commit or multiple logically separate
25+
commits. (All this is to work around the fact that Github is quite bad at dealing with force pushes
26+
and does not support `git range-diff`. Maybe one day Github will be good at git and then life can
27+
become easier.)
28+
29+
Most PRs bounce back and forth between the reviewer and the author several times, so it is good to
30+
keep track of who is expected to take the next step. We are using the `S-waiting-for-review` and
31+
`S-waiting-for-author` labels for that. If a reviewer asked you to do some changes and you think
32+
they are all taken care of, post a comment saying `@rustbot ready` to mark a PR as ready for the
33+
next round of review.
34+
1635
### Larger-scale contributions
1736

1837
If you are thinking about making a larger-scale contribution -- in particular anything that needs
@@ -45,14 +64,6 @@ process for such contributions:
4564
This process is largely informal, and its primary goal is to more clearly communicate expectations.
4665
Please get in touch with us if you have any questions!
4766

48-
### Managing the review state
49-
50-
Most PRs bounce back and forth between the reviewer and the author several times, so it is good to
51-
keep track of who is expected to take the next step. We are using the `S-waiting-for-review` and
52-
`S-waiting-for-author` labels for that. If a reviewer asked you to do some changes and you think
53-
they are all taken care of, post a comment saying `@rustbot ready` to mark a PR as ready for the
54-
next round of review.
55-
5667
## Preparing the build environment
5768

5869
Miri heavily relies on internal and unstable rustc interfaces to execute MIR,

Diff for: src/tools/miri/ci/ci.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ case $HOST_TARGET in
154154
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe
155155
TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
156156
TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
157-
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap threadname pthread
157+
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap random sync threadname pthread
158158
TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm
159159
TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std
160160
TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std

Diff for: src/tools/miri/clippy.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
arithmetic-side-effects-allowed = ["rustc_abi::Size"]
1+
arithmetic-side-effects-allowed = ["rustc_abi::Size", "rustc_apfloat::ieee::IeeeFloat"]

Diff for: src/tools/miri/miri-script/Cargo.lock

+39-4
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ dependencies = [
6363
"windows-sys 0.52.0",
6464
]
6565

66+
[[package]]
67+
name = "fastrand"
68+
version = "2.1.1"
69+
source = "registry+https://github.com/rust-lang/crates.io-index"
70+
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
71+
6672
[[package]]
6773
name = "getrandom"
6874
version = "0.2.12"
@@ -100,9 +106,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
100106

101107
[[package]]
102108
name = "libc"
103-
version = "0.2.153"
109+
version = "0.2.159"
104110
source = "registry+https://github.com/rust-lang/crates.io-index"
105-
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
111+
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
106112

107113
[[package]]
108114
name = "libredox"
@@ -138,11 +144,18 @@ dependencies = [
138144
"rustc_version",
139145
"serde_json",
140146
"shell-words",
147+
"tempfile",
141148
"walkdir",
142149
"which",
143150
"xshell",
144151
]
145152

153+
[[package]]
154+
name = "once_cell"
155+
version = "1.20.2"
156+
source = "registry+https://github.com/rust-lang/crates.io-index"
157+
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
158+
146159
[[package]]
147160
name = "option-ext"
148161
version = "0.2.0"
@@ -195,9 +208,9 @@ dependencies = [
195208

196209
[[package]]
197210
name = "rustix"
198-
version = "0.38.34"
211+
version = "0.38.37"
199212
source = "registry+https://github.com/rust-lang/crates.io-index"
200-
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
213+
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
201214
dependencies = [
202215
"bitflags",
203216
"errno",
@@ -276,6 +289,19 @@ dependencies = [
276289
"unicode-ident",
277290
]
278291

292+
[[package]]
293+
name = "tempfile"
294+
version = "3.13.0"
295+
source = "registry+https://github.com/rust-lang/crates.io-index"
296+
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
297+
dependencies = [
298+
"cfg-if",
299+
"fastrand",
300+
"once_cell",
301+
"rustix",
302+
"windows-sys 0.59.0",
303+
]
304+
279305
[[package]]
280306
name = "thiserror"
281307
version = "1.0.57"
@@ -357,6 +383,15 @@ dependencies = [
357383
"windows-targets 0.52.6",
358384
]
359385

386+
[[package]]
387+
name = "windows-sys"
388+
version = "0.59.0"
389+
source = "registry+https://github.com/rust-lang/crates.io-index"
390+
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
391+
dependencies = [
392+
"windows-targets 0.52.6",
393+
]
394+
360395
[[package]]
361396
name = "windows-targets"
362397
version = "0.48.5"

Diff for: src/tools/miri/miri-script/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ rustc_version = "0.4"
2424
dunce = "1.0.4"
2525
directories = "5"
2626
serde_json = "1"
27+
tempfile = "3.13.0"

Diff for: src/tools/miri/miri-script/src/commands.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ impl Command {
172172
Command::Install { flags } => Self::install(flags),
173173
Command::Build { flags } => Self::build(flags),
174174
Command::Check { flags } => Self::check(flags),
175-
Command::Test { bless, flags, target } => Self::test(bless, flags, target),
175+
Command::Test { bless, flags, target, coverage } =>
176+
Self::test(bless, flags, target, coverage),
176177
Command::Run { dep, verbose, many_seeds, target, edition, flags } =>
177178
Self::run(dep, verbose, many_seeds, target, edition, flags),
178179
Command::Doc { flags } => Self::doc(flags),
@@ -458,9 +459,20 @@ impl Command {
458459
Ok(())
459460
}
460461

461-
fn test(bless: bool, mut flags: Vec<String>, target: Option<String>) -> Result<()> {
462+
fn test(
463+
bless: bool,
464+
mut flags: Vec<String>,
465+
target: Option<String>,
466+
coverage: bool,
467+
) -> Result<()> {
462468
let mut e = MiriEnv::new()?;
463469

470+
let coverage = coverage.then_some(crate::coverage::CoverageReport::new()?);
471+
472+
if let Some(report) = &coverage {
473+
report.add_env_vars(&mut e)?;
474+
}
475+
464476
// Prepare a sysroot. (Also builds cargo-miri, which we need.)
465477
e.build_miri_sysroot(/* quiet */ false, target.as_deref())?;
466478

@@ -479,6 +491,11 @@ impl Command {
479491
// Then test, and let caller control flags.
480492
// Only in root project as `cargo-miri` has no tests.
481493
e.test(".", &flags)?;
494+
495+
if let Some(coverage) = &coverage {
496+
coverage.show_coverage_report(&e)?;
497+
}
498+
482499
Ok(())
483500
}
484501

Diff for: src/tools/miri/miri-script/src/coverage.rs

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use std::path::PathBuf;
2+
3+
use anyhow::{Context, Result};
4+
use path_macro::path;
5+
use tempfile::TempDir;
6+
use xshell::cmd;
7+
8+
use crate::util::MiriEnv;
9+
10+
/// CoverageReport can generate code coverage reports for miri.
11+
pub struct CoverageReport {
12+
/// path is a temporary directory where intermediate coverage artifacts will be stored.
13+
/// (The final output will be stored in a permanent location.)
14+
path: TempDir,
15+
}
16+
17+
impl CoverageReport {
18+
/// Creates a new CoverageReport.
19+
///
20+
/// # Errors
21+
///
22+
/// An error will be returned if a temporary directory could not be created.
23+
pub fn new() -> Result<Self> {
24+
Ok(Self { path: TempDir::new()? })
25+
}
26+
27+
/// add_env_vars will add the required environment variables to MiriEnv `e`.
28+
pub fn add_env_vars(&self, e: &mut MiriEnv) -> Result<()> {
29+
let mut rustflags = e.sh.var("RUSTFLAGS")?;
30+
rustflags.push_str(" -C instrument-coverage");
31+
e.sh.set_var("RUSTFLAGS", rustflags);
32+
33+
// Copy-pasting from: https://doc.rust-lang.org/rustc/instrument-coverage.html#instrumentation-based-code-coverage
34+
// The format symbols below have the following meaning:
35+
// - %p - The process ID.
36+
// - %Nm - the instrumented binary’s signature:
37+
// The runtime creates a pool of N raw profiles, used for on-line
38+
// profile merging. The runtime takes care of selecting a raw profile
39+
// from the pool, locking it, and updating it before the program
40+
// exits. N must be between 1 and 9, and defaults to 1 if omitted
41+
// (with simply %m).
42+
//
43+
// Additionally the default for LLVM_PROFILE_FILE is default_%m_%p.profraw.
44+
// So we just use the same template, replacing "default" with "miri".
45+
let file_template = self.path.path().join("miri_%m_%p.profraw");
46+
e.sh.set_var("LLVM_PROFILE_FILE", file_template);
47+
Ok(())
48+
}
49+
50+
/// show_coverage_report will print coverage information using the artifact
51+
/// files in `self.path`.
52+
pub fn show_coverage_report(&self, e: &MiriEnv) -> Result<()> {
53+
let profraw_files = self.profraw_files()?;
54+
55+
let profdata_bin = path!(e.libdir / ".." / "bin" / "llvm-profdata");
56+
57+
let merged_file = path!(e.miri_dir / "target" / "coverage.profdata");
58+
59+
// Merge the profraw files
60+
cmd!(e.sh, "{profdata_bin} merge -sparse {profraw_files...} -o {merged_file}")
61+
.quiet()
62+
.run()?;
63+
64+
// Create the coverage report.
65+
let cov_bin = path!(e.libdir / ".." / "bin" / "llvm-cov");
66+
let miri_bin =
67+
e.build_get_binary(".").context("failed to get filename of miri executable")?;
68+
cmd!(
69+
e.sh,
70+
"{cov_bin} report --instr-profile={merged_file} --object {miri_bin} --sources src/"
71+
)
72+
.run()?;
73+
74+
println!("Profile data saved in {}", merged_file.display());
75+
Ok(())
76+
}
77+
78+
/// profraw_files returns the profraw files in `self.path`.
79+
///
80+
/// # Errors
81+
///
82+
/// An error will be returned if `self.path` can't be read.
83+
fn profraw_files(&self) -> Result<Vec<PathBuf>> {
84+
Ok(std::fs::read_dir(&self.path)?
85+
.filter_map(|r| r.ok())
86+
.filter(|e| e.file_type().is_ok_and(|t| t.is_file()))
87+
.map(|e| e.path())
88+
.filter(|p| p.extension().is_some_and(|e| e == "profraw"))
89+
.collect())
90+
}
91+
}

Diff for: src/tools/miri/miri-script/src/main.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
mod args;
44
mod commands;
5+
mod coverage;
56
mod util;
67

78
use std::ops::Range;
@@ -34,6 +35,8 @@ pub enum Command {
3435
/// The cross-interpretation target.
3536
/// If none then the host is the target.
3637
target: Option<String>,
38+
/// Produce coverage report if set.
39+
coverage: bool,
3740
/// Flags that are passed through to the test harness.
3841
flags: Vec<String>,
3942
},
@@ -158,9 +161,12 @@ fn main() -> Result<()> {
158161
let mut target = None;
159162
let mut bless = false;
160163
let mut flags = Vec::new();
164+
let mut coverage = false;
161165
loop {
162166
if args.get_long_flag("bless")? {
163167
bless = true;
168+
} else if args.get_long_flag("coverage")? {
169+
coverage = true;
164170
} else if let Some(val) = args.get_long_opt("target")? {
165171
target = Some(val);
166172
} else if let Some(flag) = args.get_other() {
@@ -169,7 +175,7 @@ fn main() -> Result<()> {
169175
break;
170176
}
171177
}
172-
Command::Test { bless, flags, target }
178+
Command::Test { bless, flags, target, coverage }
173179
}
174180
Some("run") => {
175181
let mut dep = false;

Diff for: src/tools/miri/miri-script/src/util.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub struct MiriEnv {
4141
pub sysroot: PathBuf,
4242
/// The shell we use.
4343
pub sh: Shell,
44+
/// The library dir in the sysroot.
45+
pub libdir: PathBuf,
4446
}
4547

4648
impl MiriEnv {
@@ -96,7 +98,8 @@ impl MiriEnv {
9698
// so that Windows can find the DLLs.
9799
if cfg!(windows) {
98100
let old_path = sh.var("PATH")?;
99-
let new_path = env::join_paths(iter::once(libdir).chain(env::split_paths(&old_path)))?;
101+
let new_path =
102+
env::join_paths(iter::once(libdir.clone()).chain(env::split_paths(&old_path)))?;
100103
sh.set_var("PATH", new_path);
101104
}
102105

@@ -111,7 +114,7 @@ impl MiriEnv {
111114
std::process::exit(1);
112115
}
113116

114-
Ok(MiriEnv { miri_dir, toolchain, sh, sysroot, cargo_extra_flags })
117+
Ok(MiriEnv { miri_dir, toolchain, sh, sysroot, cargo_extra_flags, libdir })
115118
}
116119

117120
pub fn cargo_cmd(&self, crate_dir: impl AsRef<OsStr>, cmd: &str) -> Cmd<'_> {

Diff for: src/tools/miri/rust-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
814df6e50eaf89b90793e7d9618bb60f1f18377a
1+
668959740f97e7a22ae340742886d330ab63950f

0 commit comments

Comments
 (0)