读取 Bug 检查回调数据
许多驱动程序检查回调例程提供 bug。 当 Windows 检查发出 bug 时,它会在关闭系统之前调用这些例程。 这些例程可以指定并写入称为回调数据和辅助回调数据的内存区域。
BugCheckCallback 使用 KBUGCHECK_CALLBACK_ROUTINE
此例程写入的数据将成为 回调数据的一部分。 故障转储文件不包含数据。
BugCheckSecondaryDumpDataCallback 使用 KBUGCHECK_REASON_CALLBACK_ROUTINE
此例程写入的数据将成为 辅助回调数据的一部分。 数据包含在故障转储文件中。
BugCheckAddPagesCallback 使用 KBUGCHECK_REASON_CALLBACK_ROUTINE
此例程指定的页将成为 回调数据的一部分。 这些页中的数据包含在故障转储文件中。
调试器可用的回调和辅助回调数据量取决于几个因素:
如果要对崩溃的系统执行实时调试,则已由 BugCheckCallback 写入或由 BugCheckAddPagesCallback 指定的回调数据将可用。 辅助回调数据将不可用,因为它未存储在任何固定的内存位置。
如果要调试完整内存转储或内核内存转储, 则 BugCheckAddPagesCallback 指定的回调数据以及 BugCheckSecondaryDumpDataCallback 写入的 辅助回调数据将可用。 BugCheckCallback 写入的回调数据将不可用。
如果要调试小型内存转储,则回调数据将不可用。 辅助回调数据将可用。
有关这些不同 转储文件大小的 更多详细信息,请参阅 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::StartEnumTagged、 IDebugDataSpaces3::GetNextTagged 和 IDebugDataSpaces3::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 检查的消息。