Register with the Debugger

Hooking up a debugging interface is as simple as providing closures to the Engine’s built-in debugger via Engine::register_debugger.

use rhai::debugger::{ASTNode, DebuggerCommand};

let mut engine = Engine::new();

engine.register_debugger(
    // Provide a callback to initialize the debugger state
    |engine, mut debugger| {
        debugger.set_state(...);
        debugger
    },
    // Provide a callback for each debugging step
    |context, event, node, source, pos| {
        ...

        DebuggerCommand::StepOver
    }
);

Tip: Accessing the Debugger

The type debugger::Debugger allows for manipulating break-points, among others.

The Engine’s debugger instance can be accessed via context.global_runtime_state().debugger() (immutable) or context.global_runtime_state_mut().debugger_mut() (mutable).

Callback Functions Signature

There are two callback functions to register for the debugger.

The first is simply a function to initialize the state of the debugger with the following signature.

Fn(&Engine, debugger::Debugger) -> debugger::Debugger

The second callback is a function which will be called by the debugger during each step, with the following signature.

Fn(context: EvalContext, event: debugger::DebuggerEvent, node: ASTNode, source: &str, pos: Position) -> Result<debugger::DebuggerCommand, Box<EvalAltResult>>

where:

ParameterTypeDescription
contextEvalContextthe current evaluation context
eventDebuggerEventan enum indicating the event that triggered the debugger
nodeASTNodean enum with two variants: Expr or Stmt, corresponding to the current expression node or statement node in the AST
source&strthe source of the current AST, or empty if none
posPositionposition of the current node, same as node.position()

and EvalContext is a type that encapsulates the current evaluation context.

Event

The event parameter of the second closure passed to Engine::register_debugger contains a debugger::DebuggerEvent which is an enum with the following variants.

DebuggerEvent variantDescription
Startthe debugger is triggered at the beginning of evaluation
Stepthe debugger is triggered at the next step of evaluation
BreakPoint(n)the debugger is triggered by the n-th break-point
FunctionExitWithValue(r)the debugger is triggered by a function call returning with value r which is &Dynamic
FunctionExitWithError(err)the debugger is triggered by a function call exiting with error err which is &EvalAltResult
Endthe debugger is triggered at the end of evaluation

Return value

Tip: Initialization

When a script starts evaluation, the debugger always stops at the very first AST node with the event parameter set to DebuggerStatus::Start.

This allows initialization to be done (e.g. setting up break-points).

The second closure passed to Engine::register_debugger will be called when stepping into or over expressions and statements, or when break-points are hit.

The return type of the closure is Result<debugger::DebuggerCommand, Box<EvalAltResult>>.

If an error is returned, the script evaluation at that particular instance returns with that particular error. It is thus possible to abort the script evaluation by returning an error that is not catchable, such as EvalAltResult::ErrorTerminated.

If no error is returned, then the return debugger::DebuggerCommand variant determines the continued behavior of the debugger.

DebuggerCommand variantBehaviorgdb equivalent
Continuecontinue with normal script evaluationcontinue
StepIntorun to the next expression or statement, diving into functionsstep
StepOverrun to the next expression or statement, skipping over functions
Nextrun to the next statement, skipping over functionsnext
FunctionExitrun to the end of the current function call; debugger is triggered before the function call returns and the Scope clearedfinish