Condividi tramite


Application Verifier - Debug dell'applicazione verifier arresta

Installazione e configurazione del debugger

Alcune azioni di Application Verifier possono comportare la generazione di un'eccezione. Il debugger deve essere impostato per intercettare queste eccezioni al secondo tentativo, perché Application Verifier gestirà le eccezioni al primo tentativo.

Le eccezioni generate sono di tre tipi:

  • Se l'opzione heap rileva un sovraccarico del buffer heap, viene generata un'eccezione di violazione di accesso (0xC0000005). In alcuni casi, l'opzione Controlla l'utilizzo del percorso di sistema può causare anche una violazione di accesso.

  • Un'eccezione di handle non valida (0xC0000008) viene generata quando l'opzione Rileva utilizzo handle non valido rileva un'operazione di handle non valida.

  • Un'eccezione di overflow dello stack (0xC00000FD) viene generata quando l'opzione Verifica dell'adeguatezza dello stack rileva che lo stack iniziale è troppo breve.

Un modo per prepararsi a questi eventi consiste nell'avviare il debugger in una riga di comando come indicato di seguito:

windbg -xd av -xd ch -xd sov ApplicationCommandLine

o

cdb -xd av -xd ch -xd sov ApplicationCommandLine

Se il debugger è già stato avviato, è possibile usare il comando sxd (Imposta eccezioni) per rilevare tutte le violazioni di accesso, gli handle non validi e gli overflow dello stack come eccezioni di seconda probabilità:

0:000> sxd av 

0:000> sxd ch 

0:000> sxd sov 1

È teoricamente possibile controllare Application Verifier tramite un debugger del kernel. Tuttavia, questo non è consigliato. Richiede un uso frequente dei comandi .process e .pagein, ma non offre più potenza rispetto all'uso di un debugger in modalità utente.

Installazione degli strumenti di debug

Per scaricare la versione più recente degli strumenti, vedere Scaricare gli strumenti di debug per Windows.

Configurazione dell'hardware per il debug di User-Mode

Il debug in modalità utente viene in genere eseguito in un singolo computer: il debugger viene eseguito nello stesso computer dell'applicazione che non è riuscito.

In questo caso, non è necessaria alcuna configurazione hardware specifica. In questo argomento, i termini computer host e computer di destinazione sono intercambiabili in questo caso.

Configurazione del software per il debug di User-Mode

Configurazione di base User-Mode: prima di iniziare il debug in modalità utente, è necessario scaricare i file di simboli necessari e impostare determinate variabili di ambiente.

File di simboli

È necessario scaricare i file di simboli per il processo in modalità utente di cui è in corso il debug. Se si tratta di un'applicazione scritta, deve essere compilata con file di simboli completi. Se si tratta di un'applicazione commerciale, i file di simboli possono essere disponibili in un server Web o per il download, contattare il produttore.

se si esegue il debug remoto, il percorso del file di simboli dipende dal metodo in uso:

  • Se si esegue il debug remoto tramite il debugger, i file di simboli devono trovarsi nel computer con il server di debug.

  • Se si esegue il debug remoto tramite remote.exe, i file di simboli devono trovarsi nel computer con il debugger.

  • Se si esegue il debug remoto tramite un server di elaborazione o un server di connessione KD, i file di simboli devono trovarsi nel computer con il client intelligente.

  • Se si controlla il debugger in modalità utente dal debugger del kernel, i file di simboli devono trovarsi in entrambi i computer.

Configurazione delle variabili di ambiente

Il debugger usa un'ampia gamma di variabili di ambiente per indicare una serie di impostazioni importanti.

Per altre informazioni sui debugger, vedere Introduzione al debug di Windows

Configurazione di Application Verifier con il debugger tramite la riga di comando

Per configurare Application Verifier è possibile usare la riga di comando CDB o NTSD.

Usare la riga di comando seguente:

cdb OtherOptions -vf:Flags Target

Dove Target è il nome dell'applicazione di destinazione e Flags specifica le opzioni desiderate di Application Verifier da applicare a questa destinazione.

