From f3b80ece5f1c75616102512e44a437b423452ef9 Mon Sep 17 00:00:00 2001 From: Stephan Renatus Date: Thu, 15 Jul 2021 17:31:03 +0200 Subject: [PATCH] c-api: add wasmtime_trap_code (#3086) Eventually this should be added to the wasmtime-go binding, addressing https://github.com/bytecodealliance/wasmtime-go/issues/63. Added a snippet to examples/interrupt.c to verify that this works as expected in manual testing. Signed-off-by: Stephan Renatus --- crates/c-api/include/wasmtime/extern.h | 2 +- crates/c-api/include/wasmtime/trap.h | 46 ++++++++++++++++++++++++++ crates/c-api/src/trap.rs | 26 ++++++++++++++- crates/wasmtime/src/trap.rs | 8 +++++ examples/interrupt.c | 4 +++ 5 files changed, 84 insertions(+), 2 deletions(-) diff --git a/crates/c-api/include/wasmtime/extern.h b/crates/c-api/include/wasmtime/extern.h index f3369d6360..77d66c8656 100644 --- a/crates/c-api/include/wasmtime/extern.h +++ b/crates/c-api/include/wasmtime/extern.h @@ -84,7 +84,7 @@ typedef struct wasmtime_global { size_t index; } wasmtime_global_t; -/// \brief Disciminant of #wasmtime_extern_t +/// \brief Discriminant of #wasmtime_extern_t typedef uint8_t wasmtime_extern_kind_t; /// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a diff --git a/crates/c-api/include/wasmtime/trap.h b/crates/c-api/include/wasmtime/trap.h index 2ad0800f0a..909a4801f0 100644 --- a/crates/c-api/include/wasmtime/trap.h +++ b/crates/c-api/include/wasmtime/trap.h @@ -13,6 +13,41 @@ extern "C" { #endif +/** + * \brief Code of an instruction trap. + * + * See #wasmtime_trap_code_enum for possible values. + */ +typedef uint8_t wasmtime_trap_code_t; + +/** + * \brief Trap codes for instruction traps. + */ +enum wasmtime_trap_code_enum { + /// The current stack space was exhausted. + WASMTIME_TRAP_CODE_STACK_OVERFLOW, + /// An out-of-bounds memory access. + WASMTIME_TRAP_CODE_MEMORY_OUT_OF_BOUNDS, + /// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address. + WASMTIME_TRAP_CODE_HEAP_MISALIGNED, + /// An out-of-bounds access to a table. + WASMTIME_TRAP_CODE_TABLE_OUT_OF_BOUNDS, + /// Indirect call to a null table entry. + WASMTIME_TRAP_CODE_INDIRECT_CALL_TO_NULL, + /// Signature mismatch on indirect call. + WASMTIME_TRAP_CODE_BAD_SIGNATURE, + /// An integer arithmetic operation caused an overflow. + WASMTIME_TRAP_CODE_INTEGER_OVERFLOW, + /// An integer division by zero. + WASMTIME_TRAP_CODE_INTEGER_DIVISION_BY_ZERO, + /// Failed float-to-int conversion. + WASMTIME_TRAP_CODE_BAD_CONVERSION_TO_INTEGER, + /// Code that was supposed to have been unreachable was reached. + WASMTIME_TRAP_CODE_UNREACHABLE_CODE_REACHED, + /// Execution has potentially run too long and may be interrupted. + WASMTIME_TRAP_CODE_INTERRUPT, +}; + /** * \brief Creates a new trap. * @@ -23,6 +58,17 @@ extern "C" { */ WASM_API_EXTERN wasm_trap_t *wasmtime_trap_new(const char *msg, size_t msg_len); +/** + * \brief Attempts to extract the trap code from this trap. + * + * Returns `true` if the trap is an instruction trap triggered while + * executing Wasm. If `true` is returned then the trap code is returned + * through the `code` pointer. If `false` is returned then this is not + * an instruction trap -- traps can also be created using wasm_trap_new, + * or occur with WASI modules exiting with a certain exit code. + */ +WASM_API_EXTERN bool wasmtime_trap_code(const wasm_trap_t*, wasmtime_trap_code_t *code); + /** * \brief Attempts to extract a WASI-specific exit status from this trap. * diff --git a/crates/c-api/src/trap.rs b/crates/c-api/src/trap.rs index 5bd5256fef..941ec465aa 100644 --- a/crates/c-api/src/trap.rs +++ b/crates/c-api/src/trap.rs @@ -1,6 +1,6 @@ use crate::{wasm_frame_vec_t, wasm_instance_t, wasm_name_t, wasm_store_t}; use once_cell::unsync::OnceCell; -use wasmtime::Trap; +use wasmtime::{Trap, TrapCode}; #[repr(C)] #[derive(Clone)] @@ -91,6 +91,30 @@ pub extern "C" fn wasm_trap_trace(raw: &wasm_trap_t, out: &mut wasm_frame_vec_t) out.set_buffer(vec); } +#[no_mangle] +pub extern "C" fn wasmtime_trap_code(raw: &wasm_trap_t, code: &mut i32) -> bool { + match raw.trap.trap_code() { + Some(c) => { + *code = match c { + TrapCode::StackOverflow => 0, + TrapCode::MemoryOutOfBounds => 1, + TrapCode::HeapMisaligned => 2, + TrapCode::TableOutOfBounds => 3, + TrapCode::IndirectCallToNull => 4, + TrapCode::BadSignature => 5, + TrapCode::IntegerOverflow => 6, + TrapCode::IntegerDivisionByZero => 7, + TrapCode::BadConversionToInteger => 8, + TrapCode::UnreachableCodeReached => 9, + TrapCode::Interrupt => 10, + _ => unreachable!(), + }; + true + } + None => false, + } +} + #[no_mangle] pub extern "C" fn wasmtime_trap_exit_status(raw: &wasm_trap_t, status: &mut i32) -> bool { match raw.trap.i32_exit_status() { diff --git a/crates/wasmtime/src/trap.rs b/crates/wasmtime/src/trap.rs index 790f592a78..f248734215 100644 --- a/crates/wasmtime/src/trap.rs +++ b/crates/wasmtime/src/trap.rs @@ -42,6 +42,14 @@ impl fmt::Display for TrapReason { /// A trap code describing the reason for a trap. /// /// All trap instructions have an explicit trap code. +/// +/// The code can be accessed from the c-api, where the possible values are translated +/// into enum values defined there: +/// +/// * `wasm_trap_code` in c-api/src/trap.rs, and +/// * `wasmtime_trap_code_enum` in c-api/include/wasmtime/trap.h. +/// +/// These need to be kept in sync. #[non_exhaustive] #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub enum TrapCode { diff --git a/examples/interrupt.c b/examples/interrupt.c index 41f25d7f93..b4ad55e377 100644 --- a/examples/interrupt.c +++ b/examples/interrupt.c @@ -118,6 +118,10 @@ int main() { printf("Got a trap!...\n"); // `trap` can be inspected here to see the trap message has an interrupt in it + wasmtime_trap_code_t code; + ok = wasmtime_trap_code(trap, &code); + assert(ok); + assert(code == WASMTIME_TRAP_CODE_INTERRUPT); wasm_trap_delete(trap); wasmtime_store_delete(store);