Compartilhar via


Application Verifier -Depurando paradas do Application Verifier

Instalação e configuração do depurador

Algumas ações do Verificador de Aplicativos podem resultar na geração de uma exceção. O depurador deve ser configurado para capturar essas exceções na segunda chance, pois o próprio Verificador de Aplicativo tratará as exceções de primeira chance.

As exceções geradas são de três tipos:

  • Uma exceção de violação de acesso (0xC0000005) será gerada se a opção do heap detectar um transbordamento de buffer do heap. Em alguns casos, a opção "Verificar uso do caminho do sistema" pode também causar uma violação de acesso.

  • Uma exceção de identificador inválida (0xC0000008) é gerada quando a opção Detectar uso de identificador inválido detecta uma operação de identificador inválida.

  • Uma exceção de estouro de pilha (0xC00000FD) é gerada quando a opção Verificar a pilha adequada detecta que a pilha inicial era muito curta.

Uma maneira de se preparar para esses eventos é iniciar o depurador em uma linha de comando da seguinte maneira:

windbg -xd av -xd ch -xd sov ApplicationCommandLine

or

cdb -xd av -xd ch -xd sov ApplicationCommandLine

Se você já tiver iniciado o depurador, poderá usar o comando sxd (Definir Exceções) para capturar todas as violações de acesso, identificadores inválidos e estouros de pilha como exceções de segunda chance:

0:000> sxd av 

0:000> sxd ch 

0:000> sxd sov 1

Teoricamente, é possível controlar o Verificador de Aplicativos por meio de um depurador de kernel. No entanto, isso não é recomendado – requer o uso frequente dos comandos .process e .pagein, mas não oferece mais vantagem do que usar um depurador em modo de usuário.

Instalando as ferramentas de depuração

Para baixar a versão mais recente das ferramentas, consulte Baixar Ferramentas de Depuração para Windows.

Configurando o hardware para depuração de User-Mode

A depuração no modo de usuário geralmente é feita em um único computador: o depurador é executado no mesmo computador que o aplicativo que falhou.

Nesse caso, nenhuma configuração de hardware específica é necessária. Ao longo deste tópico, os termos computador host e computador de destino são intercambiáveis neste caso.

Configurando o software para depuração de User-Mode

Configuração básica de User-Mode – antes de iniciar a depuração no modo de usuário, você deve baixar os arquivos de símbolo necessários e definir determinadas variáveis de ambiente.

Arquivos de símbolo

Você deve baixar os arquivos de símbolo para o processo de modo de usuário que está sendo depurado. Se este for um aplicativo que você escreveu, ele deverá ser criado com arquivos de símbolo completos. Se for um aplicativo comercial, os arquivos de símbolo poderão estar disponíveis em um servidor Web ou para download, entre em contato com o fabricante.

se você estiver executando a depuração remota, o local do arquivo de símbolo dependerá do método que você está usando:

  • Se você estiver executando a depuração remota por meio do depurador, os arquivos de símbolo deverão estar no computador com o servidor de depuração.

  • Se você estiver executando a depuração remota por meio de remote.exe, os arquivos de símbolo deverão estar no computador com o depurador.

  • Se você estiver executando a depuração remota por meio de um servidor de processo ou um servidor de conexão KD, os arquivos de símbolo deverão estar no computador com o cliente inteligente.

  • Se você estiver controlando o depurador de modo de usuário a partir do depurador de kernel, os arquivos de símbolo precisam estar em ambos os computadores.

Configurando variáveis de ambiente

O depurador usa uma variedade de variáveis de ambiente para indicar várias configurações importantes.

Para obter mais informações sobre depuradores, consulte Introdução à Depuração do Windows

Configurando o Verificador de Aplicativos com o Depurador usando a linha de comando

Para configurar o Verificador de Aplicativos, você pode usar a linha de comando CDB ou NTSD.

Use a seguinte linha de comando:

cdb OtherOptions -vf:Flags Target

Onde Destino é o nome do aplicativo de destino e Sinalizadores especifica as opções desejadas do Verificador de Aplicativo que devem ser aplicadas a esse destino.

Os sinalizadores devem ser uma soma dos bits que representam as opções desejadas. Os valores de bits individuais são os seguintes:

Valor de sinalizador Significado
00000001 VERIFICAÇÕES DE HEAP
00000004 MANIPULAR VERIFICAÇÕES
00000008 VERIFICAÇÕES DE SIM PARA BAIXOS RECURSOS
00000020 VERIFICAÇÕES DE TLS
00000040 PILHAS SUJAS
00000200 APIS PERIGOSAS
00001000 VERIFICAÇÕES DE EXCEÇÃO
00002000 VERIFICAÇÕES DE MEMÓRIA
00020000 VERIFICAÇÕES DIVERSAS
00040000 VERIFICAÇÕES DE BLOQUEIO

Depuração com !avrf

A extensão !avrf controla as configurações do Verificador de Aplicativos e exibe uma variedade de saídas produzidas pelo Verificador de Aplicativo. Para obter informações adicionais sobre a extensão !arvrf, consulte !avrf nos documentos do depurador.

