From 80cc4aa4b74b5fd220ef062a0ccb38c6da081a44 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 29 Sep 2020 14:11:39 -0700 Subject: [PATCH] Get early exit example working with C API; implement wasm_trap_t API --- 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 +- 5 files changed, 175 insertions(+), 76 deletions(-) rename lib/c-api/tests/{wasm-c-api-trap.c => wasm-c-api-early-exit.c} (55%) 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,