Debugger Engine (DbgEng) updates in the Windows 8 Developer Preview

Today I wanted to find out what the Debugger Engine (DbgEng) changes are between Windows 7 and the Windows 8 Developer Preview. 

To get the differences, I did a WinDiff between the SDK 7.1 ‘DbgEng.h’ header file and the new version (C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x64\sdk\inc) shipped with the Windows 8 Developer Preview’s WDK (en_windows_developer_preview_wdk_x86_x64_741966.zip).

By just looking at the header file, you can easily see that there are lots of new goodies.  Of major note is the support for inline debugging; this new feature has caused a few interface revisions and some additional constants.

So let’s dive in!

Global Functions

There is only one new global function. The DebugCreate function has been extended by adding Debugger Engine Options (DEBUG_ENGOPT_XXX) via the new DebugCreateEx function.

STDAPI
DebugCreateEx(
_In_ REFIID InterfaceId,
_In_ DWORD DbgEngOptions,
_Out_ PVOID* Interface
);

IDebugAdvanced2 Interface

The IDebugAdvanced2::Request function supports three new requests.

// InBuffer - ULONG64 for process server identification and ULONG as PID
// OutBuffer - Unused
#define DEBUG_REQUEST_WOW_PROCESS 31

// InBuffer - ULONG64 for process server identification and PWSTR as module path
// OutBuffer - Unused
#define DEBUG_REQUEST_WOW_MODULE 32

// InBuffer - Unused
// OutBuffer - Unused
// return - S_OK is non-invasive user-mode attach, S_FALSE if not (but still live user-mode), E_FAIL otherwise.
#define DEBUG_LIVE_USER_NON_INVASIVE 33

IDebugBreakpoint Interface

In alignment with the new inline debugging support, a new inline breakpoint has been added for use with IDebugBreakpoint::GetType.

#define DEBUG_BREAKPOINT_INLINE 3

IDebugBreakpoint3 Interface (new)

The new IDebugBreapkpoint3 interface has just a single function (GetGUID) that returns a GUID.  This is the unique identifier of the breakpoint. The GUID can be passed to IDebugControl5::GetBreakpointByGuid to get the IDebugBreakpoint3 interface.

STDMETHOD(GetGuid)(
THIS_
_Out_ LPGUID Guid
) PURE;

IDebugClient2 Interface

There are two new DEBUG_FORMAT_XXX constants for use with IDebugClient2::WriteDumpFile2 and IDebugClient4::WriteDumpFileWide.

#define DEBUG_FORMAT_USER_SMALL_MODULE_HEADERS 0x00008000
#define DEBUG_FORMAT_USER_SMALL_FILTER_TRIAGE 0x00010000

IDebugClient6 Interface (new)

The new IDebugEventContextCallbacks interface can be set on the debugger client via IDebugClient6::SetEventContextCallbacks. This new event interface replaces the use of IDebugClient::SetEventCallbacks.

STDMETHOD(SetEventContextCallbacks)(
THIS_
_In_opt_ PDEBUG_EVENT_CONTEXT_CALLBACKS Callbacks
) PURE;

IDebugClient7 Interface (new)

The new SetClientContext function takes one of the new a DEBUG_CLIENT_XXX constants via the new DEBUG_CLIENT_CONTEXT structure. It is used to tailor the debugger client to the various (known) debugger applications.  Integrated debugging in Visual Studio 2011 is DEBUG_CLIENT_VSINT.

// Client identification constants
#define DEBUG_CLIENT_UNKNOWN 0x0
#define DEBUG_CLIENT_VSINT 0x1
#define DEBUG_CLIENT_NTSD 0x2
#define DEBUG_CLIENT_NTKD 0x3
#define DEBUG_CLIENT_CDB 0x4
#define DEBUG_CLIENT_KD 0x5
#define DEBUG_CLIENT_WINDBG 0x6
#define DEBUG_CLIENT_WINIDE 0x7

