400 likes | 732 Views
Developing Debugger Extensions. Prasad Kakulamarri Senior Development Lead Debugging Tools for Windows PrasadK@Microsoft.com. Agenda. What are Debugger Extensions? Why create your own extension? Extension Frameworks How do they work? 64-bit support Demo. Debugger Engine.
E N D
Developing Debugger Extensions Prasad Kakulamarri Senior Development Lead Debugging Tools for Windows PrasadK@Microsoft.com
Agenda • What are Debugger Extensions? • Why create your own extension? • Extension Frameworks • How do they work? • 64-bit support • Demo
Debugger Engine • The debugger engine (DbgEng.dll): • Provides interfaces for examining and manipulating debugging targets in user mode and kernel mode • Can acquire targets, set breakpoints, monitor events, query symbols, read & write to memory, control threads and processes in a target • Can be used to write debugger extension libraries and stand-alone applications • Is shipped with the OS and as part of the debugger package • A debugger engine application that uses full functionality of the debugger engine is a debugger (Ex: Windbg) • A debugger extension library is an add-in to the debugger that provides additional functionality
What are debugger extensions? • Extensions are plug-ins that extend the functionality of the debugger • They are DLLs that use the interfaces exposed by the debugger engine typically to get information about the state of the target (process/driver/machine) • They are loaded by the debugger and run in the context of the debugger process (Ex: windbg) • Extension command syntax ![module.]extension [arguments] • Common examples include: !Analyze – Automatic analysis of a break !Process – Displays information about a specified process or all processes
Why create your own extension? • Great for automation of common debugging operations • You might want to write a command to analyze a complex data structure or get statistics of the types of operations performed by your driver • Simple structures can be dumped and analyzed using the dt debugger command; No need to write extensions for this • Complex trees and lists can be displayed, searched, and parsed to show the most commonly used information • Windows DEV team could not debug the OS without debugger extensions • We have written 100s of these: !process, !thread, !handle, !htrace, !verifier, !teb, !peb, !vad • A debugger without any extensions is like a car without any seats
Getting started… • All documentation about existing debugger extensions (!pool, !thread, !process) is in the basic debugger documentation • Installed by default in the root of the debugger in debugger.chm • All documentation about debugger extension APIs and all debugger extension code samples is part of the debugger SDK • SDK is installed by default in the current external release • Docs are available in Debugger.CHM and on MSDN here: http://msdn.microsoft.com/en-us/library/cc267445.aspx • Samples are stored under the samples directory
Debugger Extension Programming Model • Debugger extensions are DLLs loaded by the debugger using LoadLibrary • Debugger extensions run in the context of the debugger process (ex:WinDBG) • Debugger extensions are trusted by the debugger • A simple try/except is placed around execution of extensions to recover from AVs • Heap corruption in an extension will cause the debugger to crash
Debugger Extension Programming Model (contd.) • A debugger extension can make calls to • Any Win32 API • APIs run in the context of the debugger process (ex: WinDBG) • Debugger interfaces (dbgeng.dll) • Must be careful of interactions between Win32 APIs, dbgeng and dbghelp APIs • ReadProcessMemory could return different data than calling the debugger to read memory from the target process • Calling dbghelp APIs directly can change internal state dbgeng relies on • Debugger extensions run on the host debugger • In remote debugging scenarios, input/output is sent over the wire
Extensions run on the host… Target Process Host Process (Debugger) Debugger Extension No! My App Extension Interfaces My Dll 1 DbgEng My Dll 2 UserMode : I . P. C. Kernel Mode: Debug Port
Remote debugging Remote Debugger Host: Debugger Process User Input Debugger Extension Extension Interfaces DbgEng DbgEng Remote Debugging Protocol: Named Pipes, TCP
User Mode, Kernel Mode, Dump Files • Debugger extension can work with any type or version of target • The debugger API abstracts the “target” as much as possible • All basic extension APIs work on live sessions or dump files, user mode or kernel mode targets, and any version of the OS • Certain specific operations or data may not make sense on all targets • Examples • Breakpoints won’t work on dumps files • ReadPhysicalMemory is not supported for user mode targets • Debugger extensions must gracefully handle errors • The debugger package organizes extensions based on the targets they support • Ext.dll – extensions that work for both user mode and kernel mode (!analyze, !error, !list) • Uext.dll – user-mode only extensions (!runaway, !evlog) • Kext.dll – kernel-mode only extensions (!pci, !process)
Extension Frameworks • Three types of extension frameworks • WdbgExts extensions (legacy) • DbgEng Style extensions (new) • EngExtsCpp extensions (new)
Legacy (Old) Debugger Extension Interfaces (WdbgExts) • Often referred to as Old Style extensions • Continued to be fully supported by dbgeng.dll • Definitions can be found in wdbgexts.h • Expose limited functionality that enables most common features • Read and write memory • Current Process / Thread information • Expression evaluation • Basic Symbol lookup • Basic Type Lookup • Cancellation with Crtl+C/Ctrl+Break • Extension functions are called by • Asking the debugger engine for a table containing debugger extension function pointers • Making calls through these function pointers
New Debugger Interfaces (DbgEng) • Often referred to as dbgengstyle extensions • Debugger engine exposes a new, complete set of interfaces • Everything that can be performed by a debugger is exposed by the interface • Referred to as the dbgeng API • WinDBG is built on top of this API • All debugging capabilities are exposed through dbgeng.dll • Can write new standalone tools that call the interface • Debugger extension DLLs are generally more convenient in most scenarios
New Debugger Interfaces (DbgEng), contd. • Functionality supported by the dbgeng API • Read and write memory • Current thread and process information • Expression evaluation • Full symbol lookup and enumeration • Full type lookup and enumeration • General target information • Extension functions are called by • Creating debug interface objects • Calling the engine interfaces exposed by these objects
“Legacy” or “New” interface • Legacy extensions require a little less code • Macros are available for common operations • Initialization is a little less complex • Great for very simple extensions • New extension APIs provide more flexibility and features • Many more features • Fine grained control over debugger behavior • Can still use legacy interfaces and macros for part of the code • All debugging capabilities are exposed through dbgeng.dll • Windbg is built on top of this API
Legacy Extension Initialization • Legacy extension DLLs must export the following entry points • WinDbgExtensionDllInit – Required • ExtensionApiVersion – Required • CheckVersion – Optional • The debugger finds entry points using GetProcAddress on the extension DLL • Entry Points are called at DLL initialization • No Uninitialize routine in the legacy extension model
Legacy Extension Initialization typedef VOID (WDBGAPI*PWINDBG_EXTENSION_DLL_INIT)( PWINDBG_EXTENSION_APIS lpExtensionApis, USHORT MajorVersion, USHORT MinorVersion); typedef LPEXT_API_VERSION (WDBGAPI*PWINDBG_EXTENSION_API_VERSION)( VOID); typedef ULONG (WDBGAPI*PWINDBG_CHECK_VERSION)( VOID);
New Extension Initialization • New extension DLL must export the following entry points • DebugExtensionInitialize - Required • DebugExtensionNotify - Optional • DebugExtensionUninitialize - Optional • An extension cannot export both new and old entry points from its DLL • The debugger finds entry points using GetProcAddress on the extension DLL • DebugExtensionNotify is called when the target is connected or disconnected
DebugExtensionInitialize HRESULT CALLBACK DebugExtensionInitialize(PULONG Version, PULONG Flags) • Mandatory export • Called when the extension is loaded • Extension can use it to initialize any global variables • Should return the extension version in *Version • Should not query for debug session information • Debug session may not be active at this point
DebugExtensionNotify Void CALLBACK DebugExtensionNotify(ULONG Notify, ULONG64 Argument) • DebugExtensionNotify is called when the target is connected or disconnected • Optional export • Can be used to cache debug session information without the need for registering any callbacks • Notify has the following values #define DEBUG_NOTIFY_SESSION_ACTIVE 0x00000000 #define DEBUG_NOTIFY_SESSION_INACTIVE 0x00000001 #define DEBUG_NOTIFY_SESSION_ACCESSIBLE 0x00000002 #define DEBUG_NOTIFY_SESSION_INACCESSIBLE 0x00000003
DebugExtensionUninitialize • DebugExtensionUninitialize is called when the extension is unloaded • Optional export • Can perform cleanup here • Just like DebugExtensionInitialize, you cannot assume that a debug session is active at this point
Anatomy of a dbgeng extension call • A debugger extension is a function with the following recommended prototype: HRESULT CALLBACK DebugExtensionCall( PDEBUG_CLIENT Client, PCSTR args) • Where "DebugExtensionCall" is an actual name of the extension being implemented, such as "stack" for a !stack extension • Can have any other prototype • The Client parameter is the pointer to the debug engine's IDebugClient interface • The args parameter is simply the command line argument string passed to the extension
Anatomy of a dbgeng extension call (contd.) • A debugger extension is a function with the following recommended prototype: HRESULT CALLBACK DebugExtensionCall(PDEBUG_CLIENT Client, PCSTR args) • Can return DEBUG_EXTENSION_CONTINUE_SEARCH if the extension cannot handle the request • In this case the command is passed to the next extension DLL in the chain • Can return DEBUG_EXTENSION_RELOAD_EXTENSION to unload and reload the extension library • Allows extension to implement auto-update functionality
Getting Debugger Extension APIs • Legacy debugger extensions get legacy extension interfaces (function pointers) during initialization as part of init call (param1) • Pointers must be cached in a global variable named ExtensionApis • New debugger extensions get new debugger interfaces by calling DebugCreate(__uuidof (IDebugClient), &DebugClient)) DebugClient->QueryInterface(_uuidof(Interface_you_want) • New debugger extensions can also query for the legacy extension interfaces DebugClient->QueryInterface(_uuidof(IDebugControl),&DebugControl); DebugControl->GetWindbgExtensionApis64(&ExtensionApis);
Debugger input… • Debugger extensions receive arguments as one long text string • Extensions can parse parameters anyway they want • Extension will be given all input up to a ‘;’ character, unless quoted • ‘;’ is a delimiter between multiple, independent debugger commands • Debugger provides routines to help parse arguments • interface: Evaluate() • This interface does both parsing and evaluation of arguments • Strings will be treated as symbols • ‘-’ will be treated as minus
Debugger output… • Standard debugger extension output is text-based • Standard output format very similar to C-runtime • %s, %d, specifiers, etc. • Special pointerspecifier%p • Always consumes 64-bit input (always use ULONG64) • Prints 32 or 64 bits based on the target OS • Interface: DebugControl->Output(Mask, Format,...); //where DebugControl is IDebugControl object
Symbol Lookup • Use symbols to look up data in your driver • Always use a module qualifier when evaluating a symbol • Example: mydriver!data • Without a module qualifier, the debugger will load all symbol files until it finds a module with a symbol data • Avoid caching evaluated symbols (in global variables) • Driver can load and unload, and extensions are not notified of such events. Once a program database (PDB) is loaded, symbol lookup is not expensive. • Legacy interfaces • Get an address from a name: GetExpression() • Get a name from an address: GetSymbol() • New Interfaces • Get an address from a name: Evaluate(), GetOffsetByName() • Get a name from an address: GetNameByOffset()
Reading Memory • Most code uses the built-in ReadMemory macro with legacy debugger extension interfaces for memory reading • Reading raw memory should be a rare occurrence in a debugger extension • Use more structured memory reading operations
Reading Memory (contd.) • These APIs read memory on the target process / machine #define ReadMemory (ExtensionApis.lpReadProcessMemoryRoutine) Typedef ULONG (WDBGAPI*PWINDBG_READ_PROCESS_MEMORY_ROUTINE64)( ULONG64 offset, PVOID lpBuffer, ULONG cb, PULONG lpcbBytesRead); IDebugDataSpaces STDMETHOD(ReadVirtual)( THIS_ IN ULONG64 Offset, OUT PVOID Buffer, IN ULONG BufferSize, OUT OPTIONAL PULONG BytesRead);
Reading Memory (contd.) • To read data that will be interpreted as pointers, use ReadPointer(ULONG64 Address, PULONG64 Pointer); • If the target uses 32-bit pointers, the pointer is sign-extended to 64 bits.
Cancellation – Control+C Handling • Control+C handling is shared between the core debugger code and the extension • Generated by Ctrl+C in command line debuggers, Ctrl+Break in WinDbg • The debugger engine will: • Receive the Ctrl+C event • Store the state of this event in a global variable • Debugger extension must check this state • Only one thread in the debugger processes command. The debugger extension takes over this thread • Debugger cannot kill the extension thread • Debugger extension must call the engine to check the state and exit its processing • Very useful in cancelling long running commands
Extension Functions • Most extension commands run from the debugger generate text output • Example: !pool, !process, !analyze • The debugger also supports debugger extension functions • Example: _EFN_GetPoolData • Output : ‘C’ data structure that can be consumed by the caller • Extension functions are defined in extsfns.h • Allows extensions to be built using other debugger extensions, without text parsing • Exported from a DLL, like other extension commands • _EFN_ is the convention for exporting these functions to distinguish them from normal debugger extension commands • Debugger engine automatically appends this prefix • GetExtensionFunction equivalent to GetProcAddress
64-Bit Support • Everything in the debugger and debugger extension APIs is designed around 64-bit support • All recent debugger APIs treat addresses as 64-bit values • All pointers are passed as ULONG64 • Debugger manipulates all addresses as 64-bit values • Debugger extensions should, too • 32-bit addresses read from a 32-bit target need to be SIGN-EXTENDED • #define KDEXT_64BIT required before including wdbgexts.h
Some Popular Interfaces • IDebugClient • Controls the debug session • Attach/detach to a process/kernel, set/get callbacks, read/write dump files, etc. • IDebugControl • Execute debugger commands, add/remove breakpoints, get Windbg (legacy) extension APIs, etc. • IDebugSymbols • Get an address from a name: Evaluate(), GetOffsetByName() • Get a name from an address: GetNameByOffset() • Use DebugCreate to get the IDebugClient interface and go from there DebugCreate(__uuidof(IDebugClient),(void **)&DebugClient)); DebugClient->QueryInterface(__uuidof(IDebugControl), (void **)&DebugControl); DebugClient->QueryInterface(__uuidof(IDebugSymbols)( (void**)&DebugSymbols);
Demo: Write a simple dbgeng style extension to execute a command from the extension (‘lm’ to list all modules loaded by the target process).Unload and reload the extension to demo auto-update feature.
Call to Action • Write debugger extensions for a better debugging experience and to better understand the behavior of your driver/application • The debugger SDK ships with the debugger package • Easy to get started using the samples provided • Send any feedback to us: windbgfb@microsoft.com
Resources • Debugger download site: • http://www.microsoft.com/whdc/devtools/debugging/default.mspx • Debugger e-mail – for debugger bug reports and feature requests • windbgfb@microsoft.com • This is not for general debugging support • Debugger newsgroup • Microsoft.public.windbg • Good place for general debugging issues • Debugger information on WHDC Web • http://www.microsoft.com/whdc/devtools/WDK/default.mspx • Debugger documentation in MSDN • http://msdn.microsoft.com/en-us/library/cc267445.aspx