Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Instalação e configuração do depurador
Algumas ações do Application Verifier podem resultar em uma exceção sendo gerada. O depurador deve ser configurado para capturar essas exceções na segunda chance, porque o próprio Application Verifier manipulará as exceções de primeira chance.
As exceções levantadas são de três tipos:
Uma exceção de violação de acesso (0xC0000005) é gerada se a opção de heap detetar um excesso de buffer no heap. Em alguns casos, a opção Verificar o uso do caminho do sistema pode também causar uma violação de acesso.
Uma exceção de identificador inválido (0xC0000008) é gerada quando a opção Detetar uso de identificador inválido deteta uma operação de identificador inválida.
Uma exceção de estouro de pilha (0xC00000FD) é gerada quando a opção Verificar se há pilha adequada deteta 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
ou
cdb -xd av -xd ch -xd sov ApplicationCommandLine
Se você já tiver iniciado o depurador, poderá usar o comando sxd (set Exceptions) 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 Application Verifier através de um depurador de kernel. No entanto, isso não é recomendado — requer o uso frequente dos comandos .process e .pagein, mas não lhe dá mais poder do que usar um depurador de 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.
Configuração de hardware para debug do User-Mode
A depuração no modo de usuário geralmente é feita em uma única máquina: 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.
Configuração do 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ímbolos necessários e definir determinadas variáveis de ambiente.
Arquivos de símbolos
Você deve baixar os arquivos de símbolos para o processo de modo de utilizador que está a ser depurado. Se este é um aplicativo que você escreveu, ele deve ser construído com arquivos de símbolos completos. Se for um aplicativo comercial, os arquivos de símbolo podem 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 estiver usando:
Se você estiver executando a depuração remota através do depurador, os arquivos de símbolo devem estar no computador com o servidor de depuração.
Se você estiver executando a depuração remota através remote.exe, os arquivos de símbolo devem estar no computador com o depurador.
Se estiveres a executar a depuração remota através de um servidor de processos ou de um servidor de conexão KD, os ficheiros de símbolos devem estar no computador com o cliente inteligente.
Se estiver a controlar o depurador em modo de utilizador a partir do depurador do kernel, os arquivos de símbolo têm de estar em ambos os computadores.
Configurando variáveis de ambiente
O depurador usa uma variedade de variáveis de ambiente para indicar uma série de configurações importantes.
Para obter mais informações sobre depuradores, consulte Introdução à depuração do Windows
Configurando o Application Verifier com o Depurador usando a linha de comando
Para configurar o Application Verifier, você pode usar a linha de comando CDB ou NTSD.
Use a seguinte linha de comando:
cdb OtherOptions -vf:Flags Target
Onde Target é o nome do aplicativo de destino e Flags especifica as opções desejadas do Application Verifier que devem ser aplicadas a esse destino.
Os sinalizadores devem ser uma soma dos bits que representam as opções desejadas. Os valores de bit individuais são os seguintes:
| Valor da bandeira | Significado |
|---|---|
| 00000001 | VERIFICAÇÕES DE HEAP |
| 00000004 | GERIR VERIFICAÇÕES |
| 00000008 | VERIFICAÇÕES DE SIM COM POUCOS RECURSOS |
| 00000020 | VERIFICAÇÕES TLS |
| 00000040 | PILHAS SUJAS |
| 00000200 | ÁPICES PERIGOSAS |
| 00001000 | VERIFICAÇÕES DE EXCEÇÃO |
| 00002000 | VERIFICAÇÕES DE MEMÓRIA |
| 00020000 | CONTROLOS DIVERSOS |
| 00040000 | VERIFICAÇÕES DE BLOQUEIO |
Depuração com !avrf
A extensão !avrf controla as configurações do Application Verifier e exibe uma variedade de saídas produzidas pelo Application Verifier. Para obter informações adicionais sobre a extensão !arvrf, consulte !avrf nos documentos do depurador.
Sintaxe
!avrf
O comando !avrf sem quaisquer 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 do espaço virtual. Length especifica o número de registros a serem exibidos a partir do mais recente. Endereço especifica o endereço virtual. Serão apresentados registos das operações virtuais que contêm este endereço virtual.
!avrf -hp { Length | -a Address }
Exibe o log de operação do heap. Address especifica o endereço na heap. Serão mostrados registos das operações de heap que contêm esse endereço de heap.
!avrf -cs { Length | -a Address }
Exibe o log de exclusão da seção crítica. Length especifica o número de registros a serem exibidos a partir do mais recente. Endereço especifica o endereço da seção crítica. Os registros para a seção crítica específica são exibidos quando Endereço é especificado.
!avrf -dlls [ Length ]
Exibe o log de carga/descarga da DLL. Length 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 Application Verifier rastreia todas as exceções que acontecem 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 de criação de threads especificados pelo pai também são exibidos. Fornecer um ID de thread exibirá informações apenas 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 de thread, postar mensagens de thread, inicializar COM e desinicializar COM de dentro do retorno de chamada do pool de threads. Fornecer um ID de thread exibirá informações apenas para esse thread específico.
!avrf -srw [ Address | Address Length ] [ -stats ]
Exibe o registo do Slim Reader/Writer (SRW). Ao especificar o endereço, irá exibir os registos pertencentes a esse endereço de bloqueio do SRW. Quando Length é especificado junto com o Address , todos os bloqueios SRW dentro desse intervalo de endereços são exibidos. A opção -stats despeja as estatísticas de bloqueio 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 determinado momento. Especificar ModuleName (incluindo a extensão) exibe todos os recursos pendentes no módulo especificado. Especificar ResourceType exibe recursos excecionais desse tipo de recurso. A especificação de endereço despeja registros de recursos pendentes 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: Mostra alocações de handle de threads, arquivos e eventos.
!avrf –trace TraceIndex
Exibe um stack trace para o índice de rastreio especificado. Algumas estruturas usam este número de índice de 16 bits para identificar um traço de pilha. Esse índice aponta para um local dentro do banco de dados de rastreamento de pilha. Se você estiver analisando tal estrutura, você achará essa sintaxe útil.
!avrf -cnt
Exibe uma lista de contadores globais.
!avrf -brk [ BreakEventType ]
Especifica que este é um comando de interrupção de evento (break-event). Quando !avrf -brk usado sem parâmetros adicionais, as configurações de evento de quebra são exibidas. BreakEventType especifica o número do tipo do evento de interrupção. Para obter uma lista de tipos possíveis, use !avrf -brk.
!avrf -flt [ EventTypeProbability ]
Especifica que este é 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 do tipo do evento. Probabilidade especifica a frequência com que o evento falhará. Pode ser qualquer número inteiro entre 0 e 1.000.000 (0xF4240).
!avrf -flt break EventType
Faz com que o Application Verifier invada o depurador cada vez que essa falha é injetada.
!avrf -flt stacks Length
Exibe o número de comprimentos de rastreamentos de pilha para as operações de injeção de falha mais recentes.
!avrf -trg [ StartEnd | dll Module | all ]
Especifica que este é 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. End especifica o endereço final do intervalo de destino ou intervalo de exclusão. Module 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 de 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 este é um comando de intervalo de exclusão. Start especifica o endereço inicial do intervalo de destino ou intervalo de exclusão. End especifica o endereço final do intervalo de destino ou intervalo de exclusão. Module 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 de 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 por milissegundos de tempo após a execução ser retomada.
A seguir está 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.
!avrf extensão comentários
Quando a extensão !avrf é usada sem parâmetros, ela exibe as opções atuais do Application Verifier.
A extensão !avrf usa o Exts.dll no depurador.
Se tiver ocorrido um Application Verifier Stop, 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 faltando, a extensão !avrf gerará uma mensagem de erro.
Paragens contínuas e não continuáveis
Depurando uma parada contínua
Aqui está um exemplo de uma exceção de identificador inválido que foi gerada pela opção Detetar uso de identificador inválido.
Primeiro, aparece a seguinte mensagem:
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 este "Application Verifier Stop" pode ser continuado. Depois de entender o que aconteceu, 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 desta exibição resume o problema.
Pode querer consultar alguns registos neste ponto. 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 interrupção do Application Verifier não pode ser continuada. O erro é muito grave para que o processo continue em execução e não há como o Application Verifier salvar o processo.
A extensão !avrf pode ser usada para dar 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 desta exibição resume o problema.
Você também pode querer olhar para alguns logs neste momento. Você pode querer usar o comando .restart (Restart Target Application) neste momento. Ou talvez você prefira encerrar sua sessão do Application Verifier e começar a corrigir os bugs em seu código.
Resolução de problemas em erros de seções críticas
Extensão do depurador !cs
!cs pode ser usado no depurador de modo de usuário e no depurador do 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.
É necessário fazer corresponder símbolos com informações de tipo, especialmente para ntdll.dll.
A sintaxe para esta extensão é:
!cs [-s] - despeje 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 address - exibir seção crítica correspondente ao DebugInfo neste endereço.
-s irá gerar o rastreamento da pilha durante a inicialização da seção crítica, se estiver 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
Extrair informações referentes a uma seção crítica usando seu endereço, incluindo o rastreio 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
Despeje informações sobre uma seção crítica usando seu endereço de informações de depuração, incluindo rastreamento de pilha 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
Listar 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
.....
Exiba informações sobre todas as seções críticas ativas no processo atual, incluindo o rastreamento da 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
Erros de exceção de depuração
O log de exceções registra todas as exceções que ocorreram no processo de destino.
Você pode usar o comando !avrf -ex Length extension para exibir as últimas exceções; Length especifica o número de exceções. Se o Length não for especificado, todas as exceções serão exibidas.
Aqui está 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.
A depuração trata erros
!htrace pode ser usado no depurador de modo de usuário e no depurador do kernel para exibir informações de rastreamento de pilha para um ou todos os identificadores em um processo. Essas informações estarão disponíveis se o rastreamento de identificador estiver habilitado para o processo – ativado automaticamente se a verificação de identificador estiver habilitada no verificador de aplicativo. Os rastros de pilha são salvos sempre que o processo está a abrir ou fechar um identificador ou quando está a referenciar um identificador inválido. Para obter informações adicionais sobre a extensão !htrace, consulte !htrace nos documentos do depurador.
A sintaxe do depurador do kernel para esta extensão é:
!htrace [ handle [process] ]
Caso o identificador não seja especificado ou seja 0, serão exibidas informações sobre todos os identificadores no processo. Se o processo não for especificado, o processo atual será usado.
A sintaxe do depurador de modo de usuário é:
!htrace [handle]
A extensão do depurador de modo de usuário sempre exibe informações sobre o processo de depuração atual.
Exemplos:
Despejar informações 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.
Despejar informações 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.
Corrigindo erros de heap
Extensão do Depurador Verificador de Heap
A extensão do depurador do verificador de heap faz parte da extensão !heap (extensão do depurador NT heap). Ajuda simples pode ser obtida com !heap -? ou mais extenso com !heap -p -? . A extensão atual não deteta por si só se o heap de página está habilitado para um processo e age de acordo. Por enquanto, o usuário da extensão precisa saber que o heap de página está habilitado e usar comandos prefixados por !heap -p . Para obter informações adicionais sobre a extensão !htrace, consulte !heap nos documentos do depurador.
!heap -p
Lista os endereços de todos os heaps de página inteira criados pelo processo.
!heap -p -h ADDRESS-OF-HEAP
Exportação completa do heap de página completa em ADDRESS-OF-HEAP.
!heap -p -a ADDRESS
Tenta determinar se há um bloco de heap no endereço especificado. Esse valor não precisa ser o endereço do início do bloco. O comando é útil se não houver nenhuma pista sobre a natureza de uma área de memória.
Log de operação de pilha
O log de operação de heap rastreia todas as rotinas de heap. Estes incluem HeapAlloc, HeapReAlloc e HeapFree.
Você pode usar o !avrf -hp Length comando extension para exibir os últimos vários registros; Length especifica o número de registros.
Você pode usar !avrf -hp -a Address para exibir todas as operações 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 memória heap alocado. Para uma operação livre, deve ser indicado o endereço exato do início do bloco.
Para cada entrada no log, as seguintes informações são exibidas:
- A função heap foi chamada.
- O ID do thread que chamou a rotina.
- O endereço envolvido na chamada — este é 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.
- Rastreamento da pilha de chamadas.
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
Existem vários cenários de falha que podem ser encontrados. Alguns deles exigem um pouco de trabalho de detetive para obter o quadro completo.
Violação de acesso em página não acessível
Isso acontece quando o heap de página inteira é habilitado se o aplicativo testado acessa além do final do buffer. Isto também pode acontecer se tocar num bloco já libertado. 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 bloqueio corrompida
Em vários momentos durante o tempo de vida de uma alocação (alocação, liberação pelo utilizador, liberação real), o gestor de heap de página verifica se o bloco tem todos os padrões de preenchimento intactos e se os dados do cabeçalho do bloco são consistentes. Se este não for o caso, você receberá uma parada do 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), então você pode usar "!heap –p –a ADDRESS" para descobrir quais são as características do bloco.
Se o bloco for um bloco de pilha de página leve, então você precisa descobrir o endereço inicial para o cabeçalho do bloco. Você pode encontrar o endereço inicial despejando 30-40 bytes abaixo do endereço relatado e procurar os padrões mágicos de início/fim para um cabeçalho de bloco (ABCDAAAA, ABCDBBBB, ABCDAAA9, ABCDBBBA).
O cabeçalho dará todas as informações necessárias para entender a falha. Particularmente, os padrões mágicos indicarão se o bloco está alocado ou livre, e se é um heap de página leve ou um heap de página completa. As informações aqui devem ser cuidadosamente correlacionadas com a chamada problemática.
Por exemplo, se uma chamada para HeapFree é feita com o endereço de um bloco mais quatro bytes, então você receberá a mensagem corrompida. O cabeçalho de bloco aparentemente estará correto, mas terá que notar que o primeiro byte após o final do cabeçalho (primeiro byte após o valor mágico 0xDCBAXXXX) tem um endereço diferente daquele da chamada.
Ponteiros de preenchimento especiais
O gerenciador de heap de página preenche a alocação do usuário com valores que serão parecidos com ponteiros do 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 zeradas são típicas para malloc/novos usuários. Se houver uma falha (violação de acesso) em que uma leitura/gravação é tentada em endereços como F0F0F0F0, E0E0E0E0 C0C0C0C0 então muito provavelmente você acertou um desses casos.
Uma leitura/gravação em F0F0F0F0 significa que um bloco foi usado depois de ser liberado. Infelizmente, você precisará de algum trabalho de detetive para descobrir qual bloqueio causou isso. Precisa obter o rastreio de pilha da falha e, em seguida, inspecionar o código das funções na pilha. Um deles pode fazer uma suposição errada ao considerar que uma alocação está ativa.
Uma leitura/gravação em E0E0E0E0/C0C0C0C0 significa que o aplicativo não inicializou corretamente a alocação. Isso também requer inspeção de código das funções no rastreamento de pilha atual. Aqui está um exemplo para este tipo de fracasso. Em um processo de teste, uma violação de acesso ao fazer um HeapFree no endereço E0E0E0E0 foi notada. Descobriu-se 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 nele), ele chamou delete nele.
Detalhes Técnicos do Page Heap
Para detetar corrupções de heap (estouros ou subfluxos), o AppVerifier modificará a maneira como a memória é alocada preenchendo a memória solicitada com páginas completas não graváveis ou com tags especiais antes e depois da memória alocada. AppVerifier faz isso carregando Verifier.dll no processo em verificação e redirecionando algumas das APIs de Heap do Win32 chamadas pelo aplicativo para as APIs correspondentes de Verifier.dll.
Ao preencher a memória solicitada com páginas completas não graváveis (a configuração FULL está habilitada na seção de propriedades de 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 de heap são armazenados em cache em tempo real quando ocorre o estouro ou subfluxo. Lembre-se de que a memória neste modo será semelhante a esta [AppVerifier Read-Only Heap Page (4k)] [Quantidade de memória solicitada pelo Aplicativo em teste] ou como esta [Quantidade de memória solicitada pelo Aplicativo em teste] [AppVerifier Read-Only Heap Page (4k)].
A verificação de pilha 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 saturações de buffer. Se definido como True, a página de proteção é colocada no início da alocação para capturar a subutilização de buffer.
Ao preencher a memória solicitada com tags especiais (ativado desmarcando o item da caixa de seleção "Completo" nas propriedades da pilha), 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á detetada quando a memória for liberada (a quantidade mínima de bloco de memória é de 8 bytes), portanto, quando em uma variável de 3 bytes ou ocorrer um estouro de 5 bytes, ela não será imediatamente detetada.
Num evento de subfluxo, será feita uma tentativa de gravar numa página Read-Only. Isso desencadeará uma exceção. Observe que essa exceção só pode ser detetada se o aplicativo de destino estiver sendo executado em um depurador. Observe que o modo de heap de página inteira também detetará esses erros porque usa páginas de preenchimento+guarda. A razão pela qual você usaria pilha de página leve é se o seu computador não pode tolerar as altas restrições de memória de pilha de página inteira.
Para aplicativos com uso intensivo de memória ou quando é necessário usar o AppVerifier durante longos períodos de tempo (por exemplo, testes de esforço), é melhor executar testes de heap normais (leves) em vez do modo completo devido à degradação do desempenho. No entanto, quando você se deparar com um problema, ative o heap de página inteira para investigar mais.
As aplicações que estão a usar heaps personalizados (um heap que ignora a implementação do heap pelo sistema operativo) podem não obter o benefício completo de usar o heap de página ou podem até mesmo ter um mau funcionamento quando este está ativado.
Depurando erros de memória
A extensão do depurador do verificador de memória
O log de operação do espaço virtual rastreia todas as rotinas que modificam o espaço virtual de um processo de qualquer forma. Estes incluem VirtualAlloc, VirtualFree, MapViewOfFile e UnmapViewOfFile.
Você pode usar o !avrf -vs Length comando extension para exibir os últimos vários registros; Length 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 atribuição, basta que o endereço esteja contido no bloco atribuído. Para obter gratuitamente, deve ser indicado 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 denominada
- O ID do segmento que chamou a rotina
- O endereço envolvido na chamada — este é o endereço que foi devolvido por uma rotina de alocação ou que foi passado para uma rotina gratuita
- A dimensão da região envolvida no convite à apresentação de propostas
- 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
Pode ser visto pela saída que o thread 0xB4 primeiro descomprometeu 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 esta saída, lembre-se de que as entradas são despejadas começando com a mais recente. Assim, este log mostra que o thread 0xB4 alocou uma grande região na qual confirmou uma página. Mais tarde, desalocou a página e, em seguida, libertou toda a região virtual.
Ver também
Application Verifier - Visão geral
Application Verifier - Testando aplicativos
Application Verifier - Testes dentro do Application Verifier