typedef struct _DEBUG_CLIENT_CONTEXT
{
UINT cbSize;
UINT eClient;
} DEBUG_CLIENT_CONTEXT, *PDEBUG_CLIENT_CONTEXT;

// IDebugClient7

STDMETHOD(SetClientContext)(
THIS_
_In_reads_bytes_(ContextSize) PVOID Context,
_In_ ULONG ContextSize
) PURE;

IDebugControl Interface

The Engine Options (DEBUG_ENGOPT_XXX) have been updated with a higher DEBUG_ENGOPT_ALL value and a new option to disable SQM (Software Quality Metrics – better known as Customer Experience Improvement Program). The options can be set via DebugCreateEx and IDebugControl::SetEngineOptions.

// Explicitly disable SQM upload.
#define DEBUG_ENGOPT_DISABLESQM 0x00080000
#define DEBUG_ENGOPT_ALL 0x000FFFFF

IDebugControl5 Interface (new)

The new inline frame support can be toggled off and on via .inline_query 0 and .inline_query 1 respectively (it is on by default).

#define DBG_FRAME_DEFAULT 0 // the same as INLINE_FRAME_CONTEXT_INIT in dbghelp.h
#define DBG_FRAME_IGNORE_INLINE 0xFFFFFFFF // the same as INLINE_FRAME_CONTEXT_IGNORE in dbghelp.h

The DEBUG_STACK_FRAME structure has been enhanced to support inline frames via the DEBUG_STACK_FRAME_EX structure.

typedef struct _DEBUG_STACK_FRAME_EX
{
// First DEBUG_STACK_FRAME structure
ULONG64 InstructionOffset;
ULONG64 ReturnOffset;
ULONG64 FrameOffset;
ULONG64 StackOffset;
ULONG64 FuncTableEntry;
ULONG64 Params[4];
ULONG64 Reserved[6];
BOOL Virtual;
ULONG FrameNumber;

    // Extended DEBUG_STACK_FRAME fields.
ULONG InlineFrameContext;
ULONG Reserved1; // For alignment purpose.
} DEBUG_STACK_FRAME_EX, *PDEBUG_STACK_FRAME_EX;

The IDebugControl5 interface has extended the stack functions to return the additional inline frames.  Note, the older interfaces behave the same as before – they do not include inline frames.

// IDebugControl5
STDMETHOD(GetStackTraceEx)(
THIS_
_In_ ULONG64 FrameOffset,
_In_ ULONG64 StackOffset,
_In_ ULONG64 InstructionOffset,
_Out_writes_to_(FramesSize,*FramesFilled) PDEBUG_STACK_FRAME_EX Frames,
_In_ ULONG FramesSize,
_Out_opt_ PULONG FramesFilled
) PURE;

STDMETHOD(OutputStackTraceEx)(
THIS_
_In_ ULONG OutputControl,
_In_reads_opt_(FramesSize) PDEBUG_STACK_FRAME_EX Frames,
_In_ ULONG FramesSize,
_In_ ULONG Flags
) PURE;

STDMETHOD(GetContextStackTraceEx)(
THIS_
_In_reads_bytes_opt_(StartContextSize) PVOID StartContext,
_In_ ULONG StartContextSize,
_Out_writes_to_opt_(FramesSize,*FramesFilled) PDEBUG_STACK_FRAME_EX Frames,
_In_ ULONG FramesSize,
_Out_writes_bytes_opt_(FrameContextsSize) PVOID FrameContexts,
_In_ ULONG FrameContextsSize,
_In_ ULONG FrameContextsEntrySize,
_Out_opt_ PULONG FramesFilled
) PURE;

STDMETHOD(OutputContextStackTraceEx)(
THIS_
_In_ ULONG OutputControl,
_In_reads_(FramesSize) PDEBUG_STACK_FRAME_EX Frames,
_In_ ULONG FramesSize,
_In_reads_bytes_(FrameContextsSize) PVOID FrameContexts,
_In_ ULONG FrameContextsSize,
_In_ ULONG FrameContextsEntrySize,
_In_ ULONG Flags
) PURE;