I flag devono essere una somma dei bit che rappresentano le opzioni desiderate. I singoli valori di bit sono i seguenti:

Valore del flag Significato
00000001 CONTROLLI HEAP
00000004 GESTIRE I CONTROLLI
00000008 CONTROLLI SIM A BASSO CONSUMO
00000020 CONTROLLI TLS
00000040 STACK SPORCHI
00000200 API PERICOLOSE
00001000 VERIFICHE DELLE ECCEZIONI
00002000 CONTROLLI DI MEMORIA
00020000 CONTROLLI VARI
00040000 CONTROLLI DI BLOCCO

Debug con !avrf

L'estensione !avrf controlla le impostazioni di Application Verifier e visualizza un'ampia gamma di output prodotti da Application Verifier. Per altre informazioni sull'estensione !arvrf, vedere !avrf nella documentazione del debugger.

Sintassi

!avrf

Il comando !avrf senza parametri mostra le impostazioni di Application Verifier e le informazioni sull'eventuale interruzione di Application Verifier corrente e precedente.

!avrf –vs { Length | -aAddress }

Visualizza il log operazioni dello spazio virtuale. Length specifica il numero di record da visualizzare a partire dall'ultima. L'indirizzo definisce l'indirizzo virtuale. Verranno visualizzati i record delle operazioni virtuali contenenti questo indirizzo virtuale.

!avrf -hp { Length | -a Address }

Visualizza il log delle operazioni dell'heap. Address specifica l'indirizzo dell'heap. Verranno visualizzati i record delle operazioni heap contenenti questo indirizzo heap.

!avrf -cs { Length | -a Address }

Visualizza il log di eliminazione della sezione critica. Length specifica il numero di record da visualizzare a partire dall'ultima. Address specifica l'indirizzo della sezione critica. I record per la specifica sezione critica vengono visualizzati quando si specifica l'indirizzo.

!avrf -dlls [ Length ]

Visualizza il log di caricamento/scaricamento della DLL. Length specifica il numero di record da visualizzare a partire dall'ultima.

!avrf -trm

Visualizza un log di tutti i thread terminati e sospesi.

!avrf -ex [ Length ]

Visualizza il log delle eccezioni. Application Verifier tiene traccia di tutte le eccezioni che si verificano nell'applicazione.

!avrf -threads [ ThreadID ]

Visualizza informazioni sui thread nel processo di destinazione. Per i thread figli, vengono visualizzati anche la dimensione dello stack e i flag di CreateThread, specificati dal genitore. Se si specifica un ID thread, verranno visualizzate informazioni solo per quel particolare thread.

!avrf -tp [ ThreadID ]

Visualizza il log del pool di thread. Questo log può contenere tracce dello stack per varie operazioni, ad esempio la modifica della maschera di affinità thread, la modifica della priorità del thread, la registrazione di messaggi di thread, l'inizializzazione di COM e l'annullamento dell'inizializzazione di COM dal callback del pool di thread. Se si specifica un ID thread, verranno visualizzate informazioni solo per quel particolare thread.

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

Visualizza il log Slim Reader/Writer (SRW). Se si specifica Indirizzo, verranno visualizzati i record relativi all'indirizzo di blocco SRW. Quando Length viene specificato insieme all'indirizzo, vengono visualizzati tutti i blocchi SRW nell'intervallo di indirizzi. L'opzione -stats esegue il dump delle statistiche di blocco SRW.

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

