!deadlock
!deadlock 扩展显示驱动程序验证程序的死锁检测选项收集的死锁相关信息。
!deadlock
!deadlock 1
DLL
Kdexts.dll
其他信息
有关驱动程序验证程序的信息,请参阅 Windows 驱动程序工具包 (WDK) 文档。
注解
仅当驱动程序验证程序的死锁检测选项检测到锁层次结构冲突并发出 Bug 检查 0xC4 (DRIVER_VERIFIER_DETECTED_VIOLATION) 时,此扩展才会提供有用的信息。
在没有任何参数的情况下,!deadlock 扩展会导致显示基本锁层次结构拓扑。 如果问题不是简单的循环死锁,此命令将描述发生的情况。
!deadlock 1 扩展会显示堆栈跟踪。 显示的堆栈将是获取锁时处于活动状态的堆栈。
以下是示例:
0:kd> !deadlock
Deadlock detected (2 resources in 2 threads):
Thread 0: A B
Thread 1: B A
Where:
Thread 0 = 8d3ba030
Thread 1 = 8d15c030
Lock A = bba2af30 Type 'Spinlock'
Lock B = dummy!GlobalLock Type 'Spinlock'
这会告诉您涉及到哪些线程和哪些锁。 但是,这只是一个摘要,可能没有足够的信息来充分调试情况。
使用 !deadlock 1 输出在获取参与死锁的每个锁时调用堆栈的内容。 由于这些是运行时堆栈跟踪,因此在使用已检查的版本时,它们将更加完整。 Windows 10 版本 1803 之前的旧版 Windows 上提供已检查的版本。 在免费版本中,它们可能会在一行之后被截断。
0:kd> !deadlock 1
Deadlock detected (2 resources in 2 threads):
Thread 0 (8D14F750) took locks in the following order:
Lock A -- b7906f30 (Spinlock)
Stack: dummy!DummyActivateVcComplete+0x63
dummy!dummyOpenVcChannels+0x2E1
dummy!DummyAllocateRecvBufferComplete+0x436
dummy!DummyAllocateComplete+0x55
NDIS!ndisMQueuedAllocateSharedHandler+0xC9
NDIS!ndisWorkerThread+0xEE
Lock B -- dummy!GlobalLock (Spinlock)
Stack: dummy!dummyQueueRecvBuffers+0x2D
dummy!DummyActivateVcComplete+0x90
dummy!dummyOpenVcChannels+0x2E1
dummy!DummyAllocateRecvBufferComplete+0x436
dummy!DummyAllocateComplete+0x55
Thread 1 (8D903030) took locks in the following order:
Lock B -- dummy!GlobalLock (Spinlock)
Stack: dummy!dummyRxInterruptOnCompletion+0x25D
dummy!DummyHandleInterrupt+0x32F
NDIS!ndisMDpcX+0x3C
ntkrnlpa!KiRetireDpcList+0x5D
Lock A -- b7906f30 (Spinlock)
Stack: << Current stack >>
利用此信息,除了当前堆栈之外,您几乎拥有了所需的一切:
0: kd> k
ChildEBP RetAddr
f78aae6c 80664c58 ntkrnlpa!DbgBreakPoint
f78aae74 8066523f ntkrnlpa!ViDeadlockReportIssue+0x2f
f78aae9c 806665df ntkrnlpa!ViDeadlockAnalyze+0x253
f78aaee8 8065d944 ntkrnlpa!VfDeadlockAcquireResource+0x20b
f78aaf08 bfd6df46 ntkrnlpa!VerifierKeAcquireSpinLockAtDpcLevel+0x44
f78aafa4 b1bf2d2d dummy!dummyRxInterruptOnCompletion+0x2b5
f78aafc4 bfde9d8c dummy!DummyHandleInterrupt+0x32f
f78aafd8 804b393b NDIS!ndisMDpcX+0x3c
f78aaff4 804b922b ntkrnlpa!KiRetireDpcList+0x5d
从中可以看出涉及哪些锁及其获取位置。 这些信息应该足以让您调试死锁。 如果有源代码,可以使用调试器查看问题发生的具体位置:
0: kd> .lines
Line number information will be loaded
0: kd> u dummy!DummyActivateVcComplete+0x63 l1
dummy!DummyActivateVcComplete+63 [d:\nt\drivers\dummy\vc.c @ 2711]:
b1bfe6c9 837d0c00 cmp dword ptr [ebp+0xc],0x0
0: kd> u dummy!dummyQueueRecvBuffers+0x2D l1
dummy!dummyQueueRecvBuffers+2d [d:\nt\drivers\dummy\receive.c @ 2894]:
b1bf4e39 807d0c01 cmp byte ptr [ebp+0xc],0x1
0: kd> u dummy!dummyRxInterruptOnCompletion+0x25D l1
dummy!dummyRxInterruptOnCompletion+25d [d:\nt\drivers\dummy\receive.c @ 1424]:
b1bf5d05 85f6 test esi,esi
0: kd> u dummy!dummyRxInterruptOnCompletion+0x2b5 l1
dummy!dummyRxInterruptOnCompletion+2b5 [d:\nt\drivers\dummy\receive.c @ 1441]:
b1bf5d5d 8b4648 mov eax,[esi+0x48]
现在,您知道了源文件的名称和获取位置的行号。 在这种情况下,源文件将如下显示线程的行为:
线程 1:DummyActivateVcComplete 获取了虚拟微型端口锁。 然后,它调用 dummyQueueRecvBuffers,获取了虚拟全局锁。
线程 2:dummyRxInterruptOnCompletion 获取了全局锁。 然后,几行后,它获取了微型端口锁。
此时,死锁变得完全清晰。