Sintaxe

!avrf

O comando !avrf sem parâmetros mostra as configurações do Application Verifier e informações sobre as quebras atuais e anteriores do Application Verifier, se houver.

!avrf –vs { Length | -aAddress }

Exibe o log de operações de espaço virtual. O comprimento especifica o número de registros a serem exibidos a partir do mais recente. O endereço especifica o endereço virtual. Os registros das operações virtuais que contêm esse endereço virtual serão exibidos.

!avrf -hp { Length | -a Address }

Exibe o log de operações de heap. O endereço especifica o endereço do heap. Os registros das operações de heap que contêm esse endereço do heap serão exibidos.

!avrf -cs { Length | -a Address }

Exibe o log de exclusão de seção crítica. O comprimento especifica o número de registros a serem exibidos a partir do mais recente. O endereço especifica o endereço da seção crítica. Os registros da seção crítica específica são exibidos quando o endereço é especificado.

!avrf -dlls [ Length ]

Exibe o log de carregamento/descarregamento de DLL. O comprimento especifica o número de registros a serem exibidos a partir do mais recente.

!avrf -trm

Exibe um log de todos os threads encerrados e suspensos.

!avrf -ex [ Length ]

Exibe o log de exceções. O Verificador de Aplicativos rastreia todas as exceções que ocorrem no aplicativo.

!avrf -threads [ ThreadID ]

Exibe informações sobre threads no processo de destino. Para as threads filhas, o tamanho da pilha e os sinalizadores CreateThread especificados pelo pai também são exibidos. Fornecer uma ID de thread exibirá informações somente para esse thread específico.

!avrf -tp [ ThreadID ]

Exibe o log do pool de threads. Esse log pode conter rastreamentos de pilha para várias operações, como alterar a máscara de afinidade de thread, alterar a prioridade do thread, postar mensagens de thread, inicializar COM e não inicializar COM de dentro do retorno de chamada do pool de threads. Fornecer uma ID de thread exibirá informações somente para esse thread específico.

!avrf -srw [ Address | Address Length ] [ -stats ]

Exibe o log do SRW (Leitor/Gravador Fino). Ao especificar o endereço, mostrará registros pertencentes a esse endereço de bloqueio SRW. Quando o Comprimento é especificado junto com o Endereço, todos os bloqueios SRW dentro desse intervalo de endereços são exibidos. A opção -stats despeja as estatísticas do bloqueio de SRW.

!avrf -leak [ -m ModuleName ] [ -r ResourceType ] [ -a Address ] [ -t ]

Exibe o log de recursos pendentes. Esses recursos podem ou não ser vazamentos em um determinado ponto. Especificar ModuleName (incluindo a extensão) exibe todos os recursos pendentes no módulo especificado. Especificar ResourceType exibe recursos destacados desse tipo específico de recurso. Especificando despejos de endereço de registros de recursos associados com esse endereço. ResourceType pode ser um dos seguintes:

  • Heap: exibe alocações de heap usando APIs de Heap do Win32
  • Local: exibe alocações locais/globais
  • CRT: exibe alocações usando APIs CRT
  • Virtual: exibe reservas virtuais
  • BSTR: Exibe alocações BSTR
  • Registro: Exibe a chave do Registro aberta
  • Energia: exibe objetos de notificação de energia
  • Handle: exibe alocações de thread, arquivo e handle de evento

!avrf –trace TraceIndex

Exibe um rastreamento de pilha para o índice de rastreamento especificado. Algumas estruturas usam esse número de índice de 16 bits para identificar um rastreamento de pilha. Esse índice aponta para um local dentro do banco de dados de rastreamento de stack. Se você estiver analisando essa estrutura, achará essa sintaxe útil.

!avrf -cnt

Exibe uma lista de contadores globais.

!avrf -brk [ BreakEventType ]

Especifica que esse é um comando de evento de interrupção. Quando !avrf -brk é usado sem parâmetros adicionais, as configurações de evento de interrupção são exibidas. BreakEventType especifica o número de tipo do evento de interrupção. Para obter uma lista de tipos possíveis, use !avrf -brk.

!avrf -flt [ EventTypeProbability ]

Especifica que esse é um comando de injeção de falha. Quando !avrf -flt é usado sem parâmetros adicionais, as configurações atuais de injeção de falha são exibidas. EventType especifica o número de tipo do evento. A probabilidade especifica a frequência com que o evento falhará. Isso pode ser qualquer inteiro entre 0 e 1.000.000 (0xF4240).

!avrf -flt break EventType

Faz com que o Verificador de Aplicativos invada o depurador sempre que essa falha é injetada.

!avrf -flt stacks Length

Exibe o número de comprimento de rastreamentos de pilha para as operações injetadas por falha mais recentes.

!avrf -trg [ StartEnd | dll Module | all ]

