已检验版本如何指出问题

注意

Windows 10 版本 1803 之前的旧版 Windows 上提供已检查的版本。 使用驱动程序验证器和 GFlags 等工具在更高版本的 Windows 中检查驱动程序代码。

操作系统的已检查版本使用各种方法通知你它发现的问题。 这些方法包括 ASSERT 失败、断点和调试器消息。 所有这些方法都会导致内核调试器的输出。 因此,若要有用,必须使用已连接的内核模式调试器 ((如 WinDbg 或 KD) )运行已检查的版本。

有关调试的详细信息,请参阅 Windows 调试

ASSERT 失败

检查的生成执行的大多数检查都是作为 ASSERT 语句实现的。 当断言的表达式的计算结果为 FALSE 时,调试器将显示一条消息,其中包含:

  • 失败的代码表达式的文本

  • 失败的 ASSERT 例程的源代码路径、文件名和行号

以下示例显示当 I/O 管理器中的断言失败时调试器显示的输出:

*** Assertion failed: Irp->IoStatus.Status != 0xffffffff
***   Source File: D:\nt\private\ntos\io\iosubs.c, line 3305

0:Break, Ignore, Terminate Process or Terminate Thread (bipt)? b
0:Execute '!cxr BD94B918' to dump context
Break instruction exception - code 80000003 (first chance)
ntkrnlmp!DbgBreakPoint:
804a3ce4 cc               int     3

如调试器输出中所示,系统会要求用户“中断、忽略、终止进程或终止线程”。用户通过输入“b”来回答,这导致调试器停止系统执行并出现断点。 因此,用户现在可以继续调试已发现的问题。

