读取 Bug 检查回调数据

许多驱动程序检查回调例程提供 bug。 当 Windows 检查发出 bug 时,它会在关闭系统之前调用这些例程。 这些例程可以指定并写入称为回调数据和辅助回调数据的内存区域。

BugCheckCallback 使用 KBUGCHECK_CALLBACK_ROUTINE
此例程写入的数据将成为 回调数据的一部分。 故障转储文件不包含数据。

BugCheckSecondaryDumpDataCallback 使用 KBUGCHECK_REASON_CALLBACK_ROUTINE
此例程写入的数据将成为 辅助回调数据的一部分。 数据包含在故障转储文件中。

BugCheckAddPagesCallback 使用 KBUGCHECK_REASON_CALLBACK_ROUTINE
此例程指定的页将成为 回调数据的一部分。 这些页中的数据包含在故障转储文件中。

调试器可用的回调和辅助回调数据量取决于几个因素:

有关这些不同 转储文件大小的 更多详细信息,请参阅 Kernel-Mode 转储文件的品种。

显示回调数据

若要显示 bug 检查回调数据,可以使用 !bugdump 扩展。

没有任何参数, !bugdump 将显示所有回调的数据。

若要查看一个特定回调例程的数据,请使用 !bugdump组件,其中 Component 是注册该例程时传递给 KeRegisterBugCheckCallback 的相同参数。

显示辅助回调数据

有两种方法可用于显示辅助回调数据。 可以使用 .enumtag 命令,也可以编写自己的调试器扩展。

辅助回调数据的每个块都由 GUID 标记标识。 此标记由传递给 BugCheckSecondaryDumpDataCallback 的 (KBUGCHECK_SECONDARY_DUMP_DATA) ReasonSpecificData 参数的 Guid 字段指定。

.enumtag (枚举辅助回调数据) 命令不是非常精确的工具。 它显示每个辅助数据块,显示标记,然后以十六进制和 ASCII 格式显示数据。 通常,它只用于确定哪些标记实际用于辅助数据块。

若要以更实用的方式使用此数据,建议编写自己的调试器扩展。 此扩展必须调用 dbgeng.h 头文件中的方法。 有关详细信息,请参阅 编写新的调试器扩展

如果知道辅助数据块的 GUID 标记,扩展应使用 IDebugDataSpaces3::ReadTagged 方法来访问数据。 其原型如下所示:

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

下面是如何使用此方法的示例:

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

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

如果提供的 BufferSize 太小, ReadTagged 将成功,但只会将请求的字节数写入 Buffer。 如果指定的 BufferSize 太大, ReadTagged 将成功,但只会将实际块大小写入 Buffer。 如果为 TotalSize 提供指针, ReadTagged 将使用该指针返回实际块的大小。 如果无法访问块, ReadTagged 将返回失败状态代码。

如果两个块具有相同的 GUID 标记,将返回第一个匹配块,第二个块将无法访问。

如果不确定块的 GUID 标记,可以使用 IDebugDataSpaces3::StartEnumTaggedIDebugDataSpaces3::GetNextTaggedIDebugDataSpaces3::EndEnumTagged 方法来枚举标记的块。 其原型如下所示:

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;

调试回调例程

还可以调试回调例程本身。 回调例程中的断点的工作方式与任何其他断点一样。

如果回调例程导致第二个 bug 检查,则将首先处理此新的 bug 检查。 但是,Windows 不会重复停止进程的某些部分,例如,它不会写入第二个故障转储文件。 蓝屏上显示的停止代码将是代码检查第二个 bug。 如果附加了内核调试器,通常会显示有关这两个 bug 检查的消息。