Skip to content

Commit e7e5ec8

Browse files
shogochiaiclaudelwshang
authored
feat(instrument): Add --heap-trace flag for stable memory-free profiling (#107)
* feat(instrument): Add --heap-trace flag for stable memory-free profiling This adds a new `--heap-trace` flag to the `instrument` command that stores profiling traces in WASM linear memory instead of stable memory. ## Problem The current `instrument` command assumes exclusive use of stable memory for storing profiling logs. Canisters built with Emscripten, Idris2, AssemblyScript, or other toolchains that use stable memory for their own purposes will trap when calling `__get_profiling` because it attempts to read incompatible memory structures. ## Solution The `--heap-trace` flag enables profiling while avoiding stable memory entirely: - Allocates a dedicated trace buffer at the end of WASM linear memory - Uses `memory.grow` to extend the heap by `--heap-pages` (default: 64 pages = 4MB) - Stores function entry/exit traces in the heap buffer - `__get_profiling` reads from heap memory instead of stable memory - Skips pre_upgrade/post_upgrade persistence (traces don't survive upgrades) ## Usage ```bash ic-wasm input.wasm -o output.wasm instrument --heap-trace ic-wasm input.wasm -o output.wasm instrument --heap-trace --heap-pages 128 ``` ## Trade-offs - Traces do not persist across canister upgrades - Buffer size is limited by available heap memory - Detailed function traces are preserved (unlike --heap-only in PR #106) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(heap-trace): Increase memory maximum to allow memory.grow When using --heap-trace, the WASM memory maximum must be increased to accommodate the trace buffer. Without this fix, memory.grow fails silently when the module's max equals its initial size. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Update instrument documentation to reflect heap-trace and stub-wasi features - Restructure Instrument section to show execution tracing with two modes (stable memory and heap memory) and WASI compatibility as separate capabilities - Add dedicated "Heap-based tracing" subsection with usage and key differences - Update usage examples to include --heap-trace and --heap-pages flags - Consolidate CHANGELOG entries into single enhancement with sub-points Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Linwei Shang <linwei.shang@dfinity.org>
1 parent 9bcb02a commit e7e5ec8

File tree

10 files changed

+292
-9
lines changed

10 files changed

+292
-9
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [unreleased]
88

9-
* Add `--stub-wasi` flag to `instrument` subcommand to replace WASI imports with stub functions.
9+
* Enhance `instrument` subcommand with new capabilities:
10+
- Add `--stub-wasi` flag to replace WASI imports with stub functions, enabling modules compiled with Emscripten or wasi-sdk to run on the Internet Computer.
11+
- Add `--heap-trace` flag to store execution traces in heap memory instead of stable memory, enabling profiling for canisters that use stable memory for their own purposes (e.g., Emscripten, Idris2, AssemblyScript-based canisters).
1012

1113
## [0.9.9] - 2025-11-18
1214

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,12 @@ Usage: `ic-wasm <input.wasm> check-endpoints [--candid <file>] [--hidden <file>]
127127
### Instrument (experimental)
128128

129129
Provides instrumentation capabilities for canister WebAssembly modules:
130-
- **Execution tracing**: Instrument canister methods to emit execution trace to stable memory for performance profiling
130+
- **Execution tracing**: Instrument canister methods to emit execution trace for performance profiling
131+
- Stable memory mode (default): Stores traces in stable memory, persists across upgrades
132+
- Heap memory mode (`--heap-trace`): Stores traces in heap memory, for canisters that use stable memory for their own purposes
131133
- **WASI compatibility**: Replace WASI imports with stub functions to enable modules compiled with Emscripten or wasi-sdk to run on the Internet Computer
132134

133-
Usage: `ic-wasm <input.wasm> -o <output.wasm> instrument [--trace-only func1] [--start-page 16] [--page-limit 30] [--stub-wasi]`
135+
Usage: `ic-wasm <input.wasm> -o <output.wasm> instrument [--trace-only func1] [--start-page 16] [--page-limit 30] [--heap-trace] [--heap-pages 64] [--stub-wasi]`
134136

135137
#### Execution tracing
136138

@@ -207,6 +209,21 @@ fn post_upgrade() {
207209
* We cannot measure query calls.
208210
* No concurrent calls.
209211

212+
#### Heap-based tracing
213+
214+
The `--heap-trace` flag provides an alternative tracing mode that stores execution traces in heap memory instead of stable memory. This is useful for canisters that use stable memory for their own purposes (e.g., Emscripten, Idris2, or AssemblyScript-based canisters).
215+
216+
**Usage**: `ic-wasm <input.wasm> -o <output.wasm> instrument --heap-trace [--heap-pages 64]`
217+
218+
**Key differences from stable memory tracing**:
219+
- Traces are stored in WASM linear memory and **will not persist across upgrades**
220+
- No need to pre-allocate stable memory or specify `--start-page`
221+
- The `--heap-pages` flag controls buffer size (default: 64 pages = 4MB)
222+
- Memory maximum is automatically increased to accommodate the trace buffer
223+
- Cannot be used together with `--start-page` or `--page-limit`
224+
225+
The same query endpoints (`__get_profiling`, `__get_cycles`, etc.) work with both stable and heap-based tracing.
226+
210227
#### Stubbing WASI imports
211228

212229
The `--stub-wasi` flag replaces WASI imports with local stub functions, enabling WASM modules compiled with Emscripten or wasi-sdk to run on the Internet Computer. Without this flag, such modules would fail at install time with errors like:

src/bin/main.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,16 @@ enum SubCommand {
101101
/// The number of pages of the preallocated stable memory
102102
#[clap(short, long, requires("start_page"))]
103103
page_limit: Option<i32>,
104+
/// Store profiling traces in heap memory instead of stable memory.
105+
/// Use this for canisters that use stable memory for their own purposes
106+
/// (e.g., Emscripten, Idris2, AssemblyScript-based canisters).
107+
/// Traces are stored in WASM linear memory and will not persist across upgrades.
108+
#[clap(long, conflicts_with_all = &["start_page", "page_limit"])]
109+
heap_trace: bool,
110+
/// Number of WASM pages (64KB each) to allocate for heap trace buffer.
111+
/// Only used with --heap-trace. Default: 64 (4MB total)
112+
#[clap(long, default_value = "64", requires = "heap_trace")]
113+
heap_pages: i32,
104114
/// Replace WASI imports with stub functions that return 0 (success)
105115
#[clap(long)]
106116
stub_wasi: bool,
@@ -167,13 +177,17 @@ fn main() -> anyhow::Result<()> {
167177
trace_only,
168178
start_page,
169179
page_limit,
180+
heap_trace,
181+
heap_pages,
170182
stub_wasi,
171183
} => {
172184
use ic_wasm::instrumentation::{instrument, Config};
173185
let config = Config {
174186
trace_only_funcs: trace_only.clone().unwrap_or(vec![]),
175187
start_address: start_page.map(|page| i64::from(page) * 65536),
176188
page_limit: *page_limit,
189+
heap_trace: *heap_trace,
190+
heap_pages: *heap_pages,
177191
stub_wasi: *stub_wasi,
178192
};
179193
instrument(&mut m, config).map_err(|e| anyhow::anyhow!("{e}"))?;

0 commit comments

Comments
 (0)