docs/dev/showmap.md
forge test --showmap-out <DIR> replays the persisted fuzz/invariant corpus and
emits AFL-afl-showmap-style coverage files. Output is consumable by tools like
riesentoaster/differential-coverage
for cross-fuzzer / cross-approach coverage comparisons.
# 1. Run a normal campaign with `corpus_dir` configured to populate the corpus.
forge test
# 2. Replay it.
forge test \
--showmap-out coverage_data \
--showmap-approach foundry \
--showmap-domain evm
This skips the regular fuzz/invariant campaign and unit/table tests, then for every selected fuzz/invariant test:
--showmap-corpus-dir <PATH> override).worker0/corpus/*.json[.gz].<showmap-out>/<approach>__<suite>__<test>/.| Flag | Description |
|---|---|
--showmap-out <DIR> | Output root. Required to enable showmap mode. |
--showmap-approach <NAME> | Approach prefix; test identity is appended to form the dir name (default: replay). |
--showmap-trial <NAME> | Trial id used as the filename (default: trial-<unix_nanos>, unique per invocation so reruns don't overwrite). |
--showmap-domain <evm|sancov|both> | Bitmap(s) to dump (default: evm). |
--showmap-per-input | Emit one file per corpus entry instead of one aggregated per test. |
--showmap-corpus-dir <PATH> | Override the corpus dir to replay. |
<showmap-out>/<approach>__<suite>__<test>/<trial>.txt # aggregated
<showmap-out>/<approach>__<suite>__<test>/<trial>__<uuid>-<ts>.txt # --showmap-per-input
Each test gets its own approach dir so files inside it are trials of the same test,
which is the layout differential-coverage expects. <suite> is the full
path/to/File.sol:Contract identifier with /, \, and : replaced by _.
Each line: <id>:<count> where count is the saturating-summed raw hitcount.
Zero-hit edges are omitted. IDs are deterministic across forge processes:
| Domain | ID format | Meaning |
|---|---|---|
evm | evm_<bytecode_hash[:16hex]>_<pc:04x> | The first 8 bytes of the keccak256 deployed-bytecode hash + the program counter that was hit. Source: line-coverage HitMap. |
sancov | sancov_0x<guard_idx:04x> | Sancov guard index assigned at link time. |
The underscore separator (rather than :) between fields keeps the
<id>:<count> parser unambiguous.
To produce a campaign directory comparing approaches:
# Per-approach dirs are created automatically. Each invocation appends a new
# trial file; use --showmap-trial to set a stable id (e.g. across reruns).
forge test --showmap-out coverage_data --showmap-approach foundry --showmap-trial run_1
forge test --showmap-out coverage_data --showmap-approach foundry --showmap-trial run_2
# Other tools (echidna, medusa, …) write to the same `coverage_data/<name>/` layout.
# Optional: a "seeds-only" baseline produced by replaying just an initial corpus.
forge test --showmap-out coverage_data --showmap-approach seeds \
--showmap-corpus-dir path/to/seeds_corpus
differential-coverage relcov coverage_data
differential-coverage relscore coverage_data
forge fuzz replay --corpus-dir <PATH> replays corpus entries as seeds and
reports whether they execute successfully for the selected targets. It is not
the persisted-failure replay path. To reproduce the last saved fuzz failure,
run forge fuzz replay without --corpus-dir, or replay a specific failure
with forge fuzz replay --fuzz-input-file <PATH>. --corpus-dir and
--fuzz-input-file cannot be combined. The selected tests must contain exactly
one stateless fuzz function matching the artifact's selector.corpus_dir configured is SKIPped with reason
"no corpus_dir configured for this test".corpus_dir whose path does not exist on disk produces a FAIL
with reason "corpus directory not found: <path>".corpus_dir that exists but is empty (or whose entries are all
non-replayable for the current target) produces (replay: 0 entries, 0 files)
and the test is PASS.--showmap-domain sancov (or both) on a build without sancov
instrumentation produces no sancov lines; a warning is emitted.--showmap-per-input for the latter.