Especifica que esse é um comando de intervalo de destino. Quando -trg é usado sem parâmetros adicionais, os intervalos de destino atuais são exibidos. Start especifica o endereço inicial do intervalo de destino ou intervalo de exclusão. O fim especifica o endereço final do intervalo de destino ou do intervalo de exclusão. O módulo especifica o nome de um módulo a ser direcionado ou excluído. O módulo deve incluir o nome completo do módulo, incluindo a extensão .exe ou .dll. As informações do caminho não devem ser incluídas. Especificar tudo faz com que todos os intervalos de destino ou intervalos de exclusão sejam redefinidos.

!avrf -skp [ StartEnd | dll Module | all | Time ]

Especifica que esse é um comando de intervalo de exclusão. Start especifica o endereço inicial do intervalo de destino ou intervalo de exclusão. O fim especifica o endereço final do intervalo de destino ou do intervalo de exclusão. O módulo especifica o nome de um módulo a ser direcionado ou excluído. O módulo deve incluir o nome completo do módulo, incluindo a extensão .exe ou .dll. As informações do caminho não devem ser incluídas. Especificar tudo faz com que todos os intervalos de destino ou intervalos de exclusão sejam redefinidos. A especificação de Tempo faz com que todas as falhas sejam suprimidas para milissegundos de tempo após a retomada da execução.

Veja a seguir a saída fornecida pelo comando !avrf no depurador.

0:000> !avrf
Application verifier settings (816431A7):

   - full page heap
   - COM
   - RPC
   - Handles
   - Locks
   - Memory
   - TLS
   - Exceptions
   - Threadpool
   - Leak
   - SRWLock

No verifier stop active.

Note: Sometimes bugs found by verifier manifest themselves as raised
exceptions (access violations, stack overflows, invalid handles), 
and it is not always necessary to have a verifier stop.

Comentários sobre a extensão !avrf

Quando a extensão !avrf é usada sem parâmetros, ela exibe as opções atuais do Verificador de Aplicativo.

A extensão !avrf usa o Exts.dll no depurador.

Se uma Parada do Verificador de Aplicativo tiver ocorrido, a extensão !avrf sem parâmetros revelará a natureza da parada e o que a causou.

Se os símbolos para ntdll.dll e verifier.dll estiverem ausentes, a extensão !avrf gerará uma mensagem de erro.

Paradas contínuas e não contínuas

Depurando uma parada contínua

Aqui está um exemplo de uma exceção de identificador inválida que foi gerada pela opção Detectar uso de identificador inválido.

Primeiro, a seguinte mensagem é exibida:

Invalid handle - code c0000008 (first chance)

===================================================

VERIFIER STOP 00000300: pid 0x558: invalid handle exception for current stack trace

        C0000008 : Exception code.

        0012FBF8 : Exception record. Use .exr to display it.

        0012FC0C : Context record. Use .cxr to display it.

        00000000 :

===================================================

This verifier stop is continuable.

After debugging it use 'go' to continue.

===================================================

Break instruction exception - code 80000003 (first chance)

eax=00000000 ebx=6a27c280 ecx=6a226447 edx=0012fa4c esi=00942528 edi=6a27c260

eip=6a22629c esp=0012facc ebp=0012faf0 iopl=0         nv up ei pl zr na po nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

ntdll!DbgBreakPoint:

6a22629c cc               int     3

Observe que a mensagem informa que essa Parada do Verificador de Aplicativos pode continuar. Depois de entender o que ocorreu, você pode continuar executando o aplicativo de destino.

Primeiro, você deve usar a extensão !avrf. Isso fornece informações sobre a falha atual:

0:000> !avrf

Global flags: 00000100

Application verifier global flag is set.

Application verifier settings (00000004):

   - no heap checking enabled!

   - handle checks

Page heap is not active for this process.

Current stop 00000300 : c0000008 0012fbf8 0012fc0c 00000000 .

    Using an invalid handle (either closed or simply bad).

A linha final dessa exibição resume o problema.

Talvez você queira examinar alguns logs neste momento. Depois de terminar, use o comando g (Go) para iniciar o aplicativo novamente:

0:000> g

## Debugging a Non-Continuable Stop

Here is an example of an access violation that has been raised by the page heap option.

First, the following message appears:

Access violation - code c0000005 (first chance)

===================================================

VERIFIER STOP 00000008: pid 0x504: exception raised while verifying block header

        00EC1000 : Heap handle

        00F10FF8 : Heap block

        00000000 : Block size

        00000000 :

===================================================

This verifier stop is not continuable. Process will be terminated when you use the 'go' debugger command.

===================================================

Break instruction exception - code 80000003 (first chance)

eax=00000000 ebx=00000000 ecx=6a226447 edx=0012fab7 esi=00f10ff8 edi=00000008

eip=6a22629c esp=0012fb5c ebp=0012fb80 iopl=0         nv up ei pl zr na po nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

ntdll!DbgBreakPoint:

6a22629c cc               int     3

Nesse caso, a mensagem informa que esta Parada do Verificador de Aplicativos não pode ser continuada. O erro é muito grave para que o processo continue em execução e não há como o Verificador de Aplicativos salvar o processo.

A extensão !avrf pode ser usada para fornecer informações sobre a falha atual:

0:000> !avrf

Global flags: 02000100

Application verifier global flag is set.

Page heap global flag is set.