Visualizza il log delle risorse eccezionali. Queste risorse possono essere o meno perdite in un determinato momento. Se si specifica ModuleName (inclusa l'estensione) vengono visualizzate tutte le risorse in sospeso nel modulo specificato. Specificando ResourceType vengono visualizzate le risorse eccezionali di quel particolare tipo di risorsa. Specificando l'indirizzo, vengono registrate le risorse eccezionali associate a quell'indirizzo. ResourceType può essere uno dei seguenti:

  • Heap: visualizza le allocazioni dell'heap usando le API heap Win32
  • Locale: visualizza allocazioni locali/globali
  • CRT: visualizza le allocazioni usando le API CRT
  • Virtuale: visualizza le prenotazioni virtuali
  • BSTR: visualizza le allocazioni BSTR
  • Registro di sistema: viene visualizzata la chiave del Registro di sistema
  • Potenza: visualizza gli oggetti di notifica potenza
  • Handle: visualizza allocazioni di thread, file e handle di eventi

!avrf –trace TraceIndex

Visualizza un'analisi dello stack per l'indice di traccia specificato. Alcune strutture usano questo numero di indice a 16 bit per identificare una traccia dello stack. Questo indice punta a una posizione all'interno del database di analisi dello stack. Se si analizza una struttura di questo tipo, questa sintassi risulta utile.

!avrf -cnt

Visualizza un elenco di contatori globali.

!avrf -brk [ BreakEventType ]

Specifica che si tratta di un comando di evento di interruzione. Quando !avrf -brk viene usato senza parametri aggiuntivi, vengono visualizzate le impostazioni dell'evento di interruzione. BreakEventType specifica il numero di tipo dell'evento di interruzione. Per un elenco di tipi possibili, usare !avrf -brk.

!avrf -flt [ EventTypeProbability ]

Specifica che si tratta di un comando di tipo fault injection. Quando !avrf -flt viene usato senza parametri aggiuntivi, vengono visualizzate le impostazioni correnti di inserimento degli errori. EventType specifica il numero di tipo dell'evento. Probabilità specifica la frequenza con cui l'evento avrà esito negativo. Può essere un numero intero compreso tra 0 e 1.000.000 (0xF4240).

!avrf -flt break EventType

Fa sì che Application Verifier forzi un'interruzione nel debugger ogni volta che viene iniettato questo errore.

!avrf -flt stacks Length

Consente di visualizzare il numero di tracce dello stack per le operazioni con errori più recenti.

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

Specifica che si tratta di un comando di intervallo di destinazione. Quando -trg viene usato senza parametri aggiuntivi, vengono visualizzati gli intervalli di destinazione correnti. Start specifica l'indirizzo iniziale dell'intervallo di destinazione o dell'intervallo di esclusione. End specifica l'indirizzo finale dell'intervallo di destinazione o dell'intervallo di esclusione. Module specifica il nome di un modulo da specificare come destinazione o escludere. Il modulo deve includere il nome completo del modulo, inclusa l'estensione .exe o .dll. Le informazioni sul percorso non devono essere incluse. Specificare "tutti" comporta il ripristino di tutti gli intervalli di destinazione o di esclusione.

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

Specifica che si tratta di un comando di intervallo di esclusione. Start specifica l'indirizzo iniziale dell'intervallo di destinazione o dell'intervallo di esclusione. End specifica l'indirizzo finale dell'intervallo di destinazione o dell'intervallo di esclusione. Module specifica il nome di un modulo da specificare come destinazione o escludere. Il modulo deve includere il nome completo del modulo, inclusa l'estensione .exe o .dll. Le informazioni sul percorso non devono essere incluse. Quando si specifica 'tutti', tutti gli intervalli di destinazione o di esclusione vengono reimpostati automaticamente. Specificando il parametro Time, tutti i guasti verranno soppressi per un intervallo di Time millisecondi dal momento in cui l'esecuzione riprende.

Di seguito è riportato l'output fornito dal comando !avrf nel debugger.

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

Quando l'estensione !avrf viene usata senza parametri, vengono visualizzate le opzioni correnti di Application Verifier.

L'estensione !avrf usa il Exts.dll nel debugger.

Se si è verificato un arresto dell'applicazione verifier, l'estensione !avrf senza parametri rivelerà la natura dell'arresto e ciò che lo ha causato.

Se mancano simboli per ntdll.dll e verifier.dll, l'estensione !avrf genererà un messaggio di errore.

Arresti continuabili e non continuabili

Debug di un arresto continuo

Di seguito è riportato un esempio di eccezione di handle non valida generata dall'opzione Rileva utilizzo handle non valido.

Prima di tutto, viene visualizzato il messaggio seguente:

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

Si noti che il messaggio indica che questo Application Verifier Stop può essere ripreso. Dopo aver compreso cosa è avvenuto, è possibile continuare a eseguire l'applicazione di destinazione.

In primo luogo, è necessario usare l'estensione !avrf. In questo modo vengono fornite informazioni sull'errore corrente:

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

La riga finale di questa visualizzazione riepiloga il problema.

A questo punto è consigliabile esaminare alcuni log. Al termine, usare il comando g (Go) per avviare nuovamente l'applicazione:

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

In questo caso, il messaggio indica che l'Arresto dell'Application Verifier non può procedere. L'errore è troppo grave per continuare l'esecuzione del processo e non è possibile salvare il processo in Application Verifier.

L'estensione !avrf può essere usata per fornire informazioni sull'errore corrente:

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.

La riga finale di questa visualizzazione riepiloga il problema.

È anche possibile esaminare alcuni log a questo punto. A questo punto, è possibile usare il comando .restart (Riavvia applicazione di destinazione). O forse si preferisce terminare la sessione di Application Verifier e iniziare a correggere i bug nel codice.

Debug di errori critici della sezione

Estensione del debugger !cs

!cs può essere usato sia nel debugger in modalità utente che nel debugger del kernel per visualizzare informazioni sulle sezioni critiche nel processo corrente. Per altre informazioni sull'estensione !cs, vedere !cs nella documentazione del debugger.

È necessaria la corrispondenza dei simboli con informazioni sul tipo, in particolare per ntdll.dll.

La sintassi per questa estensione è:

!cs [-s] - effettua il dump di tutte le sezioni critiche attive nel processo corrente.

!cs [-s] address - esegue il dump della sezione critica a questo indirizzo.

!cs [-s] indirizzo -d : sezione critica del dump corrispondente a DebugInfo in questo indirizzo.

-s eseguirà il dump della traccia dello stack di inizializzazione della sezione critica, se disponibile.

Esempi:

Eseguire il dump delle informazioni su una sezione critica usando il relativo indirizzo

0:001> ! cs 0x7803B0F8

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

Creare un dump delle informazioni di una sezione critica utilizzando il relativo indirizzo, inclusa la traccia dello stack di inizializzazione

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

Scaricare le informazioni su una sezione critica utilizzando l'indirizzo delle informazioni di debug

0:001> !cs -d 0x6A262080

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

Eseguire il dump delle informazioni su una sezione critica utilizzando l'indirizzo delle informazioni di debug, inclusa la traccia dello stack di inizializzazione.

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

Eseguire il dump delle informazioni su tutte le sezioni critiche attive nel processo corrente

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

Esegui il dump delle informazioni su tutte le sezioni critiche attive nel processo corrente, inclusa la traccia dello stack di inizializzazione.


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

Eccezioni nell'analisi dei bug

Il log delle eccezioni registra tutte le eccezioni che si sono verificate nel processo di destinazione.

È possibile usare il comando !avrf -ex Estensione lunghezza per visualizzare le ultime eccezioni; Length specifica il numero di eccezioni. Se Length viene omesso, vengono visualizzate tutte le eccezioni.

Ecco un esempio:

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.

Il debug gestisce gli errori

!htrace può essere usato sia nel debugger in modalità utente che nel debugger del kernel per visualizzare le informazioni di analisi dello stack per uno o tutti gli handle in un processo. Queste informazioni sono disponibili se la traccia dell'handle è abilitata per il processo, abilitata automaticamente se il controllo dell'handle è abilitato nel verificatore dell'applicazione. Le tracce dello stack vengono salvate ogni volta che il processo apre o chiude un handle o quando fa riferimento a un handle non valido. Per altre informazioni sull'estensione !htrace, vedere !htrace nella documentazione del debugger.

La sintassi del debugger del kernel per questa estensione è:

!htrace [ handle [process] ]

Se handle non è specificato o è 0, verranno visualizzate informazioni su tutti gli handle del processo. Se il processo non viene specificato, verrà utilizzato il processo corrente.

La sintassi del debugger in modalità utente è:

!htrace [handle]

L'estensione del debugger in modalità utente visualizza sempre informazioni sul processo di debug corrente.

Esempi:

Scaricamento delle informazioni sull'handle 7CC nel 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.

Scaricare informazioni su tutti gli handle nel 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.

Esegui il dump delle informazioni sull'handle 7DC nel processo corrente


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.

Debug di errori dell'heap

Estensione del debugger di verifica heap

L'estensione debugger per la verifica dell'heap fa parte dell'estensione !heap (estensione del debugger NT per heap). È possibile ottenere un aiuto semplice con !heap -? o più estesa con !heap -p -? . L'estensione corrente non rileva autonomamente se l'heap di pagina è abilitato per un processo e agisce di conseguenza. Per il momento l'utente dell'estensione deve conoscere che l'heap di pagina è abilitato e deve usare i comandi con prefisso !heap -p. Per altre informazioni sull'estensione !htrace, vedere !heap nella documentazione del debugger.

!heap -p

Elenca gli indirizzi di tutti i full page heap creati nel processo.

!heap -p -h ADDRESS-OF-HEAP

Dump completo dell'heap di pagina completo in ADDRESS-OF-HEAP.

!heap -p -a ADDRESS

Verifica se è presente un blocco heap all'indirizzo ADDRESS. Questo valore non deve essere l'indirizzo dell'inizio del blocco. Il comando è utile se non c'è alcun indizio sulla natura di un'area di memoria.

Registro delle operazioni heap

Il log delle operazioni dell'heap tiene traccia di tutte le routine dell'heap. Questi includono HeapAlloc, HeapReAlloc e HeapFree.

È possibile usare il !avrf -hp Length comando di estensione per visualizzare gli ultimi record; Length specifica il numero di record.

È possibile usare !avrf -hp -a Address per visualizzare tutte le operazioni di spazio heap che hanno interessato l'indirizzo specificato. Per un'operazione di allocazione, è sufficiente che Address sia contenuto nel blocco heap allocato. Per un'operazione gratuita, è necessario specificare l'indirizzo esatto dell'inizio del blocco.

Per ogni voce nel log, vengono visualizzate le informazioni seguenti:

  • La funzione heap è stata chiamata.
  • ID thread del thread che ha chiamato la routine.
  • Indirizzo coinvolto nella chiamata, ovvero l'indirizzo restituito da una routine di allocazione o passato a una routine gratuita.
  • Dimensioni dell'area coinvolta nella chiamata.
  • Analisi dello stack della chiamata.

Le voci più recenti vengono visualizzate per prime.

In questo esempio vengono visualizzate le due voci più recenti:

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

Scenari di debug tipici

Esistono diversi scenari di errore che potrebbero verificarsi. Alcuni di loro richiedono un po’ di lavoro investigativo per avere un quadro completo.

Violazione di accesso nella pagina non accessibile

Ciò si verifica quando l'heap di pagina completo è abilitato se l'applicazione testata accede oltre la fine del buffer. Può verificarsi anche se tocca un blocco liberato. Per comprendere qual è la natura dell'indirizzo in cui si è verificata l'eccezione, è necessario usare:

!heap –p –a ADDRESS-OF-AV

Messaggio di blocco danneggiato

In diversi momenti durante la durata di un'allocazione (allocazione, rilascio da parte dell'utente, rilascio effettivo) il gestore heap di pagina controlla se il blocco ha tutti gli schemi di riempimento intatti e l'intestazione del blocco ha dati coerenti. In caso contrario, si otterrà un arresto del verificatore.

