Skip to main content
Source mapping is the feature that connects WASM execution failures back to your original Rust source code. When your contract traps or fails, Erst can show you the exact file and line number where the problem occurred.

How it works

Source mapping relies on DWARF debug symbols embedded in your contract’s WASM:
1

Compile with debug symbols

Build your contract with debug information included in the WASM binary.
2

DWARF parsing

Erst reads the .debug_info and .debug_line sections from the WASM.
3

Instruction mapping

When a trap occurs, Erst maps the WASM instruction offset to source location.
4

Display source context

The exact file, line, and column are displayed in the trace.

Enabling debug symbols

Compile your Soroban contract with debug symbols:

Using Cargo features

Add debug features to your contract build:
Cargo.toml
[profile.release]
debug = true
Then build:
stellar contract build

Alternative: Debug profile

Build with the debug profile:
stellar contract build --profile dev
Debug symbols increase WASM file size significantly. Only use them for development and debugging.

Verify debug symbols

Check if your WASM contains debug information:
wasm-objdump -h target/wasm32-unknown-unknown/release/contract.wasm | grep debug
You should see sections like:
.debug_info
.debug_line
.debug_str
.debug_abbrev

Using source mapping

When debugging with source-mapped WASM, Erst automatically shows source locations:
erst debug <transaction-hash> --network testnet

Output with source mapping

When a failure occurs, you’ll see:
❌ Error: Contract execution failed

WASM Instruction: i32.load offset=8
📄 Source: token.rs:45

45 │     let balance = storage.get(&recipient)?;
   │                   ^^^^^^^^^^^^^^^^^^^^^^^ Out of bounds access

Output without source mapping

Without debug symbols, you only see:
❌ Error: Contract execution failed

WASM Instruction: i32.load offset=8
Without debug symbols, you’ll have to manually correlate WASM instructions with your source code, which is extremely difficult.

Source mapping in the interactive viewer

The interactive trace viewer shows source locations at each step:
erst debug <transaction-hash> --interactive
Navigate to any step and see:
📍 Current State
════════════════════════════════════════════════════════════════════
Step: 125/248
Operation: ContractCall
Function: transfer

📄 Source: src/token.rs:45
🔗 GitHub: https://github.com/org/repo/blob/main/src/token.rs#L45
When in a Git repository, Erst automatically generates clickable GitHub links:
1

Git repository detection

Erst detects if you’re in a Git repository.
2

Remote URL extraction

Reads the GitHub remote URL from .git/config.
3

Commit hash

Gets the current commit hash to create a permanent link.
4

Line number

Appends #L<line> to the URL for direct navigation.
Example generated link:
https://github.com/myorg/myrepo/blob/abc123/contracts/token/src/lib.rs#L45

Split-pane source view