Application verifier settings (00000001):

   - full page heap

Page heaps active in the process (format: pageheap, lightheap, flags):

    00941000 , 00a40000 , 3 (pageheap traces )

    00b41000 , 00c40000 , 3 (pageheap traces )

    00cb1000 , 00db0000 , 3 (pageheap traces )

    00ec1000 , 00fc0000 , 3 (pageheap traces )

Current stop 00000008 : 00ec1000 00f10ff8 00000000 00000000 .

    Corrupted heap block.

A linha final dessa exibição resume o problema.

Talvez você também queira examinar alguns logs neste momento. Talvez você queira usar o comando .restart (Reiniciar Aplicativo de Destino) neste momento. Ou talvez você prefira encerrar a sessão do Verificador de Aplicativos e começar a corrigir os bugs em seu código.

Depurando erros em seções críticas

Extensão do depurador !cs

!cs podem ser usados no depurador do modo de usuário e no depurador de kernel para exibir informações sobre seções críticas no processo atual. Para obter informações adicionais sobre a extensão !cs, consulte !cs nos documentos do depurador.

Símbolos correspondentes com informações de tipo são necessários, especialmente para ntdll.dll.

A sintaxe dessa extensão é:

!cs [-s] – despejar todas as seções críticas ativas no processo atual.

!cs [-s] endereço – exibir seção crítica neste endereço.

!cs [-s] -d endereço - gerar um despejo da seção crítica correspondente ao DebugInfo neste endereço.

-s exibirá o rastreamento de pilha de inicialização de seção crítica, se disponível.

Exemplos:

Despejar informações sobre uma seção crítica usando seu endereço

0:001> ! cs 0x7803B0F8

Critical section   = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo          = 0x6A262080
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0

Despejar informações sobre uma seção crítica usando seu endereço, incluindo o rastreamento de pilha de inicialização

0:001> !cs -s 0x7803B0F8

Critical section   = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo          = 0x6A262080
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0

Stack trace for DebugInfo = 0x6A262080:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5

Despejar informações sobre uma seção crítica usando seu endereço de informações de depuração

0:001> !cs -d 0x6A262080

DebugInfo          = 0x6A262080
Critical section   = 0x7803B0F8 (MSVCRT!__app_type+0x4)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0

Exibir informações sobre uma seção crítica usando seu endereço de informações de depuração, incluindo a trilha de chamada de inicialização.

0:001> !cs -s -d 0x6A262080

DebugInfo          = 0x6A262080
Critical section   = 0x7803B0F8 (MSVCRT!__app_type+0x4)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0
Stack trace for DebugInfo = 0x6A262080:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE

Despejar informações sobre todas as seções críticas ativas no processo atual

0:001> !cs

-----------------------------------------

DebugInfo          = 0x6A261D60
Critical section   = 0x6A262820 (ntdll!RtlCriticalSectionLock+0x0)
LOCKED
LockCount          = 0x0
OwningThread       = 0x460
RecursionCount     = 0x1
LockSemaphore      = 0x0
SpinCount          = 0x0
-----------------------------------------

DebugInfo          = 0x6A261D80
Critical section   = 0x6A262580 (ntdll!DeferedCriticalSection+0x0)
NOT LOCKED
LockSemaphore      = 0x7FC
SpinCount          = 0x0
-----------------------------------------

DebugInfo          = 0x6A262600
Critical section   = 0x6A26074C (ntdll!LoaderLock+0x0)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0
.....

Exibir informações sobre todas as seções críticas ativas no processo atual, incluindo o rastreamento de pilha de inicialização.


0:001> !cs -s

...

-----------------------------------------

DebugInfo          = 0x6A261EA0
Critical section   = 0xA8001C (+0xA8001C)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0
No stack trace saved

-----------------------------------------

DebugInfo          = 0x6A261EC0
Critical section   = 0x6A263560 (ntdll!RtlpDphTargetDllsLock+0x0)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0
No stack trace saved

-----------------------------------------

DebugInfo          = 0x6A261EE0
Critical section   = 0xA90608 (+0xA90608)
NOT LOCKED
LockSemaphore      = 0x7EC
SpinCount          = 0x0
Stack trace for DebugInfo = 0x6A261EE0:

0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A20B0DC: ntdll!CsrpConnectToServer+0x1BE
0x6A20B2AA: ntdll!CsrClientConnectToServer+0x148
0x77DBE83F: KERNEL32!BaseDllInitialize+0x11F
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5

-----------------------------------------

DebugInfo          = 0x6A261F00
Critical section   = 0x77E1AEB8 (KERNEL32!BaseDllRegistryCache+0x18)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0
Stack trace for DebugInfo = 0x6A261F00:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5

Depurando erros de exceção

O log de exceções registra todas as exceções que ocorreram no processo de destino.

Você pode usar o comando de extensão !avrf -ex Length para exibir as últimas várias exceções; O comprimento especifica o número de exceções. Se o comprimento não for especificado, todas as exceções serão exibidas.

Este é um exemplo:

0:000> !avrf -ex 4

=================================