STDMETHOD(GetBreakpointByGuid)(
THIS_
_In_ LPGUID Guid,
_Out_ PDEBUG_BREAKPOINT3* Bp
) PURE;

IDebugControl6 Interface (new)

The new IDebugControl6 interface contains functions to determine the state of debugging session’s connection.

// Returns additional info states for
STDMETHOD(GetExecutionStatusEx)(
THIS_
_Out_ PULONG Status
) PURE;

STDMETHOD(GetSynchronizationStatus)(
THIS_
_Out_ PULONG SendsAttempted,
_Out_ PULONG SecondsSinceLastResponse
) PURE;

IDebugEventCallbacks Interface

The ‘Change Debuggee State’ flags (DEBUG_CDS_XXX) have a new value for refresh, and a new associated arguments (DEBUG_CDS_REFRESH_XXX). These are passed to IDebugEventCallbacks::ChangeDebuggeeState.

// Inform the GUI clients to refresh debugger windows.
#define DEBUG_CDS_REFRESH 0x00000004

// DEBUG_CDS_REFRESH IDs
#define DEBUG_CDS_REFRESH_EVALUATE 1
#define DEBUG_CDS_REFRESH_EXECUTE 2
#define DEBUG_CDS_REFRESH_EXECUTECOMMANDFILE 3
#define DEBUG_CDS_REFRESH_ADDBREAKPOINT 4
#define DEBUG_CDS_REFRESH_REMOVEBREAKPOINT 5
#define DEBUG_CDS_REFRESH_WRITEVIRTUAL 6
#define DEBUG_CDS_REFRESH_WRITEVIRTUALUNCACHED 7
#define DEBUG_CDS_REFRESH_WRITEPHYSICAL 8
#define DEBUG_CDS_REFRESH_WRITEPHYSICAL2 9
#define DEBUG_CDS_REFRESH_SETVALUE 10
#define DEBUG_CDS_REFRESH_SETVALUE2 11
#define DEBUG_CDS_REFRESH_SETSCOPE 12
#define DEBUG_CDS_REFRESH_SETSCOPEFRAMEBYINDEX 13
#define DEBUG_CDS_REFRESH_SETSCOPEFROMJITDEBUGINFO 14
#define DEBUG_CDS_REFRESH_SETSCOPEFROMSTOREDEVENT 15

IDebugEventContextCallbacks Interface (new)

The new IDebugEventContextCallbacks interface is the same as the IDebugEventCallbacks interface but adds the new DEBUG_EVENT_CONTEXT structure to each function.

typedef struct _DEBUG_EVENT_CONTEXT
{
ULONG Size;
ULONG ProcessEngineId;
ULONG ThreadEngineId;
ULONG FrameEngineId;
} DEBUG_EVENT_CONTEXT, *PDEBUG_EVENT_CONTEXT;

// IDebugEventContextCallbacks.

// The engine calls GetInterestMask once when
// the event callbacks are set for a client.
STDMETHOD(GetInterestMask)(
THIS_
_Out_ PULONG Mask
) PURE;

// A breakpoint event is generated when
// a breakpoint exception is received and
// it can be mapped to an existing breakpoint.
// The callback method is given a reference
// to the breakpoint and should release it when
// it is done with it.
STDMETHOD(Breakpoint)(
THIS_
_In_ PDEBUG_BREAKPOINT2 Bp,
_In_reads_bytes_(ContextSize) PVOID Context,
_In_ ULONG ContextSize
) PURE;

// Exceptions include breaks which cannot
// be mapped to an existing breakpoint
// instance.
STDMETHOD(Exception)(
THIS_
_In_ PEXCEPTION_RECORD64 Exception,
_In_ ULONG FirstChance,
_In_reads_bytes_(ContextSize) PVOID Context,
_In_ ULONG ContextSize
) PURE;

