Testing and debugging
Test programs with LiteSVM, profile them, debug SBF traces, and generate coverage.
The default v2 test loop is Rust-first. Instead of starting a local validator and driving it from TypeScript, the scaffolded test loads the compiled SBF program into LiteSVM inside the Rust test process.
anchor-v2-testing wraps LiteSVM, anchor test --profile renders flamegraphs, anchor debugger opens an instruction stepper, and anchor coverage emits LCOV from the same register traces. One test harness covers normal tests, performance work, debugging, and coverage.
LiteSVM
Use anchor-v2-testing as the LiteSVM test harness.
LiteSVM is an in-process Solana VM for tests. It lets a Rust test load the compiled .so, create accounts, send transactions, and inspect results without starting a validator process.
anchor-v2-testing re-exports LiteSVM and common Solana test types. Its svm() function is LiteSVM::new() by default, and switches to a trace-recording VM when the crate is built with the profile feature.
[dev-dependencies]anchor-v2-testing = { git = "https://github.com/otter-sec/anchor.git", branch = "anchor-next" }
[features]profile = ["anchor-v2-testing/profile"]Test shape#
A typical test does the same work a real client would do, but it runs against an in-memory VM:
use anchor_v2_testing::{Keypair, Message, Signer, VersionedMessage, VersionedTransaction};
#[test]fn test_initialize() { let mut svm = anchor_v2_testing::svm(); let payer = Keypair::new();
svm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap(); // add program, build instruction, sign tx, send transaction}The scaffolded template uses this pattern under programs/<name>/tests/.
Why use the wrapper#
Use anchor_v2_testing::svm() instead of calling LiteSVM::new() directly. The wrapper lets the same tests participate in:
- ordinary
anchor test anchor test --profileanchor debuggeranchor coverage
Without the wrapper, the test may pass normally but produce no traces for the tools that need them.
Profiling and debugger
Record SBF register traces, render flamegraphs, and inspect them in the debugger TUI.
anchor test --profile and anchor debugger share a trace pipeline. The test build enables the feature that forwards to anchor-v2-testing/profile, tests call anchor_v2_testing::svm(), and trace files land under target/anchor-v2-profile/.
Profile#
anchor test --profileThe profile run writes per-test traces:
target/
anchor-v2-profile/
deposit_and_withdraw/
- 0001__tx1.regs tx 1, top-level invocation
- 0001__tx1.insns
- 0001__tx1.program_id
- 0002__tx1.regs tx 1, CPI invocation
- 0002__tx1.insns
- 0002__tx1.program_id
The CLI post-processes those traces into flamegraph SVGs next to the trace directories.
Debugger#
anchor debuggerIf traces are missing, the debugger first runs the trace pipeline. Then it opens a TUI over the captured SBF steps. The debugger shows:
- the current instruction and touched registers
- source-line mapping from DWARF
- a timeline of instructions
- CPI frames in the call stack
- per-instruction CU cost where available
Use anchor debugger <test-name-fragment> to capture or open a subset of tests.
Skip run#
When traces already exist:
anchor debugger --skip-runUse this for fast TUI iteration after a previous profile/debugger run.
GDB mode#
anchor debugger --gdbGDB mode drives the solana-sbpf gdb stub and is much slower than the default trace path. Use it when you need breakpoint-style debugging rather than trace navigation.
Common failure#
If the debugger reports no traces, check that the test calls anchor_v2_testing::svm() and that the package has a feature forwarding to anchor-v2-testing/profile.
Coverage
Generate LCOV coverage from SBF register traces.
anchor coverage maps executed SBF program counters back to Rust source lines and emits LCOV. Run it from inside a program directory unless you pass --trace-dir to point at an existing trace set.
cd programs/counteranchor coverageBy default, output lands at:
target/coverage/sbf.lcovOptions#
Useful flags:
Coverage uses the same underlying trace data as profiling and debugger, so the same test-harness requirement applies. Tests should call anchor_v2_testing::svm().
DWARF#
The command builds programs with DWARF debug info so addr2line can resolve PCs to source. If source lines are missing, make sure the deploy .so exists and was built by the same source tree that produced the traces.