Thread ID: 0000052c
Exception code: c0000008
Exception address: 6a226663
Exception record: 0012fb50
Context record: 0012fb64

Displayed 1 exception log entries.

Depurando erros de identificadores

O !htrace pode ser usado no depurador de modo de usuário e de kernel para exibir informações de rastreamento de pilha de chamadas para um ou todos os manipuladores em um processo. Essas informações estarão disponíveis se o rastreamento de identificador estiver habilitado para o processo – habilitado automaticamente se a verificação de identificador estiver habilitada no verificador de aplicativos. Os rastreamentos de pilha são salvos sempre que o processo abre ou fecha um identificador, ou quando faz referência a um identificador inválido. Para obter informações adicionais sobre a extensão !htrace, consulte !htrace nos documentos do depurador.

A sintaxe do depurador de kernel para esta extensão é:

!htrace [ handle [process] ]

Se o identificador não for especificado ou for 0, as informações sobre todos os identificadores no processo serão exibidas. Se o processo não for especificado, o processo atual será usado.

A sintaxe do depurador no modo de usuário é:

!htrace [handle]

A extensão do depurador no modo de usuário sempre exibe informações sobre o processo de depuração atual.

Exemplos:

Gerar informações de despejo sobre o identificador 7CC no processo 815328b0

kd> !htrace 7CC 815328b0

Loaded \\...\kdexts extension DLL
Process 0x815328B0
ObjectTable 0xE15ECBB8

--------------------------------------

Handle 0x7CC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x77DBFCD6: KERNEL32!GetLocaleFileInfo+0x3D
0x77DBF942: KERNEL32!NlsProcessInitialize+0x11D
0x77E0C6DF: KERNEL32!NlsDllInitialize+0x35
0x6A20785C: ntdll!LdrpCallInitRoutine+0x14
0x6A205393: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DD80: ntdll!LdrpInitializeProcess+0xAF6

--------------------------------------

Handle 0x7CC - OPEN:

0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3180: ntoskrnl!ObpCreateHandle+0x304
0x801E1563: ntoskrnl!ObOpenObjectByName+0x1E9
0x77DBFCD6: KERNEL32!GetLocaleFileInfo+0x3D
0x77DBF942: KERNEL32!NlsProcessInitialize+0x11D
0x77E0C6DF: KERNEL32!NlsDllInitialize+0x35
0x6A20785C: ntdll!LdrpCallInitRoutine+0x14
0x6A205393: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DD80: ntdll!LdrpInitializeProcess+0xAF6

--------------------------------------

Parsed 0x1CA stack traces.
Dumped 0x2 stack traces.

Despejar informações sobre todos os identificadores no processo 815328b0

kd> !htrace 0 81400300

Process 0x81400300
ObjectTable 0xE10CCF60

--------------------------------------

Handle 0x7CC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Handle 0x7CC - OPEN:

0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE23B2: KERNEL32!CreateSemaphoreA+0x66
0x010011C5: badhandle!main+0x45
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Handle 0x7DC - BAD REFERENCE:

0x8018F709: ntoskrnl!ExMapHandleToPointerEx+0xEA
0x801E10F2: ntoskrnl!ObReferenceObjectByHandle+0x12C
0x801902BE: ntoskrnl!NtSetEvent+0x6C
0x80154965: ntoskrnl!_KiSystemService+0xC4
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Handle 0x7DC - CLOSE:

0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Handle 0x7DC - OPEN:

0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE265C: KERNEL32!CreateEventA+0x66
0x010011A0: badhandle!main+0x20
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Parsed 0x6 stack traces.

Dumped 0x5 stack traces.

Informações de despejo sobre o identificador 7DC no processo atual


kd> !htrace  7DC

Process 0x81400300

ObjectTable 0xE10CCF60

--------------------------------------

Handle 0x7DC - BAD REFERENCE:

0x8018F709: ntoskrnl!ExMapHandleToPointerEx+0xEA
0x801E10F2: ntoskrnl!ObReferenceObjectByHandle+0x12C
0x801902BE: ntoskrnl!NtSetEvent+0x6C
0x80154965: ntoskrnl!_KiSystemService+0xC4
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Handle 0x7DC - CLOSE:

0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Handle 0x7DC - OPEN:

0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE265C: KERNEL32!CreateEventA+0x66
0x010011A0: badhandle!main+0x20
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------

Parsed 0x6 stack traces.

Dumped 0x3 stack traces.

Depurando erros de heap

Extensão do depurador para verificação de heap

A extensão do depurador de verificação de heap faz parte da extensão !heap (extensão de depuração de heap do NT). Ajuda simples pode ser obtida com !heap -? ou mais abrangente utilizando !heap -p -? . A extensão atual não detecta sozinha se o heap de páginas está habilitado para um processo, nem age de acordo. Por enquanto, o usuário da extensão precisa saber que o heap de página está habilitado e que deve usar comandos com o prefixo !heap -p. Para obter informações adicionais sobre a extensão !htrace, consulte !heap nos documentos do depurador.

!heap -p

Exibe endereços de todos os heaps de página completa criados no processo.