View trace and source code side by side:
erst debug <transaction-hash> --interactive
# Then in the viewer:
sp
The split pane shows:
┌─────────────────────────────────┬─────────────────────────────────┐
│ Step: 125                       │ src/token.rs                    │
│ Function: transfer              │                                 │
│ Contract: CDLZFC...             │  43: pub fn transfer(           │
│                                 │  44:     amount: i128,          │
│ Arguments:                      │  45:     recipient: Address,    │
│   amount: i128(1000000)         │  46: ) -> Result<(), Error> {   │
│   recipient: Address("GC...")   │  47:     let balance = storage │
│                                 │  48:         .get(&recipient)?; │ ← Error
│ ❌ Error: Out of bounds         │  49:     require!(              │
│    at token.rs:48               │  50:         balance >= amount, │
│                                 │  51:         Error::Insufficient│
└─────────────────────────────────┴─────────────────────────────────┘
The split-pane view highlights the exact line where execution is or where an error occurred.

Source mapping architecture

Erst’s source mapping implementation consists of:

DWARF parser

Location: internal/dwarf/parser.go (Go) and src/source_mapper.rs (Rust)
  • Parses .debug_info sections for compilation units
  • Reads .debug_line sections for instruction-to-line mappings
  • Handles optimized code and inlined functions

Source mapper

Location: internal/trace/sourcemap.go
  • Maps WASM instruction offsets to source locations
  • Caches mappings for performance
  • Provides graceful fallback when debug info is missing

Source context loader

Location: internal/trace/splitpane.go
  • Reads source files from disk
  • Extracts context lines around the target line
  • Handles missing files gracefully

Debug symbol format

DWARF sections

Erst reads these DWARF sections from WASM:
SectionPurpose
.debug_infoCompilation units, types, variables
.debug_lineLine number program for instruction mapping
.debug_strString table for names and paths
.debug_abbrevAbbreviation tables for compact encoding
.debug_rangesAddress ranges for scopes

Optimization handling

Optimized builds present challenges:
  • Inlining: Functions may be inlined, making the call stack shallow
  • Dead code elimination: Some code may not exist in the WASM
  • Register allocation: Variables may not have stable locations
[profile.dev]
opt-level = 0
debug = true
For the most accurate source mapping during debugging, use unoptimized builds. For production, optimize without debug symbols.

Local variable inspection

With debug symbols, you can inspect local variables at trap points:
erst debug <transaction-hash> --interactive
# Navigate to the trap
t  # Show trap info
Output:
❌ Memory Trap Detected

Type: i32.load offset=8 out of bounds
Step: 145/248

Source Location:
  File: src/token.rs
  Line: 45
  Column: 12

Local Variables:
  amount: i128(1000000)
  recipient: Address("GC3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZ...")
  balance_ptr: 0x10008  ← Out of bounds!
  sender: Address("GDLZFC3SYJYDZT7K67VZ75HPJVI...")

Function Arguments:
  env: Env
  from: Address("GDLZFC...")
  to: Address("GC3D5K...")
  amount: i128(1000000)
Local variable inspection requires DWARF .debug_loc sections and works best with unoptimized builds.

Source mapping in error suggestions

The error suggestion engine uses source locations to provide context:
=== Potential Fixes (Heuristic Analysis) ===

1. 🔴 [Confidence: high]
   Location: token.rs:45
   Potential Fix: Array access at line 45 is out of bounds.
   Ensure the pointer is within the allocated memory range.
See Transaction debugging for more on error suggestions.

Troubleshooting

No source locations shown

1

Check debug symbols

Verify your WASM has debug sections:
wasm-objdump -h contract.wasm | grep debug
2

Rebuild with debug

Add debug = true to your Cargo.toml and rebuild.
3

Check WASM size

Debug symbols increase size significantly. If size is similar to release build, debug info may be missing.

Wrong line numbers

1

Verify build

Ensure the WASM you’re debugging matches your source code.
2

Check optimization

Highly optimized builds can have imprecise line mappings.
3

Use dev profile

Build with --profile dev for best accuracy.

Missing source files

1

Check file paths

DWARF sections contain absolute or relative paths to source files.
2

Run from repo root

Erst looks for source files relative to the current directory.
3

Check Git status

Ensure source files exist at the expected locations.

Best practices

Development builds

For debugging and development:
Cargo.toml
[profile.dev]
opt-level = 0
debug = true
stellar contract build --profile dev

Production builds

For deployment to mainnet:
Cargo.toml
[profile.release]
opt-level = 3
debug = false  # Remove debug symbols
strip = true   # Strip additional metadata
stellar contract build --release

CI/CD

Create separate build jobs:
.github/workflows/build.yml
- name: Build with debug symbols
  run: stellar contract build --profile dev
  
- name: Build for production
  run: stellar contract build --release

Future enhancements

Upcoming source mapping features:
  • Full stack trace with source locations for all frames
  • Instruction-level stepping through source code
  • Source-aware breakpoints in interactive viewer
  • Integration with IDEs for click-through debugging
  • Support for other debug formats beyond DWARF

Next steps