Debugging Decision Code¶
Warning
If you are embedding into a C++ program, it is very, very recommended that
when you include a C header file, you encapsulate it with an extern
block, like so:
extern "C" {
#include <decision.h>
#include <dsheet.h>
}
Introduction¶
As of the 0.3.0 update, you can debug Decision code through the C API! This means that when you compile Decision code in debug mode (more on this later), you can tell the debugger to tell you when:
A non-execution wire transfers a value from one socket to another, and what the value transfered was.
An execution wire activates.
A node activates.
A node calls a function or subroutine.
A function or subroutine returns.
You can also set breakpoints which, when hit, will pause the debugger.
Node breakpoints are hit when the node is activated.
Wire breakpoints are hit when:
A value is transfered if the wire is a non-execution wire.
The wire is activated if the wire is an execution wire.
Compiling in Debug Mode¶
In order to debug a sheet, you need to compile the sheet in debug mode. This will make a couple of changes to the compilation process:
When generating the bytecode, the code generator will also store debugging information alongside the bytecode, e.g. where a node starts in bytecode, which instructions call functions, etc.
Optimisation will no longer occur. This is because we don’t want to lose debugging information. For example, say there are two consecutive
NOT
instructions in the bytecode, made from twoNot
nodes in the Decision code. Optimisation would usually remove these instructions, but when debugging, we still want to know when those twoNot
nodes activate!
To compile a sheet in debug mode, you need to set the debug
property in a
CompileOptions
structure:
CompileOptions options = DEFAULT_COMPILE_OPTIONS;
options.debug = true;
// This sheet will now be compiled in debug mode.
Sheet *sheet = d_load_file("debug.dc", &options);
Debugging Agendas¶
Agendas describe what you want to happen when certain events occur during the debugging process. In code, they are in the form of function pointers:
-
typedef void (*
OnWireValue
)(Sheet *sheet, Wire wire, DType type, LexData value) Called when a value is transfered over a wire during a debugging session.
-
typedef void (*
OnExecutionWire
)(Sheet *sheet, Wire wire) Called when an execution wire is activated during a debugging session.
-
typedef void (*
OnNodeActivated
)(Sheet *sheet, size_t nodeIndex) Called when a node is activated during a debugging session.
-
typedef void (*
OnCall
)(Sheet *sheet, const NodeDefinition *funcDef, bool isC) Called when a call occurs during a debugging session.
-
typedef void (*
OnReturn
)() Called when a return occurs during a debugging session.
Once you have functions in the above forms, you can then add them to a
DebugAgenda
structure:
DebugAgenda agenda = NO_AGENDA;
agenda.onWireValue = &onWireValue;
agenda.onExecutionWire = &onExecutionWire;
agenda.onNodedActivated = &onNodeActivated;
agenda.onCall = &onCall;
agenda.onReturn = &onReturn;
Breakpoints¶
If you want to set breakpoints, you can specify where you want the breakpoints to be set:
DebugNodeBreakpoint nodeBreaks[] = {
{sheet, 2}, // The 3rd node in the sheet.
{NULL, 0} // Array needs to end with a NULL entry!
};
DebugWireBreakpoint wireBreaks[] = {
{sheet, {{2, 2}, {3, 1}}}, // The wire that goes from the 3rd socket of
// the 3rd node to the 2nd socket of the 4th
// node.
{NULL, {{0, 0}, {0, 0}}} // Array needs to end with a NULL entry!
};
// Don't forget to add them to the agenda!
agenda.nodeBreakpoints = nodeBreaks;
agenda.wireBreakpoints = wireBreaks;
Then, you can tell the debugger to let you know when the breakpoints are hit with the given function pointers:
-
typedef void (*
OnNodeBreakpoint
)(Sheet *sheet, size_t nodeIndex) Called when a node breakpoint is hit.
Then you can add them to the agenda:
agenda.onNodeBreakpoint = &onNodeBreakpoint;
agenda.onWireBreakpoint = &onWireBreakpoint;
Debugging Sessions¶
To create a debugging session, use:
-
DebugSession
d_debug_create_session
(struct _sheet *sheet, DebugAgenda agenda) Create a debugging session.
- Return
A debugging session in it’s starting state.
- Parameters
sheet
: The sheet to debug.agenda
: The agenda the session should use.
You can then start/continue a debugging session with:
-
bool
d_debug_continue_session
(DebugSession *session) Continue a debugging session until either a breakpoint is hit, or the VM halts.
- Return
True if the debugger hit a breakpoint, false if the VM halted.
- Parameters
session
: The session to continue.
Tip
You can put d_debug_continue_session
as the condition of a while loop!
This way, the session will continue until the sheet exits, and you can
process something after each breakpoint hit!
Once you are done debugging, you should use:
-
void
d_debug_stop_session
(DebugSession *session) Stop a debugging session, freeing all of the memory malloc’d by the session. The session should not be used afterwards.
- Parameters
session
: The session to stop.
Partial Example¶
This example just prints out when nodes get activated.
#include <ddebug.h>
#include <decision.h>
#include <dsheet.h>
#include <stdio.h>
void onNodeActivated(Sheet *sheet, size_t nodeIndex) {
Node node = sheet->graph.nodes[nodeIndex];
const NodeDefinition *nodeDef = node.definition;
printf("Debug> Node %s on line %zu in sheet %s has been activated!\n",
nodeDef->name, node.lineNum, sheet->filePath);
}
int main() {
// Load the sheet in debug mode.
CompileOptions options = DEFAULT_COMPILE_OPTIONS;
options.debug = true;
Sheet *sheet = d_load_file("debug.dc", &options);
// Set up the debugging agenda.
DebugAgenda agenda = NO_AGENDA;
agenda.onNodeActivated = &onNodeActivated;
// Create the session.
DebugSession session = d_debug_create_session(sheet, agenda);
// Keep continuing the session until the sheet exits.
while (d_debug_continue_session(&session)) {
printf("Debug> A breakpoint was hit!\n");
}
// Once we're done, stop the session.
d_debug_stop_session(&session);
// Free the sheet.
d_sheet_free(sheet);
return 0;
}
To see a full example that uses all of the capabilities of the debugger, feel
free to look at tests/c/debugging.c
!