!heap -p -h ADDRESS-OF-HEAP

Despejo completo de heap de página inteira em ADDRESS-OF-HEAP.

!heap -p -a ADDRESS

Tenta descobrir se há um bloco de heap no ADDRESS. Esse valor não precisa ser o endereço do início do bloco. O comando será útil se não houver nenhuma pista sobre a natureza de uma área de memória.

Log de operações de heap

O log de operações de heap rastreia todas as rotinas de heap. Eles incluem HeapAlloc, HeapReAlloc e HeapFree.

Você pode usar o !avrf -hp Length comando de extensão para exibir os últimos registros; O comprimento especifica o número de registros.

Você pode usar !avrf -hp -a Address para exibir todas as operações de espaço de heap que afetaram o Endereço especificado. Para uma operação de alocação, é suficiente que o Endereço esteja contido no bloco de heap alocado. Para uma operação livre, o endereço exato do início do bloco deve ser fornecido.

Para cada entrada no log, as seguintes informações são exibidas:

  • A função heap foi chamada.
  • O ID da thread que chamou a rotina.
  • O endereço envolvido na chamada – esse é o endereço que foi retornado por uma rotina de alocação ou que foi passado para uma rotina gratuita.
  • O tamanho da região envolvida na chamada.
  • O rastreamento de pilha da chamada.

As entradas mais recentes são exibidas primeiro.

Neste exemplo, as duas entradas mais recentes são exibidas:

0:001> !avrf -hp 2

alloc (tid: 0xFF4): 
address: 00ea2fd0 
size: 00001030
00403062: Prymes!_heap_alloc_dbg+0x1A2
00402e69: Prymes!_nh_malloc_dbg+0x19
00402e1e: Prymes!_malloc_dbg+0x1E
00404ff3: Prymes!_stbuf+0xC3
00401c23: Prymes!printf+0x43
00401109: Prymes!main+0xC9
00402039: Prymes!mainCRTStartup+0xE9
77e7a278: kernel32!BaseProcessStart+0x23

alloc (tid: 0xFF4): 
address: 00ea07d0 
size: 00000830
00403062: Prymes!_heap_alloc_dbg+0x1A2
00402e69: Prymes!_nh_malloc_dbg+0x19
00402e1e: Prymes!_malloc_dbg+0x1E
00403225: Prymes!_calloc_dbg+0x25
00401ad5: Prymes!__initstdio+0x45
00401f38: Prymes!_initterm+0x18
00401da1: Prymes!_cinit+0x21
00402014: Prymes!mainCRTStartup+0xC4

77e7a278: kernel32!BaseProcessStart+0x23

Cenários típicos de depuração

Há vários cenários de falha que podem ser encontrados. Alguns deles exigem bastante trabalho de detetive para ter uma visão completa.

Violação de acesso em página não acessível

Isso acontece quando o heap de página inteira é habilitado e o aplicativo testado acessa além do final do buffer. Isso também pode acontecer se tocar em um bloco liberado. Para entender qual é a natureza do endereço no qual a exceção ocorreu, você precisa usar:

!heap –p –a ADDRESS-OF-AV

Mensagem de bloco corrompida

Em vários momentos durante o tempo de vida de uma alocação (liberação pelo usuário, liberação real), o gerenciador de heap de página verifica se o bloco mantém todos os padrões de preenchimento intactos e se o cabeçalho do bloco tem dados consistentes. Se esse não for o caso, você receberá uma parada de verificador.

Se o bloco for um bloco de heap de página inteira (por exemplo, se você tiver certeza de que o heap de página inteira está habilitado para todas as alocações), você poderá usar "!heap –p –a ADDRESS" para identificar as características do bloco.

Se o bloco for um bloco de heap de páginas leves, você precisará descobrir o endereço inicial do cabeçalho do bloco. Você pode encontrar o endereço inicial despejando de 30 a 40 bytes abaixo do endereço relatado e procurar os padrões de início/término mágicos para um cabeçalho de bloco (ABCDAAAA, ABCDBBBB, ABCDAAA9, ABCDBBBA).

O cabeçalho fornecerá todas as informações necessárias para entender a falha. Particularmente, os padrões mágicos informarão se o bloco está alocado ou livre, se for um heap de páginas leves ou um bloco de heap de página inteira. As informações aqui devem ser correspondidas cuidadosamente com a chamada com problema.

Por exemplo, se uma chamada para HeapFree for feita com o endereço de um bloco mais quatro bytes, você receberá a mensagem corrompida. O cabeçalho do bloco parecerá correto, mas observe que o primeiro byte após o valor mágico 0xDCBAXXXX tem um endereço diferente do que está na chamada.

Ponteiros de preenchimento especiais

O gerenciador de heap de página preenche a alocação do usuário com valores que serão exibidos como ponteiros de kernel. Isso acontece quando o bloco é liberado (o valor de preenchimento é F0) e quando o bloco é alocado, mas nenhuma solicitação é feita para que o bloco seja zerado (o valor de preenchimento é E0 para heap de página leve e C0 para heap de página inteira). As alocações não zero são típicas para usuários malloc/novos. Se houver uma falha (violação de acesso) em que uma leitura/gravação é tentada em endereços como F0F0F0F0, E0E0E0E0, C0C0C0C0 provavelmente você atingiu um desses casos.

