29. Lua scripting¶
The debugger can be controlled and extended using the Lua language. Scripting allows you to write programs that execute inside the debugger, from simple batch-like scripts to advanced plug-ins that add custom commands, listen to internal events, and control execution.
From a high-level perspective, Lua and the debugger enable various tasks:
Perform setup tasks to prepare your debug session
Simplify repetitive tasks
Run fully automated tests
Extend functionality; for instance, to introspect the state and behavior of an operating system or protocol stack. These commands integrate into the existing set and share a command-line interface.
You can choose any automation level, from running the entire session under script control to providing high-level functionality with commands.
29.1. Running a script¶
To get started, this simple Lua program just displays the registers:
local db = require('db')
print(db.consoleCommand('info registers'))
If saved in script-name.lua (in the current directory), you can run
the script with:
(db) source script-name.lua
Debugger functionality is provided in the db table which is
created before the script is started. To access it, you need to use
require. Here we store the debugger table in the local variable
db.
Note
Use the --eval-command (can also be used as -e) to invoke a
Lua script from the command line and to make it execute automatically
when the debugger starts. The --eval-command works with any
command and can be specified multiple times on the command line.
29.2. Lua environments¶
Each time you run a Lua script, a new fresh Lua state is created. This means you will start out with the standard Lua and debugger libraries available imported, but not anything else. If you create a global variable, it will not normally be present the next time you run the script.
However, if a script provides new debugger functions (e.g., console commands), the Lua state is saved with the command. Subsequent invocations of that command reuse the same state, retaining global variables and closures. This allows commands to operate within their established state and depend on prior actions within that state, including previous uses of the same or other commands created by the script.
A Lua plug-in typically registers functions with the debugger for specific circumstances. These functions always execute in the state in which they were installed.
It is good practice to provide related functionality (e.g., a command set and plug-in behavior) in its own script. Multiple plug-ins can be installed via source invocation, allowing each to have its private shared state while co-existing separately.
29.3. Command interpreters¶
The debugger provides two sets of command interpreters, console commands and MI commands.
Console commands are intended for humans, provide command completion when being entered and formatted output that is easy to read.
The Machine Interface (or MI for short), was introduced by GDB to provide a more precise and stable command protocol, intended for (graphical) debugger front-ends. The GDB MI command set is supported by the debugger, allowing existing graphical debugger front-ends to be used.
Many commands can be accessed from either of these command sets, while some commands are only available in one of them.
The Lua interface allows both command sets to be used. In addition, adapted MI commands and additional functionality are also provided.
29.4. Console commands¶
The db table allows sending console commands to the debugger.
These commands execute as if typed at the terminal, and their string
results are passed back to Lua.
If you need to process the result beyond displaying or ignoring it, parsing is required. However, writing such parsers quickly becomes tedious, as the text output is primarily for display.
29.5. MI commands¶
MI commands offer precise interaction for debugger front-ends, making them well-suited for Lua scripts. Although commands are string-based, results are converted to nested Lua tables with basic types (booleans, numbers, strings).
This significantly simplifies interpreting results from Lua-to-debugger calls.
29.6. Adapted MI commands¶
Adapted MI commands are basically MI commands converted to be a more natural fit for Lua. This is done in two ways. The actual commands take arguments like any ordinary function in Lua. A return values is the value returned or a failure, instead of the MI style result table hierarchy.
29.7. Choosing a command set¶
In most cases you will probably want to use the adapted MI command set as it provides the most natural Lua experience.
The console and MI command set may be useful if you have familiarity with either or prefer the alternative output form provided.
29.8. Arrays and Lua¶
Lua array indices start at 1, unlike C. This mismatch has implications when working with debugger C expressions from Lua.
To simplify working with C expressions, array results can start at 0 in Lua, mirroring C behavior. However, the ipairs iterator (designed for Lua’s 1-based indexing) will skip the element at index 0.
Shifting the index in expressions is an alternative, but it would likely be more confusing and error-prone, especially given the context of C expressions.
29.9. Debugger API¶
-
db:stepInstruction() Single step once at instruction level. This function will quickly return an indication of whether it was successful in starting the target. Actual progress of execution will be posted as notifications
Corresponding MI instruction is
-exec-step-instrucion.