Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 7 additions & 13 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,33 @@ jobs:
name: Build and Test
runs-on: ubuntu-latest
env:
WASM_TOOLS_VERSION: 1.245.1
WIT_BINDGEN_VERSION: 0.53.1
WAC_VERSION: 0.9.0
WASMTIME_VERSION: 41.0.3
VICEROY_VERSION: 0.16.4
steps:
- uses: actions/checkout@v5
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
target: wasm32-unknown-unknown, wasm32-wasip2
components: rustfmt, clippy
- name: Install wasm-tools, wit-bindgen and wac
- uses: bytecodealliance/actions/wasmtime/setup@v1
- uses: bytecodealliance/actions/wasm-tools/setup@v1
- uses: bytecodealliance/actions/wit-bindgen/setup@v1
with:
version: ${{ env.WIT_BINDGEN_VERSION }}
- name: Install wac
run: |
tmp=$(mktemp -d)
curl -sLo "$tmp/wasm-tools.tar.gz" https://github.com/bytecodealliance/wasm-tools/releases/download/v${{ env.WASM_TOOLS_VERSION }}/wasm-tools-${{ env.WASM_TOOLS_VERSION }}-x86_64-linux.tar.gz
tar -xz -f "$tmp/wasm-tools.tar.gz" -C "$tmp"
mv "$tmp/wasm-tools-${{ env.WASM_TOOLS_VERSION }}-x86_64-linux/wasm-tools" /usr/local/bin/
curl -sLo "$tmp/wit-bindgen.tar.gz" https://github.com/bytecodealliance/wit-bindgen/releases/download/v${{ env.WIT_BINDGEN_VERSION }}/wit-bindgen-${{ env.WIT_BINDGEN_VERSION }}-x86_64-linux.tar.gz
tar -xz -f "$tmp/wit-bindgen.tar.gz" -C "$tmp"
mv "$tmp/wit-bindgen-${{ env.WIT_BINDGEN_VERSION }}-x86_64-linux/wit-bindgen" /usr/local/bin/
curl -sLo "$tmp/wac-cli" https://github.com/bytecodealliance/wac/releases/download/v${{ env.WAC_VERSION }}/wac-cli-x86_64-unknown-linux-musl
chmod a+x "$tmp/wac-cli"
mv "$tmp/wac-cli" /usr/local/bin/wac
- name: Install viceroy and wasmtime
- name: Install viceroy
run: |
tmp=$(mktemp -d)
curl -sLo "$tmp/viceroy.tar.gz" https://github.com/fastly/Viceroy/releases/download/v${{ env.VICEROY_VERSION }}/viceroy_v${{ env.VICEROY_VERSION }}_linux-amd64.tar.gz
tar -xz -f "$tmp/viceroy.tar.gz" -C "$tmp"
chmod a+x "$tmp/viceroy"
mv "$tmp/viceroy" /usr/local/bin/
curl -sLo "$tmp/wasmtime.tar.xz" https://github.com/bytecodealliance/wasmtime/releases/download/v${{ env.WASMTIME_VERSION }}/wasmtime-v${{ env.WASMTIME_VERSION }}-x86_64-linux.tar.xz
tar -xJ -f "$tmp/wasmtime.tar.xz" -C "$tmp"
mv "$tmp/wasmtime-v${{ env.WASMTIME_VERSION }}-x86_64-linux/wasmtime" /usr/local/bin/
- uses: actions/cache@v5
with:
path: |
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ test-fuzz:
RUSTFLAGS="" $(MAKE) run-fuzz WASM=tests/calculator.wasm

test-record:
$(MAKE) run-record WASM=tests/rust.wasm
$(MAKE) run-record WASM=tests/go.wasm
$(MAKE) run-record WASM=tests/python.wasm
$(MAKE) run-record WASM=tests/rust.wasm
# test the same trace with a different wasm replay
target/release/proxy-component instrument -m replay tests/rust.debug.wasm
wasmtime --invoke 'start()' composed.wasm < trace.out

run-fuzz:
target/release/proxy-component instrument -m fuzz $(WASM)
Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
This repository explores the concept of virtualizing and/or instrumenting WIT components. Given a Wasm component,
we synthesize a new component that virtualizes the host interface and can optionally call the original host when necessary.
This synthesized component can be linked, via `wac`, to produce a resulting component with the exact same WIT interface as the original,
but with the added side effects from the virtualized component. This allows us to instrument or virtualize the Wasm component without modifying the user code.
but with the added side effects from the virtualized component. This allows us to instrument or virtualize the Wasm component without modifying the user code, nor the host runtime.

