Unrelated, but while looking for faults with this PR and trying to trigger errors, I got distracted into making this run in parallel. Earlier version triggered OOM-killer and this one is still in a super-unbaked state. (Output should be buffered and sequenced, parallization logic should be refined, file naming uniqueness-approach is bad, etc).
0diff --git a/contrib/devtools/deterministic-fuzz-coverage/src/main.rs b/contrib/devtools/deterministic-fuzz-coverage/src/main.rs
1index 423393181c..171afbbed7 100644
2--- a/contrib/devtools/deterministic-fuzz-coverage/src/main.rs
3+++ b/contrib/devtools/deterministic-fuzz-coverage/src/main.rs
4@@ -2,11 +2,13 @@
5 // Distributed under the MIT software license, see the accompanying
6 // file COPYING or https://opensource.org/license/mit/.
7
8+use std::collections::VecDeque;
9 use std::env;
10 use std::fs::{read_dir, File};
11 use std::path::{Path, PathBuf};
12 use std::process::{Command, ExitCode};
13 use std::str;
14+use std::thread;
15
16 /// A type for a complete and readable error message.
17 type AppError = String;
18@@ -106,8 +108,6 @@ fn deterministic_coverage(
19 fuzz_target: &str,
20 ) -> AppResult {
21 let using_libfuzzer = using_libfuzzer(fuzz_exe)?;
22- let profraw_file = build_dir.join("fuzz_det_cov.profraw");
23- let profdata_file = build_dir.join("fuzz_det_cov.profdata");
24 let corpus_dir = corpora_dir.join(fuzz_target);
25 let mut entries = read_dir(&corpus_dir)
26 .map_err(|err| {
27@@ -121,7 +121,13 @@ fn deterministic_coverage(
28 .collect::<Vec<_>>();
29 entries.sort_by_key(|entry| entry.file_name());
30 let run_single = |run_id: u8, entry: &Path| -> Result<PathBuf, AppError> {
31- let cov_txt_path = build_dir.join(format!("fuzz_det_cov.show.{run_id}.txt"));
32+ let file_name = match entry.file_name() {
33+ Some(f) => Ok(f.to_string_lossy()),
34+ None => Err("noooo".to_string()),
35+ }?;
36+ let cov_txt_path = build_dir.join(format!("fuzz_det_cov.show.{fuzz_target}.{file_name}.{run_id}.txt"));
37+ let profraw_file = build_dir.join(format!("fuzz_det_cov.{fuzz_target}.{file_name}.profraw"));
38+ let profdata_file = build_dir.join(format!("fuzz_det_cov.{fuzz_target}.{file_name}.profdata"));
39 if !{
40 {
41 let mut cmd = Command::new(fuzz_exe);
42@@ -197,20 +203,44 @@ The coverage was not deterministic between runs.
43 //
44 // Also, This can catch issues where several fuzz inputs are non-deterministic, but the sum of
45 // their overall coverage trace remains the same across runs and thus remains undetected.
46- println!("Check each fuzz input individually ...");
47- for entry in entries {
48- let entry = entry.path();
49- if !entry.is_file() {
50- Err(format!("{} should be a file", entry.display()))?;
51+ println!("Check each fuzz input individually, {} inputs...", entries.len());
52+ thread::scope(|s| -> AppResult {
53+ use std::thread::ScopedJoinHandle;
54+ let mut handles = VecDeque::<ScopedJoinHandle<_>>::new();
55+ for entry in entries {
56+ let entry = entry.path();
57+ if !entry.is_file() {
58+ return Err(format!("{} should be a file", entry.display()));
59+ }
60+ let entry = entry.to_path_buf();
61+ handles.push_back(s.spawn(move || -> AppResult {
62+ let cov_txt_base = run_single(0, &entry)?;
63+ let cov_txt_repeat = run_single(1, &entry)?;
64+ check_diff(
65+ &cov_txt_base,
66+ &cov_txt_repeat,
67+ &format!("The fuzz target input was {}.", entry.display()),
68+ )
69+ }));
70+ if handles.len() >= std::thread::available_parallelism().map_err(|e| format!("Failed checking available_parallelism: {e}"))?.into() {
71+ match handles.remove(0) {
72+ Some(front) => match front.join() {
73+ Err(e) => Err(format!("err: {:?}", e)),
74+ Ok(result) => result,
75+ },
76+ None => Ok({})
77+ }?
78+ }
79 }
80- let cov_txt_base = run_single(0, &entry)?;
81- let cov_txt_repeat = run_single(1, &entry)?;
82- check_diff(
83- &cov_txt_base,
84- &cov_txt_repeat,
85- &format!("The fuzz target input was {}.", entry.display()),
86- )?;
87- }
88+ for h in handles {
89+ match h.join() {
90+ Err(e) => Err(format!("err: {:?}", e)),
91+ Ok(result) => result,
92+ }?
93+ }
94+
95+ Ok(())
96+ })?;
97 // Finally, check that running over all fuzz inputs in one process is deterministic as well.
98 // This can catch issues where mutable global state is leaked from one fuzz input execution to
99 // the next.