// Any of these values can be zero if they
// cannot be provided by the engine.
// Currently the kernel does not return thread
// or process change events.
STDMETHOD(CreateThread)(
THIS_
_In_ ULONG64 Handle,
_In_ ULONG64 DataOffset,
_In_ ULONG64 StartOffset,
_In_reads_bytes_(ContextSize) PVOID Context,
_In_ ULONG ContextSize
) PURE;

STDMETHOD(ExitThread)(
THIS_
_In_ ULONG ExitCode,
_In_reads_bytes_(ContextSize) PVOID Context,
_In_ ULONG ContextSize
) PURE;

// Any of these values can be zero if they
// cannot be provided by the engine.
STDMETHOD(CreateProcess)(
THIS_
_In_ ULONG64 ImageFileHandle,
_In_ ULONG64 Handle,
_In_ ULONG64 BaseOffset,
_In_ ULONG ModuleSize,
_In_opt_ PCWSTR ModuleName,
_In_opt_ PCWSTR ImageName,
_In_ ULONG CheckSum,
_In_ ULONG TimeDateStamp,
_In_ ULONG64 InitialThreadHandle,
_In_ ULONG64 ThreadDataOffset,
_In_ ULONG64 StartOffset,
_In_reads_bytes_(ContextSize) PVOID Context,
_In_ ULONG ContextSize
) PURE;

STDMETHOD(ExitProcess)(
THIS_
_In_ ULONG ExitCode,
_In_reads_bytes_(ContextSize) PVOID Context,
_In_ ULONG ContextSize
) PURE;

// Any of these values may be zero.
STDMETHOD(LoadModule)(
THIS_
_In_ ULONG64 ImageFileHandle,
_In_ ULONG64 BaseOffset,
_In_ ULONG ModuleSize,
_In_opt_ PCWSTR ModuleName,
_In_opt_ PCWSTR ImageName,
_In_ ULONG CheckSum,
_In_ ULONG TimeDateStamp,
_In_reads_bytes_(ContextSize) PVOID Context,
_In_ ULONG ContextSize
) PURE;

STDMETHOD(UnloadModule)(
THIS_
_In_opt_ PCWSTR ImageBaseName,
_In_ ULONG64 BaseOffset,
_In_reads_bytes_(ContextSize) PVOID Context,
_In_ ULONG ContextSize
) PURE;

STDMETHOD(SystemError)(
THIS_
_In_ ULONG Error,
_In_ ULONG Level,
_In_reads_bytes_(ContextSize) PVOID Context,
_In_ ULONG ContextSize
) PURE;

// Session status is synchronous like the other
// wait callbacks but it is called as the state
// of the session is changing rather than at
// specific events so its return value does not
// influence waiting. Implementations should just
// return DEBUG_STATUS_NO_CHANGE.
// Also, because some of the status
// notifications are very early or very
// late in the session lifetime there may not be
// current processes or threads when the notification
// is generated.
STDMETHOD(SessionStatus)(
THIS_
_In_ ULONG Status
) PURE;

// The following callbacks are informational
// callbacks notifying the provider about
// changes in debug state. The return value
// of these callbacks is ignored. Implementations
// can not call back into the engine.

// Debuggee state, such as registers or data spaces,
// has changed.
STDMETHOD(ChangeDebuggeeState)(
THIS_
_In_ ULONG Flags,
_In_ ULONG64 Argument,
_In_reads_bytes_(ContextSize) PVOID Context,
_In_ ULONG ContextSize
) PURE;

// Engine state has changed.
STDMETHOD(ChangeEngineState)(
THIS_
_In_ ULONG Flags,
_In_ ULONG64 Argument,
_In_reads_bytes_(ContextSize) PVOID Context,
_In_ ULONG ContextSize
) PURE;

