General usage¶
Using the facilities provided by luaexts is usually done at the debugger’s command prompt by entering extension commands, or by writing Lua scripts and running them. This section will discuss the commands that drive these interactions.
The !lua
command¶
The primary interface point for using luaexts is the !lua
command, which consumes the rest of
the command line as a chunk of Lua code, and simply runs it. Quotation marks surrounding the Lua
code are not required, as the dbgeng command parser will automatically consume all text until the
next semicolon, which is the debugger’s command-separation marker. If a semicolon is needed within
the Lua code itself, then enclosing quotation marks are required, to prevent the dbgeng command
parser from interpreting the chunk as multiple dbgeng commands. Fortunately, semicolons as
statement-termination markers are optional in Lua syntax, so they are needed very rarely.
Each !lua
command runs as a separate Lua chunk. This means that variables declared as local
will be in scope only during the execution of one command. Variables assigned to, but not declared
as local
, will become global variables, available across all subsequent chunks run by luaexts.
This can be useful, but care must be taken, because the memory being used by data structures that
are pointed to by global variables cannot be freed by the Lua garbage collector as long as they are
alive. Assigning nil
to a global variable will remove the reference to the data the variable was
pointing to, at which point the data will become a candidate for garbage collection.
Lua’s print()
function, which normally outputs to stdout, has been replaced by a function that
directs output to the debugger command window. Another slight change is that usually Lua’s
print()
will append a newline, but the print()
provided by luaexts does not append a
newline, in order to provide more control over exact text output. Instead a global say()
function exists that automatically appends a newline.
As an example, here’s how to call the IDebugSymbols::GetSymbolTypeId function, use the return values to call the IDebugSymbols::GetTypeName function, and then print the type name of a variable named foo that exists in the current scope:
!lua local module, typeid = dbgeng.symbols.get_symbol_type_id( 'foo' ) say( dbgeng.symbols.get_type_name( module, typeid ) )
Running scripts: dofile()
¶
Although the !lua
command provides the full expressiveness of Lua, it can be tedious to compose
complicated code chunks at the debugger command line. We can take advantage of built-in Lua
functionality to ease the pain. Lua provides a dofile()
function that takes a file path, loads
the file as a Lua code chunk, and runs it. Like the scoping of !lua
chunks, variables declared
as local
within the file will be scoped to the file’s chunk, while variables assigned to but not
declared local
will become global and available to any subsequent chunks run by luaexts.
Developing a script file can then be easily done by opening the file in your favorite text editor,
making changes, and then running !lua dofile('path\to\script.lua')
from the debugger command
line to test each change. (Protip: In Windbg, the previous command can be retrieved from the command
history by pressing the Up arrow key when keyboard focus is in the command line input field.)
Running registered commands: !luacmd
¶
Extensions written for luaexts can expose their functionality in two ways. First, they can simply
provide named functions to call, either globally scoped or within namespace tables. Second,
extensions can register commands that can then be run using the !luacmd
command.
Registered commands are functions that take advantage of the debugger engine’s built-in command line parser to automatically parse and tokenize a user-entered debugger command into arguments for the function.
As an example, imagine that an extension would like to expose a function for adding two numbers and
printing the result with a label. The simplest way to call such a function using the !lua
command would be like so:
!lua add_and_print( 'three', 2, 1 )
With an appropriate registered command, it could be run like this:
!luacmd add_and_print three 2 1
Although at first glance that might not appear very helpful, it has a couple of benefits. One is that the debugger engine’s command line parser has a fair amount of intelligence, and can perform tasks like automatic expression parsing and evaluation, as well as string tokenization for one-word strings so they can be given without requiring quotation marks.
The other main benefit is that registered commands can be used with user-named debugger aliases. Aliases work by replacing aliased text with substitute text. However, aliases cannot take arguments to insert into the substitute text; it is a simple, straightforward replacement. Since most Lua function calls require a closing parenthesis at the end, it would be impossible to define an alias that can be given just the three function arguments and expand into the first code example above. The best that could be done would be:
as !ap !lua add_and_print(
which would need to be called as:
!ap 'three', 2, 1 )
As !luacmd
does not use a closing parenthesis, a suitable alias for an appropriate registered
command could be defined easily like this:
as !ap !luacmd add_and_print
and then called like this:
!ap three 2 1
As with standard dbgeng extensions, luaexts registered commands can be run in unqualified if there is no ambiguity in the command name, or can be run fully qualified leading with the extension name to resolve ambiguity:
!luacmd add_and_print three 2 1
!luacmd some_ext.add_and_print three 2 1
Finding registered commands: !luahelp
¶
luaexts provides a help mechanism similar to the help mechanism for standard dbgeng extensions.
Executing the !luahelp
command with no arguments will list all registered commands along with a
brief description of the command. The !luahelp
command can also be given a registered command
name, either qualified or unqualified, to display more detailed help for the given command.
Resetting luaexts: !reinitialize
¶
The !reinitialize
command will reset the internal Lua virtual machine, wiping out all global
variables and freeing memory allocated by Lua. It will then reinitialize the extension’s
configuration by rereading the configuration files and reloading all designated Lua extensions.
This command can be very helpful while implementing a Lua extension, to reset luaexts to a pristine state, or simply to reload functions to test changes.