失败的断言影响系统的方式取决于许多因素。 在 Windows Vista 之前的 Windows 版本中,如果在系统启动过程中为操作系统启用了调试,则如果连接) ,系统将中断调试器 (,或者挂起,等待调试器连接。 如果未启用调试,则系统将崩溃,0x1E (KMODE_EXCEPTION_NOT_HANDLED) 的 Bug 检查 ,参数 1 值为 0x80000003。 在 Windows Vista 及更高版本中,仅当调试器已连接时,系统才会中断调试器。 如果未启用调试,或者启用调试但调试器未连接,则不会报告失败的断言 (尽管断言检查仍将) 执行。 如果要开发驱动程序,并希望在启用调试但调试器未连接的情况下确定性地中断调试器,则可以在代码中使用 DbgBreakPoint 语句。

某些 ASSERT 失败之前会附加 DbgPrint 输出。 此类型断言的一个常见示例是以下 PAGED_CODE 宏,该宏在 ntddk.h 和 wdm.h 中定义,用于驱动程序的已检查版本:

#define PAGED_CODE() \
    if (KeGetCurrentIrql() > APC_LEVEL) { \
KdPrint(( "EX: Pageable code called at IRQL %d\n", KeGetCurrentIrql() )); \
        ASSERT(FALSE); \
        }

此宏经常在操作系统中用于验证是否仅在适当的 IDL 上调用可分页函数。

通常可以检查失败的代码表达式的文本以确定断言的原因,包括发生 ASSERT 时的驱动程序操作以及调用的函数。 KB (显示堆栈跟踪) 调试器命令对于此分析至关重要。

有关最常见的 ASSERT 调用的列表,请参阅 Checked Build ASSERT

断点

已检查的生成还可以使用断点来指示问题。 断点前面通常有 DbgPrint 语句,这会导致调试器显示有关已遇到的问题的信息。 如果在出现断点时调试器未连接到系统,则系统崩溃,所有解释性消息都将丢失。

检查的生成断点 和消息中列出了在检查的生成断点之前以及驱动程序编写器遇到的一些最常见的消息。

以下示例演示 DbgPrint 调用和断点在调试器中的显示方式:

*** DPC routine > 1 sec --- This is not a break in KeUpdateSystemTime
Break instruction exception - code 80000003 (first chance)
NTOSKRNL!DbgBreakPoint:
804a3ce4 cc               int     3

此示例显示了一条调试器消息,该消息指示 DPC) 运行时间超过 1 秒 (单个延迟的过程调用,并说明了在已检查的生成中实现的检查类型。 此断点意味着驱动程序在 DPC 例程中花费了很长时间,这可能表示驱动程序存在严重的问题。 另一方面,如果驱动程序在其 DPC 例程中生成大量调试输出,从而延长 DPC 运行所需的时间长度,则也会发生此断点。 若要发现问题的根本原因,请检查 DPC 例程正在执行的操作,并继续一次或两次通过断点。

在遇到断点但未显示任何消息的极少数情况下,请务必使用 KB 调试器命令) 检查内核堆栈跟踪 (以确定遇到断点的位置。 通常,这将为断点的原因提供强有力的线索。

以下示例显示了许多 Windows 驱动程序开发人员已看到的常见断点,以及堆栈跟踪:

Break instruction exception - code 80000003 (first chance)
ntkrnlmp!SpinLockSpinningForTooLong:
8069aafd cc               int     3
1: kd> kb
ChildEBP RetAddr  Args to Child              
f9f77c4c 80103a6c f9f77c84 00000005 fa20e7df ntkrnlmp!SpinLockSpinningForTooLong
f9f77c58 fa20e7df 81b34930 e1558bd0 00180016 halmps!KfAcquireSpinLock+0x3c
f9f77c7c 80742683 000000ff 81ba6000 00000000 nothing+0x7df
f9f77d54 80742893 0000046c 81ba6000 f9f77d80 ntkrnlmp!IopLoadDriver+0x785
f9f77d78 805f8d2d 00000000 00000000 81fa88b8 ntkrnlmp!IopLoadUnloadDriver+0x75
f9f77dac 807cd14f f7d59ce8 00000000 00000000 ntkrnlmp!ExpWorkerThread+0x129
f9f77ddc 8069bece 805f8c04 00000001 00000000 ntkrnlmp!PspSystemThreadStartup+0x4d
00000000 00000000 00000000 00000000 00000000 ntkrnlmp!KiThreadStartup+0x16

从此堆栈跟踪中可以看到,断点是调用 KfAcquireSpinLock 的结果。 检查 wdm.h 后,可以看到这是驱动程序称为 KeAcquireSpinLock 的函数的实际名称。 即使断点之前未显示任何消息,你仍可以看到堆栈顶部的断点位置 (ntkrnlmp!SpinLockSpinningForTooLong) 。 此位置指示断点的原因:自旋锁已旋转,等待获取,时间异常长。

调试器消息

已检查的生成还可以使用调试器消息来识别或提供有关已发生的错误的附加信息。 调试器消息的最常见来源是来自非内核和非 HAL 组件的错误,或来自已检查 (调试) 用户模式程序的错误。 输出量因操作系统版本而异。

以下示例显示了安装完全检查的内部版本可能显示的输出。 此输出由 Windows XP 的预发布版本生成,因此比在完全检查的版本上通常看到的更庞大。

0:Attempting to load winsock
0:Checking for presence of ws2_32
0:Looking in ws2_32 for getaddrinfo
0:AudioSrv: 1:CreateSessionUserSid: GetCurrentUserTokenW failed, LastError=1245
1:AudioSrv: RegOpenConsoleUser: no console sid
0:(s: 0 0xc4.d0 winlogon.exe) USRK-[Wrn=170] CloseDesktop: Desktop 0X81CEAF78 still in use by thread 0XE16F3EA0
0:GetWinStationUserToken: Error 1702 getting UserToken LogonId 0
1:AudioSrv: InitializeForNewConsoleUser: User SID S-1-5-21-329068152-1292428093-1547161642-500
1:(s: 0 0x240.3a8 spoolsv.exe) USRK-[Wrn=1400] ValidateHwnd: Invalid hwnd (0X0000FFFF)
0:(s: 0 0x240.3a8 spoolsv.exe) USRK-[Wrn=1400] ValidateHwnd: Invalid hwnd (0X0000FFFF)
0:bReadUserSystemEUDCRegistry():fail NtStatus - c0000000
0:GDI: GDISRV:Fail to read system wide eudc
0:
1:TERMSRV : Not Personal Workstation
0:(s: 0 0x260.3c4 Explorer.EXE) USER-[Wrn=1400] HMValidateHandle: Invalid:00000000 Type:0x1
0:(s: 0 0x260.3c4 Explorer.EXE) USER-[Wrn=1400] HMValidateHandle: Invalid:00000000 Type:0x1
0:(s: 0 0x260.3c4 Explorer.EXE) USER-[Wrn=1400] HMValidateHandle: Invalid:00000000 Type:0x1

检查的生成 断点和消息中列出了一些在驱动程序调试期间检查的版本可以显示的最常见消息。

在各种系统组件中启用其他跟踪或信息性消息也可能导致调试器消息没有后续断点或 ASSERT 失败。