Repository: bitcoin/bitcoin
File: contrib/devtools/deterministic-fuzz-coverage/src/main.rs (Rust)

Code before:
    let profraw_file = build_dir.join("fuzz_det_cov.profraw");
    let profdata_file = build_dir.join("fuzz_det_cov.profdata");
    let corpus_dir = corpora_dir.join(fuzz_target);
    let mut entries = read_dir(&corpus_dir)
        .map_err(|err| {
            exit_help(&format!(
                "The fuzz target's input directory must exist! ({}; {})",
                corpus_dir.display(),
                err
            ))
        })?
        .map(|entry| entry.expect("IO error"))
        .collect::<Vec<_>>();
    entries.sort_by_key(|entry| entry.file_name());
    let run_single = |run_id: u8, entry: &Path| -> Result<PathBuf, AppError> {
        let cov_txt_path = build_dir.join(format!("fuzz_det_cov.show.{run_id}.txt"));
        if !{
            {
                let mut cmd = Command::new(fuzz_exe);
                if using_libfuzzer {
                    cmd.arg("-runs=1");
                }
                cmd
            }
            .env("LLVM_PROFILE_FILE", &profraw_file)
            .env("FUZZ", fuzz_target)
            .arg(entry)
            .status()
            .map_err(|e| format!("fuzz failed with {e}"))?
            .success()
        } {
            Err("fuzz failed".to_string())?;
        }
        if !Command::new(LLVM_PROFDATA)
            .arg("merge")
            .arg("--sparse")
            .arg(&profraw_file)
            .arg("-o")
            .arg(&profdata_file)
            .status()
            .map_err(|e| format!("{LLVM_PROFDATA} merge failed with {e}"))?
            .success()
        {
            Err(format!("{LLVM_PROFDATA} merge failed. This can be a sign of compiling without code coverage support."))?;
        }
        let cov_file = File::create(&cov_txt_path)
            .map_err(|e| format!("Failed to create coverage txt file ({e})"))?;
        if !Command::new(LLVM_COV)
            .args([
                "show",
                "--show-line-counts-or-regions",

Review comment (bug):
nit:
Current failure output:
```
diff --git a/home/hodlinator/bitcoin/build_fuzz/fuzz_det_cov.show.t5.r0.txt b/home/hodlinator/bitcoin/build_fuzz/fuzz_det_cov.show.t5.r1.txt
index da0315b1e4..93623d5c15 100644
--- a/home/hodlinator/bitcoin/build_fuzz/fuzz_det_cov.show.t5.r0.txt
+++ b/home/hodlinator/bitcoin/build_fuzz/fuzz_det_cov.show.t5.r1.txt
...
```
If you re-touch, it might be nice to use a/b instead of r0/r1:
```
diff --git a/home/hodlinator/bitcoin/build_fuzz/fuzz_det_cov.show.t5.a.txt b/home/hodlinator/bitcoin/build_fuzz/fuzz_det_cov.show.t5.b.txt
index da0315b1e4..93623d5c15 100644
--- a/home/hodlinator/bitcoin/build_fuzz/fuzz_det_cov.show.t5.a.txt
+++ b/home/hodlinator/bitcoin/build_fuzz/fuzz_det_cov.show.t5.b.txt
```

```suggestion
    let run_single = |run_id: char, entry: &Path, thread_id: usize| -> Result<PathBuf, AppError> {
        let cov_txt_path = build_dir.join(format!("fuzz_det_cov.show.t{thread_id}.{run_id}.txt"));
```

Code after:
        .map_err(|e| format!("The libFuzzer -help=1 output must be valid text ({e})"))?;
    Ok(help_output.contains("libFuzzer"))
}

fn deterministic_coverage(
    build_dir: &Path,
    corpora_dir: &Path,
    fuzz_exe: &Path,
    fuzz_target: &str,
    par: usize,
) -> AppResult {
    let using_libfuzzer = using_libfuzzer(fuzz_exe)?;
    if using_libfuzzer {
        println!("Warning: The fuzz executable was compiled with libFuzzer as sanitizer.");
        println!("This tool may be tripped by libFuzzer misbehavior.");
        println!("It is recommended to compile without libFuzzer.");
    }
    let corpus_dir = corpora_dir.join(fuzz_target);
    let mut entries = read_dir(&corpus_dir)
        .map_err(|err| {
            exit_help(&format!(
                "The fuzz target's input directory must exist! ({}; {})",
                corpus_dir.display(),
                err
            ))
        })?
        .map(|entry| entry.expect("IO error"))
        .collect::<Vec<_>>();
    entries.sort_by_key(|entry| entry.file_name());
    let run_single = |run_id: char, entry: &Path, thread_id: usize| -> Result<PathBuf, AppError> {
        let cov_txt_path = build_dir.join(format!("fuzz_det_cov.show.t{thread_id}.{run_id}.txt"));
        let profraw_file = build_dir.join(format!("fuzz_det_cov.t{thread_id}.{run_id}.profraw"));
        let profdata_file = build_dir.join(format!("fuzz_det_cov.t{thread_id}.{run_id}.profdata"));
        {
            let output = {
                let mut cmd = Command::new(fuzz_exe);
                if using_libfuzzer {
                    cmd.arg("-runs=1");
                }
                cmd
            }
            .env("LLVM_PROFILE_FILE", &profraw_file)
            .env("FUZZ", fuzz_target)
            .arg(entry)
            .output()
            .map_err(|e| format!("fuzz failed: {e}"))?;
            if !output.status.success() {
                Err(format!(
                    "fuzz failed!\nstdout:\n{}\nstderr:\n{}\n",
                    String::from_utf8_lossy(&output.stdout),
                    String::from_utf8_lossy(&output.stderr)