Debugging Tools for Windows

Reading Bug Check Callback Data

Many drivers supply bug check callback routines. When Windows issues a bug check, it calls these routines before shutting down the system. One task these routines can do is indicate certain important parts of memory for debugging purposes.

In Windows 2000, and Windows XP prior to SP1, the bug check callback function is named BugCheckCallback. It is primarily used to write data to a region in memory — this data is called callback data.

In Windows XP SP1, Windows Server 2003, and later versions of Windows, there are multiple callback functions. BugCheckCallback is the same as its counterpart in earlier versions of Windows, except that it is called after the dump file is written. Another callback function, BugCheckSecondaryDumpDataCallback, is called before the dump file is written — the data written by this routine is called secondary callback data.

How much of this information is available to the debugger depends on several factors:

See Varieties of Kernel-Mode Dump Files for more details on these different dump file sizes.

Displaying Callback Data

To display bug check callback data, you can use the !bugdump extension.

Without any parameters, !bugdump will display data for all callbacks.

To view data for one specific callback routine, use !bugdump Component, where Component is the same parameter that was passed to KeRegisterBugCheckCallback when that routine was registered.

Displaying Secondary Callback Data

There are two methods for displaying secondary callback data in Windows XP SP1, Windows Server 2003, and later versions of Windows. You can use the .enumtag command or you can write your own debugger extension.

Each block of secondary callback data is identified by a GUID tag. This tag is specified by the Guid field of the (KBUGCHECK_SECONDARY_DUMP_DATA)ReasonSpecificData parameter passed to BugCheckSecondaryDumpDataCallback.

The .enumtag (Enumerate Secondary Callback Data) command is not a very precise instrument. It displays every secondary data block, showing the tag and then showing the data in hexadecimal and ASCII format. It is generally useful only to determine what tags are actually being used for secondary data blocks.

To use this data in a more practical way, it is recommended that you write your own debugger extension. This extension must call methods in the dbgeng.h header file. For details, see Writing New Debugger Extensions.

If you know the GUID tag of the secondary data block, your extension should use the method IDebugDataSpaces3::ReadTagged to access the data. Its prototype is as follows:

STDMETHOD(ReadTagged)(
    THIS_
    IN LPGUID Tag,
    IN ULONG Offset,
    OUT OPTIONAL PVOID Buffer,
    IN ULONG BufferSize,
    OUT OPTIONAL PULONG TotalSize
    ) PURE; 

Here is an example of how to use this method:

UCHAR RawData[MY_DATA_SIZE];
GUID MyGuid = .... ;

Success = DataSpaces->ReadTagged(  &MyGuid,  0,  RawData,
                                   sizeof(RawData),  NULL); 

If you supply a BufferSize that is too small, ReadTagged will succeed but will write only the requested number of bytes to Buffer. If you specify a BufferSize that is too large, ReadTagged will succeed but will write only the actual block size to Buffer. If you supply a pointer for TotalSize, ReadTagged will use it to return the size of the actual block. If the block cannot be accessed, ReadTagged will return a failure status code.

If two blocks have identical GUID tags, the first matching block will be returned, and the second block will be inaccessible.

If you are not sure of the GUID tag of your block, you can use the IDebugDataSpaces3::StartEnumTagged, IDebugDataSpaces3::GetNextTagged, and IDebugDataSpaces3::EndEnumTagged methods to enumerate the tagged blocks. Their prototypes are as follows:

STDMETHOD(StartEnumTagged)(
    THIS_
    OUT PULONG64 Handle
    ) PURE;

STDMETHOD(GetNextTagged)(
    THIS_
    IN ULONG64 Handle,
    OUT LPGUID Tag,
    OUT PULONG Size
    ) PURE;

STDMETHOD(EndEnumTagged)(
    THIS_
    IN ULONG64 Handle
    ) PURE; 

Debugging Callback Routines

It is also possible to debug the callback routine itself. Breakpoints within callback routines work just like any other breakpoint.

If the callback routine causes a second bug check, this new bug check will be processed first. However, Windows will not repeat certain parts of the Stop process — for example, it will not write a second crash dump file. The Stop code displayed on the blue screen will be the second bug check code. If a kernel debugger is attached, messages about both bug checks will usually appear.

Build machine: CAPEBUILD