26f6aa9e3b
When writing the Windows dbgeng driver for Dexter, I couldn't work out why it would either launch a process and leave it free running, or if I started the process suspended, never do anything with it. The result was a hack to create and attach processes manually. This has been flaking out on Reids Windows buildbot, and clearly wasn't a good solution. Digging into this, it turns out that the "normal" cdb / windbg behaviour of breaking whenever we attach to a process is not the default: it has to be explicitly requested from the debug engine. This patch does so (by setting DEBUG_ENGOPT_INITIAL_BREAK in the engine options), after which we can simply call "CreateProcessAndAttach2" and everything automagically works. No test for this behaviour: everything was just broken before. Differential Revision: https://reviews.llvm.org/D74409 |
||
---|---|---|
.. | ||
README.md | ||
__init__.py | ||
breakpoint.py | ||
client.py | ||
control.py | ||
dbgeng.py | ||
probe_process.py | ||
setup.py | ||
symbols.py | ||
symgroup.py | ||
sysobjs.py | ||
utils.py |
README.md
Debugger Engine backend
This directory contains the Dexter backend for the Windows Debugger Engine (DbgEng), which powers tools such as WinDbg and CDB.
Overview
DbgEng is available as a collection of unregistered COM-"like" objects that one accesses by calling DebugCreate in DbgEng.dll. The unregistered nature means normal COM tooling can't access them; as a result, this backend uses ctypes to describe the COM objects and call their methods.
This is obviously not a huge amount of fun; on the other hand, COM has maintained ABI compatible interfaces for decades, and nothing is for free.
The dexter backend follows the same formula as others; it creates a process and breaks on "main", then steps through the program, observing states and stack frames along the way.
Implementation details
This backend uses a mixture of both APIs for accessing information, and the direct command-string interface to DbgEng for performing some actions. We have to use the DbgEng stepping interface, or we would effectively be building a new debugger, but certain things (like enabling source-line stepping) only seem to be possible from the command interface.
Each segment of debugger responsibility has its own COM object: Client, Control, Symbols, SymbolGroups, Breakpoint, SystemObjects. In this python wrapper, each COM object gets a python object wrapping it. COM methods that are relevant to our interests have a python method that wraps the COM one and performs data marshalling. Some additional helper methods are added to the python objects to extract data.
The majority of the work occurs in setup.py and probe_process.py. The former contains routines to launch a process and attach the debugger to it, while the latter extracts as much information as possible from a stopped process, returning a list of stack frames with associated variable information.
Sharp edges
On process startup, we set a breakpoint on main and then continue running to it. This has the potential to never complete -- although of course, there's no guarantee that the debuggee will ever do anything anyway.
There doesn't appear to be a way to instruct DbgEng to "step into" a function call, thus after reaching main, we scan the module for all functions with line numbers in the source directory, and put breakpoints on them. An alternative implementation would be putting breakpoints on every known line number.
Finally, it's unclear whether arbitrary expressions can be evaluated in arbitrary stack frames, although this isn't something that Dexter currently supports.