Skip to main content

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}