VM Hooks

Everything that runs in Guile runs on its virtual machine, a C program that defines a number of operations that Scheme programs can perform.

Note that there are multiple VM “engines” for Guile. Only some of them have support for hooks compiled in. Normally the deal is that you get hooks if you are running interactively, and otherwise they are disabled, as they do have some overhead (about 10 or 20 percent).

To ensure that you are running with hooks, pass --debug to Guile when running your program, or otherwise use the call-with-vm and set-vm-engine! procedures to ensure that you are running in a VM with the debug engine.

To digress, Guile’s VM has 4 different hooks that can be fired at different times. For implementation reasons, these hooks are not actually implemented with first-class Scheme hooks (see Hooks); they are managed using an ad-hoc interface.

VM hooks are called with one argument: the current frame. See Frames. Since these hooks may be fired very frequently, Guile does a terrible thing: it allocates the frames on the C stack instead of the garbage-collected heap.

The upshot here is that the frames are only valid within the dynamic extent of the call to the hook. If a hook procedure keeps a reference to the frame outside the extent of the hook, bad things will happen.

The interface to hooks is provided by the (system vm vm) module:

(use-modules (system vm vm))

All of these functions implicitly act on the VM for the current thread only.

Scheme Procedure: vm-add-next-hook! f

Arrange to call f when before an instruction is retired (and executed).

Scheme Procedure: vm-add-apply-hook! f

Arrange to call f whenever a procedure is applied. The frame locals will be the callee, followed by the arguments to the call.

Note that procedure application is somewhat orthogonal to continuation pushes and pops. To know whether a call is a tail call or not, with respect to the frame previously in place, check the value of the frame pointer compared the previous frame pointer.

Scheme Procedure: vm-add-return-hook! f

Arrange to call f before returning from a frame. The values in the frame will be the frame’s return values.

Note that it’s possible to return from an “inner” frame: one that was not immediately proceeded by a call with that frame pointer. In that case, it corresponds to a non-local control flow jump, either because of applying a composable continuation or because of restoring a saved undelimited continuation.

Scheme Procedure: vm-add-abort-hook!

Arrange to call f after aborting to a prompt. See Prompts.

Unfortunately, the values passed to the prompt handler are not easily available to f.

Scheme Procedure: vm-remove-next-hook! f
Scheme Procedure: vm-remove-apply-hook! f
Scheme Procedure: vm-remove-return-hook! f
Scheme Procedure: vm-remove-abort-hook! f

Remove f from the corresponding VM hook for the current thread.

These hooks do impose a performance penalty, if they are on. Obviously, the vm-next-hook has quite an impact, performance-wise. Therefore Guile exposes a single, heavy-handed knob to turn hooks on or off, the VM trace level. If the trace level is positive, hooks run; otherwise they don’t.

For convenience, when the VM fires a hook, it does so with the trap level temporarily set to 0. That way the hooks don’t fire while you’re handling a hook. The trace level is restored to whatever it was once the hook procedure finishes.

Scheme Procedure: vm-trace-level

Retrieve the “trace level” of the VM. If positive, the trace hooks associated with vm will be run. The initial trace level is 0.

Scheme Procedure: set-vm-trace-level! level

Set the “trace level” of the VM.

See A Virtual Machine for Guile, for more information on Guile’s virtual machine.