Uma leitura/gravação em F0F0F0F0 significa que um bloco foi usado após ter sido liberado. Infelizmente você vai precisar de algum trabalho de detetive para descobrir qual bloco causou isso. Você precisa obter o rastro de pilha da falha e, em seguida, inspecionar o código das funções na pilha. Um deles pode fazer uma suposição errada sobre uma alocação estar ativa.

Uma leitura/gravação em E0E0E0E0/C0C0C0C0 significa que o aplicativo não configurou adequadamente a alocação de memória. Isso também requer a inspeção de código das funções no registro de pilha atual. Aqui está um exemplo para esse tipo de falha. Em um processo de teste, uma violação de acesso ao fazer um HeapFree no endereço E0E0E0E0 foi notada. Aconteceu que o teste alocou uma estrutura, não a inicializou corretamente e, em seguida, chamou o destrutor do objeto. Como um determinado campo não era nulo (tinha E0E0E0E0), ele executou a exclusão do campo.

Detalhes técnicos da página heap

Para detectar corrupções de heap (estouros ou subfluxos), o AppVerifier modificará a maneira como a memória é alocada, adicionando preenchimento à memória solicitada com páginas totalmente não graváveis ou com marcas especiais posicionadas antes e depois da memória alocada. O AppVerifier faz isso carregando Verifier.dll no processo que está sendo verificado e redirecionando algumas das APIs do Heap Win32 chamadas pelo aplicativo para APIs de Verifier.dll correspondentes.

Ao preencher a memória solicitada com páginas totalmente não graváveis (quando a configuração FULL está habilitada na seção de propriedades do heap de página e é a configuração padrão), o AppVerifier consumirá uma grande quantidade de memória virtual, mas tem a vantagem de que os eventos de corrupção do heap são armazenados em cache em tempo real quando ocorre um estouro ou subfluxo. Lembre-se de que a memória nesse modo será semelhante a esta [AppVerifier Read-Only Página heap (4k)] [Quantidade de memória solicitada pelo Aplicativo em teste], ou assim [Quantidade de memória solicitada pelo Aplicativo em teste] [AppVerifier Read-Only Página heap (4k)].

A verificação de heap colocará uma página de proteção no início ou no final da alocação, dependendo da propriedade Backward. Se Backward estiver definido como False, que é o padrão, ele colocará uma página de proteção no final da alocação para capturar estouros de buffer. Se estiver definido como True, a página de proteção será colocada no início da alocação para capturar subutilizações de buffer.

Ao preencher a memória solicitada com marcas especiais (habilitada desmarcando o item de caixa de seleção "Completo" nas propriedades do heap), o AppVerifier verificará e alertará você quando essa memória for liberada. O principal problema ao usar essa técnica é que há alguns casos em que a corrupção de memória só será detectada quando a memória for liberada (a quantidade mínima de bloco de memória é de 8 bytes), portanto, quando ocorrer uma variável de 3 bytes ou um estouro de 5 bytes, ela não será detectada imediatamente.

Em um evento de subfluxo, será feita uma tentativa de gravar em uma página de Read-Only. Isso disparará uma exceção. Observe que essa exceção só poderá ser capturada se o aplicativo de destino estiver sendo executado em um depurador. Observe que o modo heap de página inteira também detectará esses erros porque ele usa páginas de preenchimento+proteção. O motivo pelo qual você usaria o heap de páginas leves é se o computador não puder tolerar as restrições de memória elevadas do heap de página inteira.

Para aplicativos com uso intensivo de memória ou quando for necessário usar o AppVerifier durante longos períodos de tempo (por exemplo, teste de estresse), é melhor executar testes de heap normais (leves) em vez de modo completo devido à degradação do desempenho. No entanto, quando você encontrar um problema, ative o heap de página inteira para investigar mais.

Os aplicativos que estão usando heaps personalizados (heaps que ignoram a implementação do heap do sistema operacional) podem não ter o benefício total de usar o heap de página ou podem até mesmo apresentar mau funcionamento quando ele estiver habilitado.

Depurando erros de memória

A extensão do depurador de memória

O log de operações de espaço virtual rastreia todas as rotinas que modificam o espaço virtual de um processo de qualquer forma. Elas incluem VirtualAlloc, VirtualFree, MapViewOfFile e UnmapViewOfFile.

Você pode usar o !avrf -vs Length comando de extensão para exibir os últimos registros; O comprimento especifica o número de registros.

Você pode usar !avrf -vs -a Address para exibir todas as operações de espaço virtual que afetaram o Endereço especificado. Para uma alocação, é suficiente que o Endereço esteja contido no bloco alocado. ** Para obter gratuitamente, é necessário fornecer o endereço exato do início da região.

