Debugging Modes
Soroban contracts are as much as possible regular Rust programs and can be debugged using the same tools you'd usually use.
The debugging facilities available differ significantly depending on whether a contract is compiled natively for local testing, or compiled into Wasm for deployment.
Deciding between these two modes and making the most of them while debugging requires a careful understanding of which code is compiled-in to deployed contracts and which code is only available for local testing.
Local-testing mode
It is possible (and encouraged during development) to compile Soroban contracts natively (eg. as x86-64 or AArch64 code) and link against the host environment directly, such that the contract is not a guest running in a Wasm virtual machine at all: it is simply one native library calling another -- its host -- and both host and contract are linked together as a single program, together with a test harness.
This configuration is referred to as "local-testing mode" and since it eliminates the Wasm virtual machine from the debugging experience it has many advantages:
- Tests run much faster since there is no VM interpreting them, and will execute in parallel by default on multiple threads.
- Tests can use numerous standard testing and debugging techniques:
- The standard Rust
#[test]
harness, including push-button IDE support for running and re-running single tests. - Standard IDE-supported debuggers, including IDE support for setting breakpoints and inspecting values in both contract and host.
- Lightweight debugging via standard logging or tracing.
- Systematic testing such as fuzzing, property-testing, or even model checking or formal verification.
- The simplest of all debugging approaches, printing to standard error.
- The standard Rust
Local-testing mode is the default configuration when compiling code targeting your local computer's CPU and operating system, which is what cargo will do if you set up a new Rust project and don't specify a target.
Wasm mode
If on the other hand you wish to compile for deployment, you must tell cargo to build for the Wasm target.
Building for Wasm will disable many of the debugging facilities described above, typically for one of three reasons:
- The Wasm VM simply can't (or the VM we've chosen doesn't) provide them.
- The Wasm VM could provide them but doing so would violate constraints of the contract Rust dialect.
- The Wasm VM could provide them but doing so would make the resulting Wasm code impractically large.
While we encourage most testing to happen in local-testing mode, some problems will obviously only arise in deployment and some debugging facilities thus remain available even there:
- A "sandbox" host with a mock-ledger that can read and write
CONTRACT_DATA
ledger entries to the local filesystem. - A general logging system that allows contracts to log values of the shared host/guest "value" type, even in production.
- User-extensible
Status
codes that can be returned from any contract call to indicate problems.