Se il blocco è un blocco heap di pagina completo (ad esempio, se si sa che l'heap di pagina completa è abilitato per tutte le allocazioni), è possibile usare "!heap –p –a ADDRESS" per scoprire quali sono le caratteristiche del blocco.

Se il blocco è un blocco heap di pagina leggera, è necessario individuare l'indirizzo iniziale dell'intestazione del blocco. È possibile trovare l'indirizzo iniziale eseguendo un dump di 30-40 byte al di sotto dell'indirizzo segnalato e cercando i pattern magici di inizio/fine per un'intestazione di blocco (ABCDAAAA, ABCDBBBB, ABCDAAA9, ABCDBBBA).

L'intestazione fornirà tutte le informazioni necessarie per comprendere l'errore. In particolare, i pattern magici indicano se il blocco è allocato o libero e se si tratta di un heap di pagina leggera o di un blocco heap di pagina completo. L'informazione qui deve essere confrontata attentamente con la chiamata problematica.

Ad esempio, se viene effettuata una chiamata a HeapFree con l'indirizzo di un blocco più quattro byte, si otterrà il messaggio danneggiato. L'intestazione del blocco avrà un aspetto corretto, ma sarà necessario notare che il primo byte dopo la fine dell'intestazione (primo byte dopo il valore magic 0xDCBAXXXX) ha un indirizzo diverso da quello nella chiamata.

Puntatori di riempimento speciali

Il gestore dell'heap della pagina riempie l'allocazione dell'utente con valori che verranno visualizzati come puntatori di kernel. Ciò si verifica quando il blocco viene liberato (il valore di riempimento è F0) e quando il blocco viene allocato, ma non viene effettuata alcuna richiesta per azzerare il blocco (il valore di riempimento è E0 per l'heap di pagina leggera e C0 per l'heap di pagina completa). Le allocazioni non zero sono tipiche per gli utenti malloc/nuovi. Se si verifica un errore (violazione di accesso) in cui viene eseguito un tentativo di lettura/scrittura in indirizzi come F0F0F0F0, E0E0E0E0, C0C0C0C0, probabilmente si raggiunge uno di questi casi.

Una operazione di lettura/scrittura su F0F0F0F0 indica che un blocco è stato usato dopo essere stato liberato. Sfortunatamente, avrai bisogno di un po' di lavoro da detective per scoprire quale blocco lo ha causato. È necessario ottenere la traccia dello stack del guasto e quindi esaminare il codice delle funzioni nello stack. Uno di essi potrebbe fare un presupposto errato su un'allocazione ancora viva.

Una lettura/scrittura in E0E0E0E0/C0C0C0C0 indica che l'applicazione non ha inizializzato correttamente l'allocazione. Ciò richiede anche l'ispezione del codice delle funzioni nella traccia di stack corrente. Ecco un esempio per questo tipo di errore. In un processo di test è stata notata una violazione di accesso durante l'esecuzione di un heapFree sull'indirizzo E0E0E0E0. Si è scoperto che il test ha allocato una struttura, non l'ha inizializzata correttamente e ha quindi chiamato il distruttore dell'oggetto. Poiché un determinato campo non era null (aveva E0E0E0E0 in esso) ha chiamato delete su di esso.

Dettagli tecnici dell'heap della pagina

Per rilevare i danneggiamenti dell'heap (overflow o underflow), AppVerifier modificherà la modalità di allocazione della memoria riempiendo la memoria richiesta con pagine non scrivibili complete o con tag speciali prima e dopo la memoria allocata. AppVerifier esegue questa operazione caricando Verifier.dll nel processo verificato e reindirizzando alcune delle API heap Win32 chiamate dall'applicazione alle API Verifier.dll corrispondenti.

Quando si riempie la memoria richiesta con pagine non scrivibili complete (l'impostazione FULL è abilitata nella sezione delle proprietà dell'heap della pagina ed è l'impostazione predefinita), AppVerifier utilizzerà una grande quantità di memoria virtuale, ma ha il vantaggio che gli eventi di danneggiamento dell'heap vengono memorizzati nella cache in tempo reale quando si verifica l'overflow o l'underflow. Tenere presente che la memoria in questa modalità sarà simile a questa [AppVerifier Read-Only Pagina Heap (4k)] [quantità di memoria richiesta dall'applicazione sottoposta a test] o simile a questa [quantità di memoria richiesta dall'applicazione sottoposta a test] [AppVerifier Read-Only Pagina Heap (4k)].

Il controllo dell'heap inserisce una pagina di protezione all'inizio o alla fine dell'allocazione a seconda della proprietà Backward. Se l'opzione Indietro è impostata su False, ovvero l'impostazione predefinita, verrà posizionata una pagina di protezione alla fine dell'allocazione per intercettare i sovraccarichi del buffer. Se è impostato su True, la pagina di protezione viene posizionata all'inizio dell'allocazione per intercettare i sottoscorri del buffer.

Quando si riempie la memoria richiesta con tag speciali (abilitati deselezionando l'elemento della casella di controllo "Full" nelle proprietà dell'heap), AppVerifier verificherà e avvisa l'utente quando viene rilasciata la memoria. Il problema principale nell'uso di questa tecnica è che ci sono alcuni casi in cui il danneggiamento della memoria verrà rilevato solo quando viene rilasciata la memoria (la quantità minima di blocco di memoria è di 8 byte), quindi quando si verifica un overflow a 3 byte o un overflow a 5 byte non verrà rilevato immediatamente.

In caso di evento di underflow, verrà effettuato un tentativo di scrivere in una pagina Read-Only. Verrà attivata un'eccezione. Si noti che questa eccezione può essere rilevata solo se l'applicazione di destinazione viene eseguita in un debugger. Si noti che la modalità heap di pagina completa rileverà anche questi errori perché usa le pagine padding+guard. Il motivo per cui si utilizza il "light page heap" è nel caso in cui il computer non possa tollerare i vincoli di memoria elevati dell'heap di pagina completo.

Per le applicazioni a elevato utilizzo di memoria o quando è necessario usare AppVerifier durante lunghi periodi di tempo (ad esempio, test di stress), è preferibile eseguire test heap normali (leggeri) anziché la modalità completa a causa della riduzione delle prestazioni. Tuttavia, quando si verifica un problema, attivare l'heap a pagina intera per approfondire l'analisi.

Le applicazioni che usano heap personalizzati (un heap che ignora l'implementazione del sistema operativo dell'heap) potrebbero non ottenere il vantaggio completo dell'uso del page heap o potrebbero anche non funzionare correttamente quando il page heap è abilitato.

Debug di errori di memoria

Estensione del debugger di verifica della memoria

Il log delle operazioni dello spazio virtuale tiene traccia di tutte le routine che modificano lo spazio virtuale di un processo in qualsiasi modo. Sono inclusi VirtualAlloc, VirtualFree, MapViewOfFile e UnmapViewOfFile.

È possibile usare il !avrf -vs Length comando di estensione per visualizzare gli ultimi record; Length specifica il numero di record.

È possibile usare !avrf -vs -a Address per visualizzare tutte le operazioni sullo spazio virtuale che hanno interessato l'indirizzo specificato. Per un'allocazione, è sufficiente che l'indirizzo sia contenuto nel blocco allocato. Per un numero gratuito, è necessario specificare l'indirizzo esatto dell'inizio dell'area.

Per ogni voce nel log, vengono visualizzate le informazioni seguenti:

  • La funzione chiamata
  • L'ID del thread che ha chiamato la routine
  • Indirizzo coinvolto nella chiamata, ovvero l'indirizzo restituito da una routine di allocazione o passato a una routine gratuita
  • Dimensioni dell'area coinvolta nella chiamata
  • Tipo di operazione di memoria (parametro AllocationType)
  • Tipo di protezione richiesto
  • Analisi dello stack della chiamata

Esempi

Le voci più recenti vengono visualizzate per prime.

Nell'esempio seguente vengono visualizzate le due voci più recenti:

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

Dall'output si vede che il thread 0xB4 ha prima decommittedo una pagina e quindi ha rilasciato l'intera area virtuale.

Ecco una visualizzazione di tutte le operazioni che influiscono sull'indirizzo 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

Per leggere questo output, tenere presente che le voci vengono scaricate a partire da quella più recente. Di conseguenza, questo log mostra che il thread 0xB4 ha allocato un'area di grandi dimensioni in cui ha committato una pagina. Successivamente decommette la pagina e quindi rilascia l'intera area virtuale.

Vedere anche

Application Verifier - Panoramica

Application Verifier - Testing Applications

Application Verifier - Test in Application Verifier

Application Verifier - Codici di arresto e definizioni

Application Verifier - Domande frequenti