Para cada entrada no log, as seguintes informações são exibidas:

  • A função chamada
  • O ID da thread que chamou a rotina
  • O endereço envolvido na chamada – esse é o endereço que foi retornado por uma rotina de alocação ou que foi passado para uma rotina gratuita
  • O tamanho da região envolvida na chamada
  • O tipo de operação de memória (o parâmetro AllocationType)
  • O tipo de proteção solicitada
  • O rastreamento de pilha da chamada

Exemplos

As entradas mais recentes são exibidas primeiro.

No exemplo a seguir, as duas entradas mais recentes são exibidas:

0:001> !avrf -vs 2

VirtualFree (tid: 0xB4): addr:04bb0000 sz:00400000 op:8000 prot:0
        00aa1ac2: verifier!VsLogCall+0x42
        00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
        68925d17: kernel32!VirtualFreeEx+0x35
        6892611c: kernel32!VirtualFree+0x13
        75ef6525: mshtml+0x116525
        75ef68af: mshtml+0x1168AF
        6a20787c: ntdll!LdrpCallInitRoutine+0x14
        6a211c6f: ntdll!LdrUnloadDll+0x39A
        689275c1: kernel32!FreeLibrary+0x3B
        77b22d69: ole32!CoQueryReleaseObject+0x1E6
        77b02bd2: ole32!SetErrorInfo+0x1ED

VirtualFree (tid: 0xB4): addr:04bb0000 sz:00001000 op:4000 prot:0

        00aa1ac2: verifier!VsLogCall+0x42
        00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
        68925d17: kernel32!VirtualFreeEx+0x35
        6892611c: kernel32!VirtualFree+0x13
        75ef65ae: mshtml+0x1165AE
        75ef68af: mshtml+0x1168AF
        6a20787c: ntdll!LdrpCallInitRoutine+0x14
        6a211c6f: ntdll!LdrUnloadDll+0x39A
        689275c1: kernel32!FreeLibrary+0x3B
        77b22d69: ole32!CoQueryReleaseObject+0x1E6
        77b02bd2: ole32!SetErrorInfo+0x1ED

Ele pode ser visto na saída em que o thread 0xB4 primeiro descompactou uma página e, em seguida, liberou toda a região virtual.

Aqui está uma exibição de todas as operações que afetam o endereço 0x4BB1000:

0:001> !avrf -vs -a 4bb1000

Searching in vspace log for address 04bb1000 ...

VirtualFree (tid: 0xB4): addr:04bb0000 sz:00400000 op:8000 prot:0
        00aa1ac2: verifier!VsLogCall+0x42
        00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
        68925d17: kernel32!VirtualFreeEx+0x35
        6892611c: kernel32!VirtualFree+0x13
        75ef6525: mshtml+0x116525
        75ef68af: mshtml+0x1168AF
        6a20787c: ntdll!LdrpCallInitRoutine+0x14
        6a211c6f: ntdll!LdrUnloadDll+0x39A
        689275c1: kernel32!FreeLibrary+0x3B
        77b22d69: ole32!CoQueryReleaseObject+0x1E6
        77b02bd2: ole32!SetErrorInfo+0x1ED

VirtualFree (tid: 0xB4): addr:04bb1000 sz:00001000 op:4000 prot:0

        00aa1ac2: verifier!VsLogCall+0x42
        00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
        68925d17: kernel32!VirtualFreeEx+0x35
        6892611c: kernel32!VirtualFree+0x13
        75ef65ae: mshtml+0x1165AE
        75ef68af: mshtml+0x1168AF
        6a20787c: ntdll!LdrpCallInitRoutine+0x14
        6a211c6f: ntdll!LdrUnloadDll+0x39A
        689275c1: kernel32!FreeLibrary+0x3B
        77b22d69: ole32!CoQueryReleaseObject+0x1E6
        77b02bd2: ole32!SetErrorInfo+0x1ED

VirtualAlloc (tid: 0xB4): addr:04bb0000 sz:00010000 op:1000 prot:4

        00aa1ac2: verifier!VsLogCall+0x42
        00aa1988: verifier!AVrfpNtAllocateVirtualMemory+0x37
        68925ca3: kernel32!VirtualAllocEx+0x61
        68926105: kernel32!VirtualAlloc+0x16
        75ef63f3: mshtml+0x1163F3

VirtualAlloc (tid: 0xB4): addr:04bb0000 sz:00400000 op:2000 prot:4

        00aa1ac2: verifier!VsLogCall+0x42
        00aa1988: verifier!AVrfpNtAllocateVirtualMemory+0x37
        68925ca3: kernel32!VirtualAllocEx+0x61
        68926105: kernel32!VirtualAlloc+0x16
        75ef63d9: mshtml+0x1163D9

Para ler este resultado, lembre-se de que as entradas são exibidas começando pela mais recente. Assim, esse log mostra que a thread 0xB4 alocou uma região grande na qual comprometeu uma página. Posteriormente, descompactou a página e liberou toda a região virtual.

Consulte Também

Verificador de Aplicativos – Visão geral

Verificador de Aplicativos – Testando aplicativos

Verificador de Aplicativos – Testes no Verificador de Aplicativos

Verificador de Aplicativos – Parar códigos e definições

Verificador de Aplicativos – Perguntas frequentes