Skip to content

Trap Handling #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
clearloop opened this issue Nov 27, 2020 · 5 comments
Closed

Trap Handling #1

clearloop opened this issue Nov 27, 2020 · 5 comments

Comments

@clearloop
Copy link
Collaborator

clearloop commented Nov 27, 2020

Traps

Find a solution to handle traps in wasm execution

Keywords

  • frame_info
  • look_up_frame_info
  • look_up_trap_info

Issues

Under some conditions, certain instructions may produce a trap, which immediately aborts execution. Traps cannot be handled by WebAssembly code, but are reported to the outside environment, where they typically can be caught.

#Traps concept in WebAssembly Overview

Administrative Instructions in WebAssembly Execution

In order to express the reduction of traps, calls, and control instructions, the syntax of instructions is extended to include the following administrative instructions:

instr  ::=  ...
        |   trap
        |   invoke funcaddr
        |   init_elem tabbleaddr u32 funcidx*
        |   init_data memaddr u32 byte*
        |   labe𝑛 { instr* } instr* end
        |   frame𝑛 { frame } instr* end

The trap instruction represents the occurrence of a trap. Traps are bubbled up through nested instruction sequences, ultimately reducing the entire program to a single 𝗍𝗋𝖺𝗉 instruction, signaling abrupt termination.

#Administrative Instructions

Runtime Result

A result is the outcome of a computation. It is either a sequence of values or a trap.

result  ::=  val*
             trap

#Trap as results in runtime

@clearloop
Copy link
Collaborator Author

clearloop commented Nov 27, 2020

Here is an example of handling or not

Rust Code

#[no_mangle]
pub fn _start() {
  panic!("Hello, world!");
}

Results

wasmtime

Error: failed to run main module `target/wasm32-unknown-unknown/debug/bt.wasm`

Caused by:
    0: failed to invoke command default
    1: wasm trap: unreachable
       wasm backtrace:
         0: 0x1cda - <unknown>!__rust_start_panic
         1: 0x1cce - <unknown>!rust_panic
         2: 0x1c9e - <unknown>!std::panicking::rust_panic_with_hook::hc5713da015ebaa19
         3:  0x26e - <unknown>!std::panicking::begin_panic::{{closure}}::h8e62ab0ea555186f
         4:  0xfba - <unknown>!std::sys_common::backtrace::__rust_end_short_backtrace::h34a944558df1326a
         5:  0x15c - <unknown>!std::panicking::begin_panic::h03c636dac2b8fb70
         6: 0x1a65 - <unknown>!_start

wasmer

error: failed to run `target/wasm32-unknown-unknown/debug/bt.wasm`
│   1: RuntimeError: unreachable
           at __rust_start_panic (bt.wasm[67]:0x1cda)
           at rust_panic (bt.wasm[66]:0x1cce)
           at std::panicking::rust_panic_with_hook::hc5713da015ebaa19 (bt.wasm[65]:0x1c9e)
           at std::panicking::begin_panic::{{closure}}::h8e62ab0ea555186f (bt.wasm[2]:0x26e)
           at std::sys_common::backtrace::__rust_end_short_backtrace::h34a944558df1326a (bt.wasm[36]:0xfba)
           at std::panicking::begin_panic::h03c636dac2b8fb70 (bt.wasm[0]:0x15c)
           at _start (bt.wasm[55]:0x1a65)
╰─> 2: unreachable

wavm