// Symbol state has changed.
STDMETHOD(ChangeSymbolState)(
THIS_
_In_ ULONG Flags,
_In_ ULONG64 Argument
) PURE;

IDebugSymbols3 Interface

The ‘Get Source Entries by Line’ flags (DEBUG_GSEL_XXX) has an addition entry for inline functions. The constant is passed to IDebugSymbols3::GetSourceEntriesByLine.

// Only return caller sites of the inline function
#define DEBUG_GSEL_INLINE_CALLSITE 0x00000010

IDebugSymbols4 Interface (new)

The IDebugSymbols4 interface allows you to determine the symbol of an inline frame.

STDMETHOD(GetScopeEx)(
THIS_
_Out_opt_ PULONG64 InstructionOffset,
_Out_opt_ PDEBUG_STACK_FRAME_EX ScopeFrame,
_Out_writes_bytes_opt_(ScopeContextSize) PVOID ScopeContext,
_In_ ULONG ScopeContextSize
) PURE;

STDMETHOD(SetScopeEx)(
THIS_
_In_ ULONG64 InstructionOffset,
_In_opt_ PDEBUG_STACK_FRAME_EX ScopeFrame,
_In_reads_bytes_opt_(ScopeContextSize) PVOID ScopeContext,
_In_ ULONG ScopeContextSize
) PURE;

STDMETHOD(GetNameByInlineContext)(
THIS_
_In_ ULONG64 Offset,
_In_ ULONG InlineContext,
_Out_writes_opt_(NameBufferSize) PSTR NameBuffer,
_In_ ULONG NameBufferSize,
_Out_opt_ PULONG NameSize,
_Out_opt_ PULONG64 Displacement
) PURE;

STDMETHOD(GetNameByInlineContextWide)(
THIS_
_In_ ULONG64 Offset,
_In_ ULONG InlineContext,
_Out_writes_opt_(NameBufferSize) PWSTR NameBuffer,
_In_ ULONG NameBufferSize,
_Out_opt_ PULONG NameSize,
_Out_opt_ PULONG64 Displacement
) PURE;

STDMETHOD(GetLineByInlineContext)(
THIS_
_In_ ULONG64 Offset,
_In_ ULONG InlineContext,
_Out_opt_ PULONG Line,
_Out_writes_opt_(FileBufferSize) PSTR FileBuffer,
_In_ ULONG FileBufferSize,
_Out_opt_ PULONG FileSize,
_Out_opt_ PULONG64 Displacement
) PURE;

STDMETHOD(GetLineByInlineContextWide)(
THIS_
_In_ ULONG64 Offset,
_In_ ULONG InlineContext,
_Out_opt_ PULONG Line,
_Out_writes_opt_(FileBufferSize) PWSTR FileBuffer,
_In_ ULONG FileBufferSize,
_Out_opt_ PULONG FileSize,
_Out_opt_ PULONG64 Displacement
) PURE;

STDMETHOD(OutputSymbolByInlineContext)(
THIS_
_In_ ULONG OutputControl,
_In_ ULONG Flags,
_In_ ULONG64 Offset,
_In_ ULONG InlineContext
) PURE;

IDebugSymbols5 Interface (new)

And last, but definitely not least, is the new IDebugSymbols5 interface allows you to set and get the current frame by index (i.e. the same operation as .frame NN).

// IDebugSymbols5
STDMETHOD(GetCurrentScopeFrameIndexEx)(
THIS_
_In_ ULONG Flags,
_Out_ PULONG Index
) PURE;

STDMETHOD(SetScopeFrameByIndexEx)(
THIS_
_In_ ULONG Flags,
_In_ ULONG Index
) PURE;

Conclusion

As you can see, this release of the debugger is quite significant.  The integration in to Visual Studio, the support for inline function debugging, and the greatly improved local variable support are all great features that we’ll all get immediate productivity improvement from.