foundry_evm/executors/sancov.rs
1use super::RawCallResult;
2use foundry_evm_core::evm::FoundryEvmNetwork;
3
4const SANCOV_BUFFER_CAPACITY: usize = 65536;
5
6/// RAII guard that activates sancov coverage collection for the duration of an EVM call.
7///
8/// Allocates a thread-local scratch buffer for sancov hits and sets it as the active coverage map.
9/// After execution, sancov hits are appended to the call result separately from EVM edge coverage.
10pub(super) struct SancovGuard {
11 collect_edges: bool,
12}
13
14thread_local! {
15 static SANCOV_BUFFER: std::cell::RefCell<Vec<u8>> =
16 std::cell::RefCell::new(vec![0u8; SANCOV_BUFFER_CAPACITY]);
17}
18
19impl SancovGuard {
20 pub(super) fn new(collect_edges: bool, collect_trace_cmp: bool) -> Self {
21 if collect_edges {
22 SANCOV_BUFFER.with(|buf| {
23 let mut buf = buf.borrow_mut();
24 buf.fill(0);
25 let ptr = buf.as_mut_ptr();
26 let len = buf.len();
27 foundry_evm_sancov::set_coverage_map(ptr, len);
28 });
29 }
30 if collect_trace_cmp {
31 foundry_evm_sancov::clear_cmp_operands();
32 }
33 Self { collect_edges }
34 }
35
36 /// Populate the result's sancov coverage buffer with edge hits.
37 pub(super) fn append_edges_into<FEN: FoundryEvmNetwork>(result: &mut RawCallResult<FEN>) {
38 let sancov_used = foundry_evm_sancov::sancov_edge_count();
39 if sancov_used == 0 {
40 return;
41 }
42
43 SANCOV_BUFFER.with(|buf| {
44 let buf = buf.borrow();
45 let sancov_slice = &buf[..sancov_used.min(buf.len())];
46
47 if !sancov_slice.iter().any(|&b| b > 0) {
48 return;
49 }
50
51 result.sancov_coverage = Some(sancov_slice.to_vec());
52 });
53 }
54
55 /// Drain captured comparison operands and attach them to the result for dictionary injection.
56 pub(super) fn drain_cmp_into<FEN: FoundryEvmNetwork>(result: &mut RawCallResult<FEN>) {
57 let cmp_values = foundry_evm_sancov::drain_cmp_operands();
58 if !cmp_values.is_empty() {
59 result.sancov_cmp_values = Some(cmp_values);
60 }
61 }
62}
63
64impl Drop for SancovGuard {
65 fn drop(&mut self) {
66 if self.collect_edges {
67 foundry_evm_sancov::clear_coverage_map();
68 }
69 }
70}