Runtime exception: wavm.reachedUnreachable
Call stack:
  host!/Users/mercury/.wasmer/bin/wavm!unreachableTrap(WAVM::Runtime::ContextRuntimeData*)+41
  wasm!target/wasm32-unknown-unknown/debug/bt.wasm!__rust_start_panic+0
  wasm!target/wasm32-unknown-unknown/debug/bt.wasm!rust_panic+14
  wasm!target/wasm32-unknown-unknown/debug/bt.wasm!_ZN3std9panicking20rust_panic_with_hook17hc5713da015ebaa19E+109
  wasm!target/wasm32-unknown-unknown/debug/bt.wasm!_ZN3std9panicking11begin_panic28_$u7b$$u7b$closure$u7d$$u7d$17h8e62ab0ea555186fE+65
  wasm!target/wasm32-unknown-unknown/debug/bt.wasm!_ZN3std10sys_common9backtrace26__rust_end_short_backtrace17h34a944558df1326aE+41
  wasm!target/wasm32-unknown-unknown/debug/bt.wasm!_ZN3std9panicking11begin_panic17h03c636dac2b8fb70E+34
  wasm!target/wasm32-unknown-unknown/debug/bt.wasm!_start+13
  thnk!C to WASM thunk!()->()+0
  host!/Users/mercury/.wasmer/bin/wavm!WAVM::Platform::catchSignals(void (*)(void*), bool (*)(void*, WAVM::Platform::Signal, WAVM::Platform::CallStack&&), void*)+151
  host!/Users/mercury/.wasmer/bin/wavm!WAVM::Runtime::unwindSignalsAsExceptions(std::__1::function<void ()> const&)+87
  host!/Users/mercury/.wasmer/bin/wavm!WAVM::Runtime::invokeFunction(WAVM::Runtime::Context*, WAVM::Runtime::Function const*, WAVM::IR::FunctionType, WAVM::IR::UntaggedValue const*, WAVM::IR::UntaggedValue*)+317
  host!/Users/mercury/.wasmer/bin/wavm!State::execute(WAVM::IR::Module const&, WAVM::Runtime::Instance*)+1393
  host!/Users/mercury/.wasmer/bin/wavm!State::run(char**)+8474
  host!/Users/mercury/.wasmer/bin/wavm!std::__1::__function::__func<State::runAndCatchRuntimeExceptions(char**)::'lambda'(), std::__1::allocator<State::runAndCatchRuntimeExceptions(char**)::'lambda'()>, void ()>::operator()()+21
  <2 redundant frames omitted>
  host!/Users/mercury/.wasmer/bin/wavm!WAVM::Runtime::catchRuntimeExceptions(std::__1::function<void ()> const&, std::__1::function<void (WAVM::Runtime::Exception*)> const&)+18
  host!/Users/mercury/.wasmer/bin/wavm!State::runAndCatchRuntimeExceptions(char**)+104
  host!/Users/mercury/.wasmer/bin/wavm!main+1290

zsh: abort      wavm run --abi=emscripten --function=_start

wasm3

Error: [Fatal] repl_call: [trap] unreachable executed
Error: [trap] unreachable executed ()

wasm3 log config

wasmi

thread 'main' panicked at ': Trap(Trap { kind: Unreachable })', src/bin/instantiate.rs:87:14
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

@clearloop
Copy link
Collaborator Author

clearloop commented Nov 30, 2020

The panic trace is stored in the name section, which is listed in the appendix of WebAssembly Specification.

parity-wasm

parity-wasm provides a parse_names function for parsing name_section in struct Module

Corresponding custom section with proper header will convert to name sections
If some of them will fail to be decoded, Err variant is returned with the list of
(index, Error) tuples of failed sections.

https://github.com/paritytech/parity-wasm/blob/5b56959a1ef3b7841352fd59a5cfb1c474dc1ff0/src/elements/module.rs#L405-L434

This function can generate the target name section but need to be filtered.

wasmer

wasmer builds RuntimeErrorInner with TrapCode and program counter,

struct RuntimeErrorInner {
    /// The source error (this can be a custom user `Error` or a [`TrapCode`])
    source: RuntimeErrorSource,
    /// The reconstructed Wasm trace (from the native trace and the `GlobalFrameInfo`).
    wasm_trace: Vec<FrameInfo>,
    /// The native backtrace
    native_trace: Backtrace,
}

impl RuntimeErrorInner {
    fn from_trap(trap: Tap) -> Self {
        // ...
    }
}

enum Trap {
    // ...
    /// A trap raised from machine code generated from Wasm
    Wasm {
        /// The program counter in generated code where this trap happened.
        pc: usize,
        /// Native stack backtrace at the time the trap occurred
        backtrace: Backtrace,
        /// Optional trapcode associated to the signal that caused the trap
        signal_trap: Option<TrapCode>,
    }, 
}

wasmtime

For wasmtime, there is a FrameInfo generated along with FunctionInfo from program counter

