Skip to content

Commit

Permalink
adopt to new meter, streaming __get_profiling (#41)
Browse files Browse the repository at this point in the history
* bug fix to always allocate 2M heap memory in `__get_profiling`
* `use-new-metering` flag
* streaming `__get_profiling`
  • Loading branch information
chenyan-dfinity committed Oct 11, 2023
1 parent 822ebef commit a2ceb43
Show file tree
Hide file tree
Showing 14 changed files with 383 additions and 111 deletions.
96 changes: 48 additions & 48 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ic-wasm"
version = "0.5.1"
version = "0.6.0"
authors = ["DFINITY Stiftung"]
edition = "2021"
description = "A library for performing Wasm transformations specific to canisters running on the Internet Computer"
Expand All @@ -19,7 +19,7 @@ required-features = ["exe"]

[dependencies]
walrus = "0.20.1"
candid = "0.9.0"
candid = "0.9.9"
rustc-demangle = "0.1"
thiserror = "1.0.35"

Expand Down
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ Usage: `ic-wasm <input.wasm> -o <output.wasm> instrument --trace-only func1 --tr
Instrumented canister has the following additional endpoints:

* `__get_cycles: () -> (int64) query`. Get the current cycle counter.
* `__get_profiling: () -> (vec { record { int32; int64 }}) query`. Get the execution trace log.
* `__get_profiling: (idx:int32) -> (vec { record { int32; int64 }}, opt int32) query`. Get the execution trace log, starting with `idx` 0. If the log is larger than 2M, it returns the first 2M of trace, and the next `idx` for the next 2M chunk.
* `__toggle_tracing: () -> ()`. Disable/enable logging the execution trace.
* `__toggle_entry: () -> ()`. Disable/enable clearing exection trace for each update call.
* `icp:public name` metadata. Used to map func_id from execution trace to function name.
Expand All @@ -110,7 +110,7 @@ When `--trace-only` flag is provided, the counter and trace logging will only ha

By default, execution trace is stored in the first few pages (up to 32 pages) of stable memory. Without any user side support, we cannot profile upgrade or code which accesses stable memory. If the canister can pre-allocate a fixed region of stable memory at `canister_init`, we can then pass this address to `ic-wasm` via the `--start-page` flag, so that the trace is written to this pre-allocated space without corrupting the rest of the stable memory access.

Another optional flag `--page-limit` specifies the number of pre-allocated pages in stable memory. By default, it's set to 30 pages. We only store trace up to `page-limit` pages, the remaining trace is dropped. Currently, due to the message size limit, we can only store 2M of trace data, which equates to roughly 30 pages. This limitation can be lifted in the future by supporting streamed output of the trace.
Another optional flag `--page-limit` specifies the number of pre-allocated pages in stable memory. By default, it's set to 4096 pages (256MB). We only store trace up to `page-limit` pages, the remaining trace is dropped.

The recommended way of pre-allocating stable memory is via the `Region` library in Motoko, and `ic-stable-structures` in Rust. But developers are free to use any other libraries or even the raw stable memory system API to pre-allocate space, as long as the developer can guarantee that the pre-allocated space is not touched by the rest of the code.

Expand All @@ -121,7 +121,7 @@ import Region "mo:base/Region";
actor {
stable let profiling = do {
let r = Region.new();
ignore Region.grow(r, 32);
ignore Region.grow(r, 4096); // Increase the page number if you need larger log space
r;
};
...
Expand All @@ -146,7 +146,7 @@ const UPGRADES: MemoryId = MemoryId::new(1);
#[ic_cdk::init]
fn init() {
let memory = MEMORY_MANAGER.with(|m| m.borrow().get(PROFILING));
memory.grow(32);
memory.grow(4096); // Increase the page number if you need larger log space
...
}
#[ic_cdk::pre_upgrade]
Expand All @@ -163,10 +163,9 @@ fn post_upgrade() {

#### Current limitations

* Without pre-allocating stable memory from user code, we cannot profile upgrade or code that accesses stable memory.
* Without pre-allocating stable memory from user code, we cannot profile upgrade or code that accesses stable memory. You can profile traces larger than 256M, if you pre-allocate large pages of stable memory and specify the `page-limit` flag. Larger traces can be fetched in a streamming fashion via `__get_profiling(idx)`.
* Since the pre-allocation happens in `canister_init`, we cannot profile `canister_init`.
* If heartbeat is present, it's hard to measure any other method calls. It's also hard to measure a specific heartbeat event.
* We only store the first 2M of profiling data.
* We cannot measure query calls.
* No concurrent calls.

Expand Down
5 changes: 5 additions & 0 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ enum SubCommand {
/// The number of pages of the preallocated stable memory
#[clap(short, long, requires("start_page"))]
page_limit: Option<i32>,
/// Use the new metering cost, default to false. This flag will eventually be removed and set to true after the mainnet has adapted the new metering.
#[clap(short, long)]
use_new_metering: bool,
},
}

Expand Down Expand Up @@ -113,12 +116,14 @@ fn main() -> anyhow::Result<()> {
trace_only,
start_page,
page_limit,
use_new_metering,
} => {
use ic_wasm::instrumentation::{instrument, Config};
let config = Config {
trace_only_funcs: trace_only.clone().unwrap_or(vec![]),
start_address: start_page.map(|page| page * 65536),
page_limit: *page_limit,
use_new_metering: *use_new_metering,
};
instrument(&mut m, config).map_err(|e| anyhow::anyhow!("{e}"))?;
}
Expand Down
Loading

0 comments on commit a2ceb43

Please sign in to comment.