Currently, the tool focuses on using this technique to perform fuzzing, and record & replay for Wasm components.
In the future, we can apply the same technique to other use cases, such as generating adapters.
Expand All @@ -20,8 +20,9 @@ To test record and fuzzing, run `make test`.

```
$ proxy-component instrument -m record <component.wasm>
$ <your_wasm_runtime> composed.wasm > trace.out # store the stdout trace to trace.out
```
Run `composed.wasm` in the host runtime which the original wasm is supposed to run. The tool provides a guest implementation for record and replay APIs, which outputs the trace to stdout while recording, and reads the trace from stdin while replay.
Run `composed.wasm` in the host runtime which the original wasm is supposed to run. The tool provides a [guest implementation](components/recorder/) for record and replay APIs, which outputs the trace to stdout while recording, and reads the trace from stdin while replay.

The host runtime can also choose to implement the [`record` interface](https://github.com/chenyan2002/proxy-component/blob/main/assets/recorder.wit#L3). Then we can use the `--use-host-recorder` flag to skip composing the guest-side record implementation.

Expand All @@ -34,16 +35,18 @@ $ proxy-component instrument -m replay <component.wasm>
$ wasmtime --invoke 'start()' composed.wasm < trace.out
```

Note that the trace is self-contained, and `composed.wasm` doesn't have any imports. This means that we can run `composed.wasm` in a regular `wasmtime` without the host interface.
Note that the trace is self-contained, and `composed.wasm` doesn't have any imports. This means that we can run `composed.wasm` in a regular `wasmtime`.

Another interesting use case is that we can replay the trace with a different Wasm binary, likely with a different compiler flag, or
a different optimization strategy, to compare the performance. We have assertions in the replay phase to make sure that the trace
a different optimization strategy, to compare the performance.

We provide a [Debug component](components/debug/) that does not go through instrumentation. You can use the Debug component in your code to perform I/O operations while in the replay mode. We have assertions in the replay phase to make sure that the trace
is still valid with the new binary.

### Fuzzing

```
$ cargo run instrument -m fuzz <component.wasm>
$ proxy-component instrument -m fuzz <component.wasm>
$ wasmtime --invoke 'start()' composed.wasm
```

Expand Down
Binary file modified assets/debug.wasm
Binary file not shown.
1 change: 1 addition & 0 deletions assets/util.wit
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package proxy:util;

interface debug {
print: func(x: string);
eprint: func(x: string);
get-random: func() -> list<u8>;
}

Expand Down
2 changes: 1 addition & 1 deletion assets/workspace_cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["record_imports", "record_exports"]
resolver = "2"

[workspace.dependencies]
wit-bindgen = { version = "0.52.0", default-features = false, features = ["bitflags", "std"] }
wit-bindgen = { version = "0.53.1", default-features = false, features = ["bitflags", "std"] }
wasm-wave = { git = "https://github.com/chenyan2002/wasm-tools.git", branch = "extend-wave", version = "0.239.0", default-features = false }
#wasm-wave = { path = "/Users/chenyan/src/bytecodealliance/wasm-tools/crates/wasm-wave", default-features = false }
arbitrary = "1.4.2"
Expand Down
3 changes: 3 additions & 0 deletions components/debug/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ impl Guest for Component {
fn print(s: String) {
println!("{}", s);
}
fn eprint(s: String) {
eprintln!("{}", s);
}
fn get_random() -> Vec<u8> {
let mut data = vec![0u8; 1024];
getrandom::fill(&mut data).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ impl State {
__params.push(wasm_wave::to_string(&ToValue::to_value(&#arg_names)).unwrap());
)*
let mut __buf = __params.join(",");
proxy::util::debug::print(&format!("import: {}({})", #display_name, __buf));
__buf += #display_name;
proxy::util::debug::print(&format!("import: {}", __buf));
let mut u = Unstructured::new(&__buf.as_bytes());
let res = u.arbitrary().unwrap();
let res_str = wasm_wave::to_string(&ToValue::to_value(&res)).unwrap();
Expand Down
Binary file added tests/rust.debug.wasm
Binary file not shown.
Loading