/// Information about trap.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct TrapInformation {
    /// The offset of the trapping instruction in native code.
    /// It is relative to the beginning of the function.
    pub code_offset: binemit::CodeOffset,
    /// Code of the trap.
    pub trap_code: ir::TrapCode,
}

struct TrapInner {
    reason: TrapReason,
    wasm_trace: Vec<FrameInfo>,
    native_trace: Backtrace,
}

/// Creates a new `Trap`.
///
/// * `store` - this is optionally provided, if available. If `None` we'll
///   look up the last store, if available, used to call wasm code on the
///   stack.
///
/// * `trap_pc` - this is the precise program counter, if available, that
///   wasm trapped at. This is used when learning about the wasm stack trace
///   to ensure we assign the correct source to every frame.
///
/// * `reason` - this is the wasmtime-internal reason for why this trap is
///   being created.
///
/// * `native_trace` - this is a captured backtrace from when the trap
///   occurred, and this will iterate over the frames to find frames that
///   lie in wasm jit code.
fn new_with_trace(
    store: Option<&Store>,
    trap_pc: Option<usize>,
    reason: TrapReason,
    native_trace: Backtrace,
) -> Self {}

@clearloop
Copy link
Collaborator Author

clearloop commented Dec 1, 2020

So what is the program counter? How to get it?

/// The inner `u32` points to what?
struct Func(u32);

Function Name

let fn_name = module.function_names.get(&fn_index);

@clearloop
Copy link
Collaborator Author

clearloop commented Dec 8, 2020

Prints functions in Call Stack (wasmi)

[
    FuncRef(
        Internal { signature=Signature { params: [], return_type: None } },
    ),
    FuncRef(
        Internal { signature=Signature { params: [I32, I32, I32], return_type: None } },
    ),
    FuncRef(
        Internal { signature=Signature { params: [I32], return_type: None } },
    ),
    FuncRef(
        Internal { signature=Signature { params: [I32], return_type: None } },
    ),
    FuncRef(
        Internal { signature=Signature { params: [I32, I32, I32, I32], return_type: None } },
    ),
    FuncRef(
        Internal { signature=Signature { params: [I32, I32], return_type: None } },
    ),
]

Comparing to the ideal outputs

Error: failed to run main module `panic.wasm`

Caused by:
    0: failed to invoke command default
    1: wasm trap: unreachable
       wasm backtrace:
         0: 0x3a93 - <unknown>!__rust_start_panic
         1: 0x3a87 - <unknown>!rust_panic
         2: 0x3a57 - <unknown>!std::panicking::rust_panic_with_hook::h7ba07724d623fbd6
         3: 0x17eb - <unknown>!std::panicking::begin_panic::{{closure}}::haa116d4044ff002f
         4: 0x1cf6 - <unknown>!std::sys_common::backtrace::__rust_end_short_backtrace::h4273a622fa86868a
         5: 0x16d9 - <unknown>!std::panicking::begin_panic::hc8f5829adc10d800
         6: 0x1678 - <unknown>!_start

It's fine.

[
    "rust_panic",
    "_ZN3std9panicking20rust_panic_with_hook17hc5713da015ebaa19E",
    "_ZN3std9panicking11begin_panic28_$u7b$$u7b$closure$u7d$$u7d$17he51f9abd5f6c28d2E",
    "_ZN3std10sys_common9backtrace26__rust_end_short_backtrace17hc867af7cfda96dafE",
    "_ZN3std9panicking11begin_panic17h0a859a469eb06beaE",
    "_ZN5panic23panic_in_wasm_backtrace17ha67ca05cd299040fE",
    "_start",
]

Try to decode with https://github.com/alexcrichton/rustc-demangle

@clearloop
Copy link
Collaborator Author

[
    rust_panic,
    std::panicking::rust_panic_with_hook,
    std::panicking::begin_panic::{{closure}},
    std::sys_common::backtrace::__rust_end_short_backtrace,
    std::panicking::begin_panic,
    panic::panic_in_wasm_backtrace,
    _start,
]
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Trap(Trap { kind: Unreachable })', src/bin/instantiate.rs:84:6
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Done, patractlabs/wasmi@80bb84b.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant