!deadlock

A extensão !deadlock exibe informações sobre deadlocks coletados pela opção Detecção de Deadlock do Verificador de Driver.

!deadlock 
!deadlock 1

DLL

Kdexts.dll

Informações Adicionais

Para saber mais sobre o Verificador de Driver, confira a documentação do WDK (Kit de Driver do Windows).

Comentários

Essa extensão só fornecerá informações úteis se a opção Detecção de Deadlock do Verificador de Driver detectar uma violação de hierarquia de bloqueio e emitir bug check 0xC4 (DRIVER_VERIFIER_DETECTED_VIOLATION).

Sem argumentos, a extensão !deadlock leva à exibição da topologia básica da hierarquia de bloqueio. Se o problema não for um simples deadlock cíclico, este comando descreverá a situação que ocorreu.

A extensão !deadlock 1 leva à exibição de rastreamentos de pilha. As pilhas exibidas serão as ativas no momento em que os bloqueios foram adquiridos.

Este é um exemplo:

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'

Isso informa os threads e os bloqueios que estão envolvidos. Mas, isso visa ser um resumo e pode não ser suficiente para depurar a situação da forma adequada.

Use !deadlock 1 para imprimir o conteúdo das pilhas de chamadas no momento de aquisição de cada bloqueio que participa do deadlock. Como são rastreamentos de pilha em tempo de execução, eles serão mais completos com o uso de uma compilação verificada. As compilações verificadas estavam disponíveis em versões anteriores do Windows antes do Windows 10, versão 1803. Em uma compilação livre, elas podem ser truncadas após uma linha.

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 >>

Com essas informações, você tem quase tudo o que é necessário, exceto a pilha atual:

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

A partir disso, é possível ver quais bloqueios estavam envolvidos e onde foram adquiridos. Essas informações devem ser suficientes para você depurar o deadlock. Se o código-fonte estiver disponível, você poderá usar o depurador para ver exatamente onde ocorreu o problema:

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]

Agora você sabe o nome do arquivo de origem e o número da linha onde ocorreu a aquisição. Nesse caso, os arquivos de origem mostrarão que os threads se comportaram desta maneira:

  • Thread 1: DummyActivateVcComplete obteve o bloqueio de miniporta fictícia. Depois, chamou dummyQueueRecvBuffers, que obteve o bloqueio global de fictícia.

  • Thread 2: dummyRxInterruptOnCompletion obteve o bloqueio global. Algumas linhas depois, obteve o bloqueio de miniporta.

Neste ponto, o deadlock fica bem claro.