diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index be775dcd146e2..841b685d7ab89 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -26,8 +26,8 @@ pub struct InvariantConfig { pub max_assume_rejects: u32, /// Number of runs to execute and include in the gas report. pub gas_report_samples: u32, - /// Path where invariant corpus is stored. If not configured then coverage guided fuzzing is - /// disabled. + /// Path where invariant corpus is stored, enables coverage guided fuzzing and edge coverage + /// metrics. pub corpus_dir: Option, /// Whether corpus to use gzip file compression and decompression. pub corpus_gzip: bool, @@ -43,6 +43,8 @@ pub struct InvariantConfig { pub timeout: Option, /// Display counterexample as solidity calls. pub show_solidity: bool, + /// Whether to collect and display edge coverage metrics. + pub show_edge_coverage: bool, } impl Default for InvariantConfig { @@ -64,6 +66,7 @@ impl Default for InvariantConfig { show_metrics: true, timeout: None, show_solidity: false, + show_edge_coverage: false, } } } @@ -88,6 +91,7 @@ impl InvariantConfig { show_metrics: true, timeout: None, show_solidity: false, + show_edge_coverage: false, } } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index b84afccac63e9..54a73cf2733b8 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -357,6 +357,10 @@ impl<'a> InvariantExecutor<'a> { runs < self.config.runs }; + // Invariant runs with edge coverage if corpus dir is set or showing edge coverage. + let edge_coverage_enabled = + self.config.corpus_dir.is_some() || self.config.show_edge_coverage; + 'stop: while continue_campaign(runs) { let initial_seq = corpus_manager.new_sequence(&invariant_test)?; @@ -407,9 +411,9 @@ impl<'a> InvariantExecutor<'a> { // Collect line coverage from last fuzzed call. invariant_test.merge_coverage(call_result.line_coverage.clone()); - // If coverage guided fuzzing is enabled then merge edge count with current history + // If running with edge coverage then merge edge count with the current history // map and set new coverage in current run. - if self.config.corpus_dir.is_some() { + if edge_coverage_enabled { let (new_coverage, is_edge) = call_result.merge_edge_coverage(&mut self.history_map); if new_coverage { @@ -514,15 +518,14 @@ impl<'a> InvariantExecutor<'a> { // End current invariant test run. invariant_test.end_run(current_run, self.config.gas_report_samples as usize); - if let Some(progress) = progress { // If running with progress then increment completed runs. progress.inc(1); // Display metrics in progress bar. - if self.config.corpus_dir.is_some() { + if edge_coverage_enabled { progress.set_message(format!("{}", &corpus_manager.metrics)); } - } else if self.config.corpus_dir.is_some() + } else if edge_coverage_enabled && last_metrics_report.elapsed() > DURATION_BETWEEN_METRICS_REPORT { // Display metrics inline if corpus dir set. diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 4d4c39c7c360d..0cf637c4349f0 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -211,7 +211,6 @@ impl InspectorStackBuilder { stack.set_chisel(chisel_state); } stack.collect_line_coverage(line_coverage.unwrap_or(false)); - stack.collect_edge_coverage(true); stack.collect_logs(logs.unwrap_or(true)); stack.print(print.unwrap_or(false)); stack.tracing(trace_mode); diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index ff92acec6f9ec..4129f8938d793 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -705,8 +705,15 @@ impl<'a> FunctionRunner<'a> { let runner = self.invariant_runner(); let invariant_config = &self.config.invariant; + let mut executor = self.clone_executor(); + // Enable edge coverage if running with coverage guided fuzzing or with edge coverage + // metrics (useful for benchmarking the fuzzer). + executor.inspector_mut().collect_edge_coverage( + invariant_config.corpus_dir.is_some() || invariant_config.show_edge_coverage, + ); + let mut evm = InvariantExecutor::new( - self.clone_executor(), + executor, runner, invariant_config.clone(), identified_contracts, diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index a15a4ea3b7876..aa27ba775495c 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -1121,6 +1121,7 @@ corpus_min_size = 0 failure_persist_dir = "cache/invariant" show_metrics = true show_solidity = false +show_edge_coverage = false [labels] @@ -1234,7 +1235,8 @@ exclude = [] "failure_persist_dir": "cache/invariant", "show_metrics": true, "timeout": null, - "show_solidity": false + "show_solidity": false, + "show_edge_coverage": false }, "ffi": false, "allow_internal_expect_revert": false, diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 87723919e400f..408b0b4af251b 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -160,6 +160,7 @@ impl ForgeTestProfile { show_metrics: true, timeout: None, show_solidity: false, + show_edge_coverage: false, }; config.sanitized()