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.