Previous: Interpreter, Up: Toolchain reference [Contents][Index]
The (hoot repl)
module provides a set of REPL commands to
assist with inspecting and debugging Wasm modules. As a matter of
course, Hoot’s Scheme compiler should not cause low-level Wasm
runtime errors, but when it does, or when working with the Wasm
toolchain directly, these REPL tools may provide some assistance.
To install the REPL commands, simply import the module:
scheme@(guile-user)> ,use (hoot repl)
To see a list of all the Wasm commands, run:
scheme@(guile-user)> ,help wasm
To demonstrate the debugging features, let’s create a trivial module with a buggy function:
scheme@(guile-user)> (define src '(module (func (export "main") (param $x i32) (result i32) (i32.add (local.get $x) (unreachable)))))
When called, this function will hit the unreachable
instruction
and throw a runtime error. Let’s compile the WAT source, load it into
the VM, and get a reference to the main
function:
scheme@(guile-user)> ,use (wasm resolve) (wasm vm) (wasm wat) scheme@(guile-user)> (define wasm (validate-wasm (resolve-wasm (wat->wasm src)))) scheme@(guile-user)> (define instance (instantiate-wasm wasm)) scheme@(guile-user)> (define main (wasm-instance-export-ref instance "main"))
To trap the Wasm runtime error and open a Wasm debugging REPL, the
wasm-catch
REPL command can be prefixed before an
expression:
scheme@(guile-user)> ,wasm-catch (main 7) ice-9/boot-9.scm:1674:22: In procedure raise-exception: ERROR: 1. &wasm-runtime-error: instruction: (unreachable) position: (func 0 1) instance: #<wasm-instance 140506559041920> stack: #<<wasm-stack> items: (7)> blocks: ((wasm-block)) locals: #(7) 2. &message: "Wasm runtime error: unreachable" 3. &irritants: () Entering Wasm debug prompt. Type `,help wasm' for info or `,q' to continue. scheme@(guile-user) [1]>
Once in a Wasm debug context, many of the other REPL commands become
usable. To highlight the instruction where execution has paused, use
wasm-pos
:
scheme@(guile-user) [1]> ,wasm-pos (func 0 (param $x i32) (result i32) (local.get 0) <<< (unreachable) >>> (i32.add))
To print the contents of the values stack, use wasm-stack
:
scheme@(guile-user) [1]> ,wasm-stack Value stack: 0: 7
To print the contents of the function locals, use wasm-locals
:
scheme@(guile-user) [1]> ,wasm-locals Locals: 0: 7
To evaluate arbitary Wasm instructions in the current context, either
in an attempt to repair interpreter state or just for fun, use
wasm-eval
:
scheme@(guile-user) [1]> ,wasm-eval '(local.get 0) scheme@(guile-user) [1]> ,wasm-stack Value stack: 0: 7 1: 7
There are now two i32 values on the stack. If we were to proceed with
execution, the next instruction, i32.add
, should add them
together and return a result of 14. To resume execution, use
wasm-continue
:
scheme@(guile-user) [1]> ,wasm-continue $5 = 14
Evaluating arbitrary Wasm commands in a debugging context is very helpful when trying to understand the nature of a bug, but bear in mind that cursed things may happen during the process as there is no validation applied. This goes especially for when you try to resume execution.
See Interpreter for detailed information on running Wasm within Guile and Toolchain reference in general for working with Wasm directly.
Evaluate exp with verbose Wasm tracing enabled. This will print out every instruction along with the state of the value stack and function locals at the time of evaluation.
Evaluate exp and print out a table showing how many times each kind of Wasm instruction was executed as well as a total instruction count.
Catch and debug Wasm runtime errors that are raised by evaluating exp.
The following commands are usable only in the context of a Wasm debug REPL:
Print the state of the Wasm stack.
Print the state of the Wasm function locals.
Print the current function disassembly and highlight the instruction where Wasm execution has paused.
Evaluate the Wasm instruction instr in the current debug
context. Use this when attempting to fix the state of the Wasm stack
or locals before attempting to resume with ,wasm-continue
.
The following commands behave differently depending on if they are run within a Wasm debug REPL or not.
Display information about wasm, or the current Wasm instance when debugging.
When in a debugger, exit and resume Wasm execution. In the event that
this is run after trapping a runtime error, your warranty is void and
all bets are off! While it may be dangerous, this does allow one to
manually fix the Wasm interpreter state manually with
,wasm-eval
and attempt to proceed, which can come in handy
sometimes.
When not in a debugger, set the Wasm execution mode to continue without interruption. In other words, deactive the instruction stepper if it is active.
When in a debugger, resume Wasm execution but pause before the next instruction is evaluated.
When not in a debugger, set Wasm execution to pause before each instruction is evaluated.
Previous: Interpreter, Up: Toolchain reference [Contents][Index]