From ed75704971cc402802a8bc498ead3a9ab7714d25 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 28 Sep 2020 15:14:47 -0700 Subject: [PATCH 1/4] Add early exit example --- Cargo.toml | 5 ++ examples/early_exit.rs | 82 ++++++++++++++++++++++++++++ examples/engine_cross_compilation.rs | 5 +- examples/wasi.rs | 6 +- 4 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 examples/early_exit.rs diff --git a/Cargo.toml b/Cargo.toml index fb84f986c14..f034431fab6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -128,6 +128,11 @@ test-no-traps = ["wasmer-wast/test-no-traps"] name = "static_and_dynamic_functions" harness = false +[[example]] +name = "early-exit" +path = "examples/early_exit.rs" +required-features = ["cranelift"] + [[example]] name = "engine-jit" path = "examples/engine_jit.rs" diff --git a/examples/early_exit.rs b/examples/early_exit.rs new file mode 100644 index 00000000000..7253fd519ae --- /dev/null +++ b/examples/early_exit.rs @@ -0,0 +1,82 @@ +//! This example shows how the host can terminate execution of Wasm early from +//! inside a host function called by the Wasm. + +use anyhow::bail; +use std::fmt; +use wasmer::{imports, wat2wasm, Function, Instance, Module, NativeFunc, RuntimeError, Store}; +use wasmer_compiler_cranelift::Cranelift; +use wasmer_engine_jit::JIT; + +// First we need to create an error type that we'll use to signal the end of execution. +#[derive(Debug, Clone, Copy)] +struct ExitCode(u32); + +// This type must implement `std::error::Error` so we must also implement `std::fmt::Display` for it. +impl fmt::Display for ExitCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +// And then we implement `std::error::Error`. +impl std::error::Error for ExitCode {} + +// The host function that we'll use to terminate execution. +fn early_exit() { + // This is where it happens. + RuntimeError::raise(Box::new(ExitCode(1))); +} + +fn main() -> anyhow::Result<()> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + br#" +(module + (type $run_t (func (param i32 i32) (result i32))) + (type $early_exit_t (func (param) (result))) + (import "env" "early_exit" (func $early_exit (type $early_exit_t))) + (func $run (type $run_t) (param $x i32) (param $y i32) (result i32) + (call $early_exit) + (i32.add + local.get $x + local.get $y)) + (export "run" (func $run))) +"#, + )?; + + let store = Store::new(&JIT::new(&Cranelift::default()).engine()); + let module = Module::new(&store, wasm_bytes)?; + + let import_object = imports! { + "env" => { + "early_exit" => Function::new_native(&store, early_exit), + } + }; + let instance = Instance::new(&module, &import_object)?; + + // get the `run` function which we'll use as our entrypoint. + let run_func: NativeFunc<(i32, i32), i32> = + instance.exports.get_native_function("run").unwrap(); + + // When we call a function it can either succeed or fail. + match run_func.call(1, 7) { + Ok(result) => { + bail!( + "Expected early termination with `ExitCode`, found: {}", + result + ); + } + // we're expecting it to fail. + // we attempt to downcast the error into the error type that we were expecting. + Err(e) => match e.downcast::() { + // we found the exit code used to terminate execution. + Ok(exit_code) => { + println!("Exited early with exit code: {}", exit_code); + Ok(()) + } + Err(e) => { + bail!("Unknown error `{}` found. expected `ErrorCode`", e); + } + }, + } +} diff --git a/examples/engine_cross_compilation.rs b/examples/engine_cross_compilation.rs index 1c979513872..272da2cfcee 100644 --- a/examples/engine_cross_compilation.rs +++ b/examples/engine_cross_compilation.rs @@ -27,7 +27,7 @@ use wasmer_engine_native::Native; fn main() -> Result<(), Box> { // Let's declare the Wasm module with the text representation. let wasm_bytes = wat2wasm( - r#" + br#" (module (type $sum_t (func (param i32 i32) (result i32))) (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) @@ -35,8 +35,7 @@ fn main() -> Result<(), Box> { local.get $y i32.add) (export "sum" (func $sum_f))) -"# - .as_bytes(), +"#, )?; // Define a compiler configuration. diff --git a/examples/wasi.rs b/examples/wasi.rs index 72617e83596..c9c779b7450 100644 --- a/examples/wasi.rs +++ b/examples/wasi.rs @@ -21,9 +21,9 @@ use wasmer_engine_jit::JIT; use wasmer_wasi::WasiState; fn main() -> Result<(), Box> { - let wasm_path = format!( - "{}/tests/wasi-wast/wasi/unstable/hello.wasm", - std::env::var("CARGO_MANIFEST_DIR").unwrap() + let wasm_path = concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/wasi-wast/wasi/unstable/hello.wasm" ); // Let's declare the Wasm module with the text representation. let wasm_bytes = std::fs::read(wasm_path)?; From 374cd36ec2381a620780762b046bc996d45f4788 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 29 Sep 2020 10:56:02 -0700 Subject: [PATCH 2/4] Add early exit Wasm C API example (wip) --- lib/c-api/src/wasm_c_api/mod.rs | 20 +++- lib/c-api/tests/CMakeLists.txt | 5 + lib/c-api/tests/assets/call_trap.wasm | Bin 0 -> 66 bytes lib/c-api/tests/assets/call_trap.wat | 10 ++ lib/c-api/tests/wasm-c-api-trap.c | 128 ++++++++++++++++++++++++++ 5 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 lib/c-api/tests/assets/call_trap.wasm create mode 100644 lib/c-api/tests/assets/call_trap.wat create mode 100644 lib/c-api/tests/wasm-c-api-trap.c diff --git a/lib/c-api/src/wasm_c_api/mod.rs b/lib/c-api/src/wasm_c_api/mod.rs index 79737d3ac50..5fc32e4af64 100644 --- a/lib/c-api/src/wasm_c_api/mod.rs +++ b/lib/c-api/src/wasm_c_api/mod.rs @@ -1150,15 +1150,22 @@ pub struct wasm_ref_t; // opaque type which is a `RuntimeError` #[repr(C)] -pub struct wasm_trap_t {} +pub struct wasm_trap_t { + // have a vec of frames +// have a message +} #[no_mangle] -pub unsafe extern "C" fn wasm_trap_delete(trap: Option>) { - if let Some(t_inner) = trap { - let _ = Box::from_raw(t_inner.cast::().as_ptr()); - } +pub unsafe extern "C" fn wasm_trap_new( + store: &mut wasm_store_t, + message: &wasm_message_t, +) -> Box { + todo!() } +#[no_mangle] +pub unsafe extern "C" fn wasm_trap_delete(_trap: Option>) {} + #[no_mangle] pub unsafe extern "C" fn wasm_trap_message( trap: *const wasm_trap_t, @@ -1769,6 +1776,9 @@ pub unsafe extern "C" fn wasm_tabletype_as_externtype( #[allow(non_camel_case_types)] type wasm_name_t = wasm_byte_vec_t; +#[allow(non_camel_case_types)] +type wasm_message_t = wasm_byte_vec_t; + #[repr(C)] #[allow(non_camel_case_types)] pub struct wasm_exporttype_t { diff --git a/lib/c-api/tests/CMakeLists.txt b/lib/c-api/tests/CMakeLists.txt index 728e11181a1..339eaf53f90 100644 --- a/lib/c-api/tests/CMakeLists.txt +++ b/lib/c-api/tests/CMakeLists.txt @@ -35,6 +35,7 @@ add_executable(wasm-c-api-serialize wasm-c-api/example/serialize.c) # Custom Wasm C API tests add_executable(wasm-c-api-wasi wasm-c-api-wasi.c) +add_executable(wasm-c-api-trap wasm-c-api-trap.c) if (DEFINED WASI_TESTS) add_executable(test-wasi-import-object test-wasi-import-object.c) @@ -249,3 +250,7 @@ target_link_libraries(wasm-c-api-wasi general ${WASMER_LIB}) target_compile_options(wasm-c-api-wasi PRIVATE ${COMPILER_OPTIONS}) add_test(wasm-c-api-wasi wasm-c-api-wasi) +set_property(TARGET wasm-c-api-trap PROPERTY C_STANDARD 11) +target_link_libraries(wasm-c-api-trap general ${WASMER_LIB}) +target_compile_options(wasm-c-api-trap PRIVATE ${COMPILER_OPTIONS}) +add_test(wasm-c-api-trap wasm-c-api-trap) diff --git a/lib/c-api/tests/assets/call_trap.wasm b/lib/c-api/tests/assets/call_trap.wasm new file mode 100644 index 0000000000000000000000000000000000000000..44cbb2797532a9beacc593922220267163546cae GIT binary patch literal 66 zcmZQbEY4+QU|?Y6VoG4DuV<`JU|?VpVq{LuE8|K{EXt{jPp!x-VPIrtVq{=vXJjra U%>%0CX5?fLU{GLCV9eqM0IV+yg#Z8m literal 0 HcmV?d00001 diff --git a/lib/c-api/tests/assets/call_trap.wat b/lib/c-api/tests/assets/call_trap.wat new file mode 100644 index 00000000000..6f4bb0f8042 --- /dev/null +++ b/lib/c-api/tests/assets/call_trap.wat @@ -0,0 +1,10 @@ +(module + (type $run_t (func (param i32 i32) (result i32))) + (type $early_exit_t (func (param) (result))) + (import "env" "early_exit" (func $early_exit (type $early_exit_t))) + (func $run (type $run_t) (param $x i32) (param $y i32) (result i32) + (call $early_exit) + (i32.add + (local.get $x) + (local.get $y))) + (export "run" (func $run))) \ No newline at end of file diff --git a/lib/c-api/tests/wasm-c-api-trap.c b/lib/c-api/tests/wasm-c-api-trap.c new file mode 100644 index 00000000000..b2a777c50a7 --- /dev/null +++ b/lib/c-api/tests/wasm-c-api-trap.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include + +#include "wasm.h" +#include "wasmer_wasm.h" + +// Use the last_error API to retrieve error messages +void print_wasmer_error() +{ + int error_len = wasmer_last_error_length(); + printf("Error len: `%d`\n", error_len); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); +} + +wasm_store_t* store = NULL; + +wasm_trap_t* early_exit(wasm_val_t args[], wasm_val_t results[]) { + wasm_message_t* trap_message = NULL; + const char* message_inner = "trapping from a host import"; + wasm_byte_vec_new_uninitialized(trap_message, strlen(message_inner)); + // TODO: should we free this data? + return wasm_trap_new(store, trap_message); +} + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE* file = fopen("assets/call_trap.wasm", "r"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Instantiate. + printf("Instantiating module...\n"); + + // TODO: fill imports + + wasm_functype_t* host_func_type = wasm_functype_new_0_0(); + wasm_func_t* host_func = wasm_func_new(store, host_func_type, (wasm_func_callback_t)early_exit); + wasm_extern_t* host_func_as_extern = wasm_func_as_extern(host_func); + wasm_functype_delete(host_func_type); + + wasm_extern_t* imports[] = { host_func_as_extern }; + + own wasm_instance_t* instance = + wasm_instance_new(store, module, (const wasm_extern_t *const *) imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + print_wasmer_error(); + return 1; + } + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + fprintf(stderr, "found %zu exports\n", exports.size); + + // TODO: + wasm_func_t* run_func = NULL; + if (run_func == NULL) { + printf("> Error accessing export!\n"); + print_wasmer_error(); + return 1; + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + if (wasm_func_call(run_func, NULL, NULL)) { + printf("> Error calling function!\n"); + return 1; + } + + wasm_extern_vec_delete(&exports); + + // NEEDS REVIEW: + for(int i = 0; i < num_imports; ++i) { + wasm_extern_delete(imports[i]); + } + free(imports); + + // Shut down. + printf("Shutting down...\n"); + wasm_func_delete(run_func); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} From 1ed1a52d2505e6a9c90966a605bc593d1469501d Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 29 Sep 2020 14:11:39 -0700 Subject: [PATCH 3/4] Get early exit example working with C API; implement wasm_trap_t API --- CHANGELOG.md | 1 + lib/api/src/lib.rs | 4 +- lib/c-api/src/wasm_c_api/mod.rs | 127 +++++++++++++----- lib/c-api/tests/CMakeLists.txt | 24 ++-- ...m-c-api-trap.c => wasm-c-api-early-exit.c} | 94 ++++++++----- lib/engine/src/trap/frame_info.rs | 2 +- 6 files changed, 176 insertions(+), 76 deletions(-) rename lib/c-api/tests/{wasm-c-api-trap.c => wasm-c-api-early-exit.c} (55%) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea7288f069a..267fd762c7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## **[Unreleased]** +- [#1657](https://github.com/wasmerio/wasmer/pull/1657) Implement `wasm_trap_t` and `wasm_frame_t` for Wasm C API; add examples in Rust and C of exiting early with a host function. - [#1645](https://github.com/wasmerio/wasmer/pull/1645) Move the install script to https://github.com/wasmerio/wasmer-install ## 1.0.0-alpha3 - 2020-09-14 diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index f5631870dfb..6725141aec7 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -70,8 +70,8 @@ pub use wasmer_compiler::{ }; pub use wasmer_compiler::{CpuFeature, Features, Target}; pub use wasmer_engine::{ - ChainableNamedResolver, DeserializeError, Engine, InstantiationError, LinkError, NamedResolver, - NamedResolverChain, Resolver, RuntimeError, SerializeError, + ChainableNamedResolver, DeserializeError, Engine, FrameInfo, InstantiationError, LinkError, + NamedResolver, NamedResolverChain, Resolver, RuntimeError, SerializeError, }; pub use wasmer_types::{ Atomically, Bytes, GlobalInit, LocalFunctionIndex, MemoryView, Pages, ValueType, diff --git a/lib/c-api/src/wasm_c_api/mod.rs b/lib/c-api/src/wasm_c_api/mod.rs index 5fc32e4af64..e336d94eb3a 100644 --- a/lib/c-api/src/wasm_c_api/mod.rs +++ b/lib/c-api/src/wasm_c_api/mod.rs @@ -17,9 +17,9 @@ use crate::c_try; use crate::ordered_resolver::OrderedResolver; use wasmer::{ - Engine, ExportType, Extern, ExternType, Function, FunctionType, Global, GlobalType, ImportType, - Instance, Memory, MemoryType, Module, Mutability, Pages, RuntimeError, Store, Table, TableType, - Val, ValType, + Engine, ExportType, Extern, ExternType, FrameInfo, Function, FunctionType, Global, GlobalType, + ImportType, Instance, Memory, MemoryType, Module, Mutability, Pages, RuntimeError, Store, + Table, TableType, Val, ValType, }; #[cfg(feature = "jit")] use wasmer_engine_jit::JIT; @@ -712,7 +712,13 @@ pub unsafe extern "C" fn wasm_func_new( num_rets ]; - let _traps = callback(processed_args.as_ptr(), results.as_mut_ptr()); + let trap = callback(processed_args.as_ptr(), results.as_mut_ptr()); + dbg!(&trap); + if !trap.is_null() { + let trap: Box = Box::from_raw(trap); + dbg!("raising trap!"); + RuntimeError::raise(Box::new(trap.inner)); + } // TODO: do something with `traps` let processed_results = results @@ -783,7 +789,7 @@ pub unsafe extern "C" fn wasm_func_call( func: &wasm_func_t, args: *const wasm_val_t, results: *mut wasm_val_t, -) -> Option> { +) -> Option> { let num_params = func.inner.ty().params().len(); let params: Vec = (0..num_params) .map(|i| (&(*args.add(i))).try_into()) @@ -798,7 +804,7 @@ pub unsafe extern "C" fn wasm_func_call( } None } - Err(e) => Some(NonNull::new_unchecked(Box::into_raw(Box::new(e)) as _)), + Err(e) => Some(Box::new(e.into())), } } @@ -1151,37 +1157,47 @@ pub struct wasm_ref_t; // opaque type which is a `RuntimeError` #[repr(C)] pub struct wasm_trap_t { - // have a vec of frames -// have a message + inner: RuntimeError, +} + +impl From for wasm_trap_t { + fn from(other: RuntimeError) -> Self { + Self { inner: other } + } } #[no_mangle] pub unsafe extern "C" fn wasm_trap_new( - store: &mut wasm_store_t, + _store: &mut wasm_store_t, message: &wasm_message_t, -) -> Box { - todo!() +) -> Option> { + let message_bytes: &[u8] = message.into_slice()?; + let message_str = c_try!(std::str::from_utf8(message_bytes)); + let runtime_error = RuntimeError::new(message_str); + let trap = runtime_error.into(); + + Some(Box::new(trap)) } #[no_mangle] pub unsafe extern "C" fn wasm_trap_delete(_trap: Option>) {} #[no_mangle] -pub unsafe extern "C" fn wasm_trap_message( - trap: *const wasm_trap_t, - out_ptr: *mut wasm_byte_vec_t, -) { - let re = &*(trap as *const RuntimeError); - // this code assumes no nul bytes appear in the message - let mut message = format!("{}\0", re); - message.shrink_to_fit(); +pub unsafe extern "C" fn wasm_trap_message(trap: &wasm_trap_t, out_ptr: &mut wasm_byte_vec_t) { + let message = trap.inner.message(); + out_ptr.size = message.len(); + // TODO: make helper function for converting `Vec` into owned `wasm_T_vec_t` + let mut dupe_data: Box<[u8]> = message.into_bytes().into_boxed_slice(); + out_ptr.data = dupe_data.as_mut_ptr(); + mem::forget(dupe_data); +} - // TODO use `String::into_raw_parts` when it gets stabilized - (*out_ptr).size = message.as_bytes().len(); - (*out_ptr).data = message.as_mut_ptr(); - mem::forget(message); +#[no_mangle] +pub unsafe extern "C" fn wasm_trap_origin(trap: &wasm_trap_t) -> Option> { + trap.inner.trace().first().map(Into::into).map(Box::new) } +// TODO: old comment, rm after finish implementation // in trap/RuntimeError we need to store // 1. message // 2. origin (frame); frame contains: @@ -1190,11 +1206,19 @@ pub unsafe extern "C" fn wasm_trap_message( // 3. module offset // 4. which instance this was apart of -/*#[no_mangle] -pub unsafe extern "C" fn wasm_trap_trace(trap: *const wasm_trap_t, out_ptr: *mut wasm_frame_vec_t) { - let re = &*(trap as *const RuntimeError); - todo!() -}*/ +#[no_mangle] +pub unsafe extern "C" fn wasm_trap_trace(trap: &wasm_trap_t, out_ptr: &mut wasm_frame_vec_t) { + let frames = trap.inner.trace(); + out_ptr.size = frames.len(); + // TODO: make helper function for converting `Vec` into owned `wasm_T_vec_t` + let mut dupe_data: Box<[wasm_frame_t]> = frames + .iter() + .map(Into::into) + .collect::>() + .into_boxed_slice(); + out_ptr.data = dupe_data.as_mut_ptr(); + mem::forget(dupe_data); +} #[repr(C)] pub struct wasm_extern_t { @@ -1550,9 +1574,52 @@ pub unsafe extern "C" fn wasm_functype_results(ft: &wasm_functype_t) -> *const w out as *const _ } -#[derive(Debug)] +#[derive(Debug, Clone)] #[repr(C)] -pub struct wasm_frame_t {} +pub struct wasm_frame_t { + info: FrameInfo, +} + +impl<'a> From<&'a FrameInfo> for wasm_frame_t { + fn from(other: &'a FrameInfo) -> Self { + other.clone().into() + } +} + +impl From for wasm_frame_t { + fn from(other: FrameInfo) -> Self { + Self { info: other } + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_copy(frame: &wasm_frame_t) -> Box { + Box::new(frame.clone()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_delete(_frame: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_instance(frame: &wasm_frame_t) -> *const wasm_instance_t { + //todo!("wasm_frame_instance") + std::ptr::null() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_func_index(frame: &wasm_frame_t) -> u32 { + frame.info.func_index() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_func_offset(frame: &wasm_frame_t) -> usize { + frame.info.func_offset() +} + +#[no_mangle] +pub unsafe extern "C" fn wasm_frame_module_offset(frame: &wasm_frame_t) -> usize { + frame.info.module_offset() +} wasm_declare_vec!(frame); diff --git a/lib/c-api/tests/CMakeLists.txt b/lib/c-api/tests/CMakeLists.txt index 339eaf53f90..24a135baddb 100644 --- a/lib/c-api/tests/CMakeLists.txt +++ b/lib/c-api/tests/CMakeLists.txt @@ -31,11 +31,11 @@ add_executable(wasm-c-api-serialize wasm-c-api/example/serialize.c) #add_executable(wasm-c-api-start wasm-c-api/example/start.c) #add_executable(wasm-c-api-table wasm-c-api/example/table.c) #add_executable(wasm-c-api-threads wasm-c-api/example/threads.c) -#add_executable(wasm-c-api-trap wasm-c-api/example/trap.c) +add_executable(wasm-c-api-trap wasm-c-api/example/trap.c) # Custom Wasm C API tests add_executable(wasm-c-api-wasi wasm-c-api-wasi.c) -add_executable(wasm-c-api-trap wasm-c-api-trap.c) +add_executable(wasm-c-api-early-exit wasm-c-api-early-exit.c) if (DEFINED WASI_TESTS) add_executable(test-wasi-import-object test-wasi-import-object.c) @@ -238,19 +238,19 @@ add_test(NAME wasm-c-api-serialize # WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/example/ #) -#target_link_libraries(wasm-c-api-trap general ${WASMER_LIB}) -#target_compile_options(wasm-c-api-trap PRIVATE ${COMPILER_OPTIONS}) -#add_test(NAME wasm-c-api-trap -# COMMAND wasm-c-api-trap -# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/example/ -#) +target_link_libraries(wasm-c-api-trap general ${WASMER_LIB}) +target_compile_options(wasm-c-api-trap PRIVATE ${COMPILER_OPTIONS}) +add_test(NAME wasm-c-api-trap + COMMAND wasm-c-api-trap + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/example/ +) set_property(TARGET wasm-c-api-wasi PROPERTY C_STANDARD 11) target_link_libraries(wasm-c-api-wasi general ${WASMER_LIB}) target_compile_options(wasm-c-api-wasi PRIVATE ${COMPILER_OPTIONS}) add_test(wasm-c-api-wasi wasm-c-api-wasi) -set_property(TARGET wasm-c-api-trap PROPERTY C_STANDARD 11) -target_link_libraries(wasm-c-api-trap general ${WASMER_LIB}) -target_compile_options(wasm-c-api-trap PRIVATE ${COMPILER_OPTIONS}) -add_test(wasm-c-api-trap wasm-c-api-trap) +set_property(TARGET wasm-c-api-early-exit PROPERTY C_STANDARD 11) +target_link_libraries(wasm-c-api-early-exit general ${WASMER_LIB}) +target_compile_options(wasm-c-api-early-exit PRIVATE ${COMPILER_OPTIONS}) +add_test(wasm-c-api-early-exit wasm-c-api-early-exit) diff --git a/lib/c-api/tests/wasm-c-api-trap.c b/lib/c-api/tests/wasm-c-api-early-exit.c similarity index 55% rename from lib/c-api/tests/wasm-c-api-trap.c rename to lib/c-api/tests/wasm-c-api-early-exit.c index b2a777c50a7..c20f0d29929 100644 --- a/lib/c-api/tests/wasm-c-api-trap.c +++ b/lib/c-api/tests/wasm-c-api-early-exit.c @@ -9,21 +9,21 @@ // Use the last_error API to retrieve error messages void print_wasmer_error() { - int error_len = wasmer_last_error_length(); - printf("Error len: `%d`\n", error_len); - char *error_str = malloc(error_len); - wasmer_last_error_message(error_str, error_len); - printf("Error str: `%s`\n", error_str); + int error_len = wasmer_last_error_length(); + printf("Error len: `%d`\n", error_len); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); } wasm_store_t* store = NULL; -wasm_trap_t* early_exit(wasm_val_t args[], wasm_val_t results[]) { - wasm_message_t* trap_message = NULL; - const char* message_inner = "trapping from a host import"; - wasm_byte_vec_new_uninitialized(trap_message, strlen(message_inner)); - // TODO: should we free this data? - return wasm_trap_new(store, trap_message); +own wasm_trap_t* early_exit(const wasm_val_t args[], wasm_val_t results[]) { + own wasm_message_t trap_message; + wasm_name_new_from_string(&trap_message,"trapping from a host import"); + own wasm_trap_t* trap = wasm_trap_new(store, &trap_message); + wasm_name_delete(&trap_message); + return trap; } int main(int argc, const char* argv[]) { @@ -63,23 +63,22 @@ int main(int argc, const char* argv[]) { // Instantiate. printf("Instantiating module...\n"); - // TODO: fill imports - wasm_functype_t* host_func_type = wasm_functype_new_0_0(); - wasm_func_t* host_func = wasm_func_new(store, host_func_type, (wasm_func_callback_t)early_exit); - wasm_extern_t* host_func_as_extern = wasm_func_as_extern(host_func); - wasm_functype_delete(host_func_type); + wasm_func_t* host_func = wasm_func_new(store, host_func_type, early_exit); - wasm_extern_t* imports[] = { host_func_as_extern }; + wasm_functype_delete(host_func_type); + const wasm_extern_t* imports[] = { wasm_func_as_extern(host_func) }; own wasm_instance_t* instance = - wasm_instance_new(store, module, (const wasm_extern_t *const *) imports, NULL); + wasm_instance_new(store, module, imports, NULL); if (!instance) { printf("> Error instantiating module!\n"); print_wasmer_error(); return 1; } + wasm_func_delete(host_func); + // Extract export. printf("Extracting export...\n"); own wasm_extern_vec_t exports; @@ -89,36 +88,69 @@ int main(int argc, const char* argv[]) { return 1; } fprintf(stderr, "found %zu exports\n", exports.size); + + wasm_module_delete(module); + wasm_instance_delete(instance); - // TODO: - wasm_func_t* run_func = NULL; + wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]); if (run_func == NULL) { printf("> Error accessing export!\n"); print_wasmer_error(); return 1; } - wasm_module_delete(module); - wasm_instance_delete(instance); - // Call. printf("Calling export...\n"); - if (wasm_func_call(run_func, NULL, NULL)) { - printf("> Error calling function!\n"); + own const wasm_val_t args[] = { + { + .kind = WASM_I32, + .of = { .i32 = 1 } + }, + { + .kind = WASM_I32, + .of = { .i32 = 7 } + }, + }; + own wasm_val_t rets[1] = { }; + own wasm_trap_t* trap = wasm_func_call(run_func, args, rets); + if (!trap) { + printf("> Error calling function: expected trap!\n"); return 1; } - wasm_extern_vec_delete(&exports); + printf("Printing message...\n"); + own wasm_name_t message; + wasm_trap_message(trap, &message); + printf("> %s\n", message.data); + + /* printf("Printing origin...\n"); + own wasm_frame_t* frame = wasm_trap_origin(trap); + if (frame) { + print_frame(frame); + wasm_frame_delete(frame); + } else { + printf("> Empty origin.\n"); + } - // NEEDS REVIEW: - for(int i = 0; i < num_imports; ++i) { - wasm_extern_delete(imports[i]); + printf("Printing trace...\n"); + own wasm_frame_vec_t trace; + wasm_trap_trace(trap, &trace); + if (trace.size > 0) { + for (size_t i = 0; i < trace.size; ++i) { + print_frame(trace.data[i]); + } + } else { + printf("> Empty trace.\n"); } - free(imports); + + wasm_frame_vec_delete(&trace);*/ + wasm_trap_delete(trap); + wasm_name_delete(&message); + + wasm_extern_vec_delete(&exports); // Shut down. printf("Shutting down...\n"); - wasm_func_delete(run_func); wasm_store_delete(store); wasm_engine_delete(engine); diff --git a/lib/engine/src/trap/frame_info.rs b/lib/engine/src/trap/frame_info.rs index 310dc56796d..464131f1949 100644 --- a/lib/engine/src/trap/frame_info.rs +++ b/lib/engine/src/trap/frame_info.rs @@ -282,7 +282,7 @@ pub fn register( /// each frame is described by this structure. /// /// [`Trap`]: crate::Trap -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FrameInfo { module_name: String, func_index: u32, From d53d58ee4c1f597524a24c978545c4400d230cf2 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 29 Sep 2020 15:43:56 -0700 Subject: [PATCH 4/4] Add examples to tarpaulin coverage --- .tarpaulin.toml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.tarpaulin.toml b/.tarpaulin.toml index 9f7dae3decf..78b59bfb065 100644 --- a/.tarpaulin.toml +++ b/.tarpaulin.toml @@ -1,17 +1,16 @@ [cranelift_coverage] features = "cranelift,singlepass,llvm,test-no-traps,test-cranelift" +examples = ["early-exit", "engine-jit", "engine-native", "engine-headless", "cross-compilation", "compiler-cranelift", "exported-function", "wasi"] release = true [llvm_coverage] features = "cranelift,singlepass,llvm,test-no-traps,test-llvm" +examples = ["compiler-llvm"] release = true [singlepass_coverage] features = "cranelift,singlepass,llvm,test-no-traps,test-singlepass" -release = true - -[feature_a_and_b_coverage] -features = "feature_a feature_b" +examples = ["compiler-singlepass"] release = true [report]