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).
<details><summary>diff</summary>
diff --git a/contrib/devtools/deterministic-fuzz-coverage/src/main.rs b/contrib/devtools/deterministic-fuzz-coverage/src/main.rs
index 423393181c..171afbbed7 100644
--- a/contrib/devtools/deterministic-fuzz-coverage/src/main.rs
+++ b/contrib/devtools/deterministic-fuzz-coverage/src/main.rs
@@ -2,11 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/license/mit/.
+use std::collections::VecDeque;
use std::env;
use std::fs::{read_dir, File};
use std::path::{Path, PathBuf};
use std::process::{Command, ExitCode};
use std::str;
+use std::thread;
/// A type for a complete and readable error message.
type AppError = String;
@@ -106,8 +108,6 @@ fn deterministic_coverage(
fuzz_target: &str,
) -> AppResult {
let using_libfuzzer = using_libfuzzer(fuzz_exe)?;
- 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| {
@@ -121,7 +121,13 @@ fn deterministic_coverage(
.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"));
+ let file_name = match entry.file_name() {
+ Some(f) => Ok(f.to_string_lossy()),
+ None => Err("noooo".to_string()),
+ }?;
+ let cov_txt_path = build_dir.join(format!("fuzz_det_cov.show.{fuzz_target}.{file_name}.{run_id}.txt"));
+ let profraw_file = build_dir.join(format!("fuzz_det_cov.{fuzz_target}.{file_name}.profraw"));
+ let profdata_file = build_dir.join(format!("fuzz_det_cov.{fuzz_target}.{file_name}.profdata"));
if !{
{
let mut cmd = Command::new(fuzz_exe);
@@ -197,20 +203,44 @@ The coverage was not deterministic between runs.
//
// Also, This can catch issues where several fuzz inputs are non-deterministic, but the sum of
// their overall coverage trace remains the same across runs and thus remains undetected.
- println!("Check each fuzz input individually ...");
- for entry in entries {
- let entry = entry.path();
- if !entry.is_file() {
- Err(format!("{} should be a file", entry.display()))?;
+ println!("Check each fuzz input individually, {} inputs...", entries.len());
+ thread::scope(|s| -> AppResult {
+ use std::thread::ScopedJoinHandle;
+ let mut handles = VecDeque::<ScopedJoinHandle<_>>::new();
+ for entry in entries {
+ let entry = entry.path();
+ if !entry.is_file() {
+ return Err(format!("{} should be a file", entry.display()));
+ }
+ let entry = entry.to_path_buf();
+ handles.push_back(s.spawn(move || -> AppResult {
+ let cov_txt_base = run_single(0, &entry)?;
+ let cov_txt_repeat = run_single(1, &entry)?;
+ check_diff(
+ &cov_txt_base,
+ &cov_txt_repeat,
+ &format!("The fuzz target input was {}.", entry.display()),
+ )
+ }));
+ if handles.len() >= std::thread::available_parallelism().map_err(|e| format!("Failed checking available_parallelism: {e}"))?.into() {
+ match handles.remove(0) {
+ Some(front) => match front.join() {
+ Err(e) => Err(format!("err: {:?}", e)),
+ Ok(result) => result,
+ },
+ None => Ok({})
+ }?
+ }
}
- let cov_txt_base = run_single(0, &entry)?;
- let cov_txt_repeat = run_single(1, &entry)?;
- check_diff(
- &cov_txt_base,
- &cov_txt_repeat,
- &format!("The fuzz target input was {}.", entry.display()),
- )?;
- }
+ for h in handles {
+ match h.join() {
+ Err(e) => Err(format!("err: {:?}", e)),
+ Ok(result) => result,
+ }?
+ }
+
+ Ok(())
+ })?;
// Finally, check that running over all fuzz inputs in one process is deterministic as well.
// This can catch issues where mutable global state is leaked from one fuzz input execution to
// the next.