How to Use forc-addr2line to Debug Compiled Sway Contracts
forc-addr2line translates bytecode addresses from compiled Sway contracts back to original source code locations using source-map JSON files generated during compilation.
When debugging compiled Sway smart contracts on the Fuel network, raw opcode indices often obscure the underlying source code responsible for specific bytecode instructions. The forc-addr2line command bridges this gap by mapping bytecode addresses directly to Sway source files, enabling developers to pinpoint exactly which lines of code generated specific opcodes.
What is forc-addr2line?
forc-addr2line is a specialized CLI utility within the Fuel Labs Sway toolchain that performs address-to-source mapping for compiled contracts. Unlike traditional debuggers that require DWARF symbols, this tool operates directly on the source-map JSON produced during forc build, making it lightweight and purpose-built for the Sway compiler's output format.
The tool reads the mapping between bytecode addresses and source spans, resolves file paths, and renders highlighted code snippets that show the exact source lines corresponding to a given opcode index.
How forc-addr2line Works
The implementation in forc/src/cli/commands/addr2line.rs orchestrates a four-stage pipeline that transforms raw bytecode addresses into readable source context.
Reading the Source-Map JSON
The command first deserializes the source-map file generated during compilation. In forc-pkg/src/pkg.rs, the compiler writes this JSON alongside the compiled binary when debug info is enabled.
let contents = fs::read(&command.sourcemap_path)?;
let sm: SourceMap = serde_json::from_slice(&contents)?;
The SourceMap struct, defined in sway-core/src/source_map.rs, contains the mapping data structure that links opcode indices to source file locations.
Mapping Opcode Index to Source Span
Once loaded, the tool queries the source map using SourceMap::addr_to_span(opcode_index). This method returns an optional tuple containing the file path and a LocationRange struct that specifies the exact start and end positions in the source file.
if let Some((mut path, range)) = sm.addr_to_span(command.opcode_index) {
// Process the source location
}
The LocationRange contains LineCol positions that identify the precise character offsets within the Sway source file where the opcode was generated.
Resolving Relative Paths and Reading Context
If the stored path is relative, the command resolves it against the user-provided search directory (-S flag). The helper function read_range in forc/src/cli/commands/addr2line.rs then opens the source file and converts LineCol positions into byte offsets using line_col_to_pos.
The tool extracts a configurable window of source lines around the target span, controlled by the --context parameter (defaulting to 2 lines).
Rendering Highlighted Snippets
Finally, the extracted text and location data are wrapped in an annotate_snippets::Snippet struct. The diagnostic renderer, created via forc_util::create_diagnostics_renderer(), prints the snippet to stdout with visual highlighting that marks the exact byte range corresponding to the requested opcode.
let snippet = Snippet {
// title, footer, slices with source text and highlights
};
let renderer = create_diagnostics_renderer();
info!("{}", renderer.render(snippet));
Step-by-Step Workflow for Debugging Contracts
Follow this workflow to debug compiled Sway contracts using opcode addresses:
-
Compile with debug information – Run
forc buildto generate the source-map JSON. Debug builds include this by default.forc build -
Identify the opcode index – Extract the address from a panic message, stack trace, or DWARF debugger output. For example, a trace might indicate "panic at pc 215".
-
Execute forc-addr2line – Run the command with the source-map path, opcode index, and project root.
forc addr2line \ -g ./out/debug/debug_info.json \ -i 215 \ -S . \ -c 4 -
Analyze the output – Review the highlighted source snippet to identify which specific expression or statement generated the opcode at that address.
Command-Line Options and Usage Examples
The forc addr2line command accepts several flags to control its behavior:
-g, --sourcemap-path <PATH>– Required. Path to the source-map JSON file generated during compilation.-i, --opcode-index <INDEX>– Required. The bytecode address (opcode index) to translate.-S, --search-dir <DIR>– Optional. Root directory for resolving relative paths stored in the source map. Defaults to current directory.-c, --context <LINES>– Optional. Number of context lines to display above and below the target source span. Default is 2.
Advanced Example: Library Integration
For tooling that needs programmatic access, you can invoke the addr2line logic directly from Rust:
use forc_cli::commands::addr2line::{Command, exec};
let cmd = Command {
search_dir: std::path::PathBuf::from("."),
sourcemap_path: std::path::PathBuf::from("./out/debug/debug_info.json"),
context: 3,
opcode_index: 215,
};
exec(cmd).expect("addr2line failed");
Key Implementation Files
Understanding the source code helps when extending or debugging the tool itself:
| File | Role |
|---|---|
forc/src/cli/commands/addr2line.rs |
CLI implementation that orchestrates source-map reading, address mapping, and snippet rendering. |
sway-core/src/source_map.rs |
Defines the SourceMap struct and addr_to_span method that maps opcode indices to source locations. |
forc-pkg/src/pkg.rs |
Handles generation and serialization of source-map JSON during the package build process. |
Summary
- forc-addr2line bridges the gap between compiled bytecode and Sway source code by reading source-map JSON files.
- The tool maps opcode indices to exact source file locations using the
SourceMap::addr_to_spanmethod insway-core/src/source_map.rs. - Use the
-gflag to specify the source-map path,-ifor the opcode index, and-cto control context lines. - The implementation in
forc/src/cli/commands/addr2line.rshandles path resolution, source file reading, and highlighted snippet rendering viaannotate_snippets.
Frequently Asked Questions
What file format does forc-addr2line require?
forc-addr2line requires a source-map JSON file generated during compilation. This file is produced automatically when running forc build and contains the mapping between bytecode addresses and source code spans. The tool deserializes this JSON into a SourceMap struct as defined in sway-core/src/source_map.rs.
How do I find the opcode index to use with forc-addr2line?
You can obtain the opcode index from panic messages, stack traces, or DWARF debugger output when running your compiled contract. For example, a runtime error might report "panic at pc 215", where "215" is the opcode index you pass to the -i flag. This index represents the position in the compiled bytecode where execution triggered the event.
Can I use forc-addr2line with release builds?
Yes, forc-addr2line works with both debug and release builds as long as the source-map JSON was generated during compilation. The source map is created by default during forc build, but ensure the debug_info.json or equivalent file exists in your output directory. If the file is missing, verify that your build configuration hasn't disabled debug information generation in forc-pkg/src/pkg.rs.
What happens if the source file changed after compilation?
If the source file was modified after the contract was compiled, forc-addr2line may return inaccurate line numbers or fail to locate the exact source span. The tool relies on the byte offsets and line/column positions stored in the source-map JSON, which correspond to the state of the source files at compile time. For accurate debugging, ensure your source files match the version used during the build that generated the source map.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →