Verifica applicazione - Arresta il verifica dell'applicazione di debug
Installazione e installazione del debugger
Alcune azioni di verifica applicazione possono causare la generazione di un'eccezione. Il debugger deve essere impostato per rilevare queste eccezioni nella seconda possibilità, perché application Verifier stesso gestirà le prime eccezioni di probabilità.
Le eccezioni generate sono di tre tipi:
Viene generata un'eccezione di violazione di accesso (0xC0000005) se l'opzione heap rileva un overrun del buffer heap. In alcuni casi, l'opzione Controlla l'utilizzo del percorso di sistema può causare anche una violazione di accesso.
Viene generata un'eccezione di handle non valida (0xC0000008) quando l'opzione Rileva l'utilizzo dell'handle non valido rileva un'operazione di handle non valida.
Viene generata un'eccezione di overflow dello stack (0xC00000FD) quando l'opzione Check for appropriate stack rileva che lo stack iniziale era troppo breve.
Un modo per preparare questi eventi consiste nell'avviare il debugger in una riga di comando come indicato di seguito:
windbg -xd av -xd ch -xd sov ApplicationCommandLine
oppure
cdb -xd av -xd ch -xd sov ApplicationCommandLine
Se è già stato avviato il debugger, è 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ù energia 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 è riuscita.
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 in fase di 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 usato:
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 con Debug di Windows
Configurazione del verificatore dell'applicazione con il debugger usando 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 Flag specifica le opzioni di verifica applicazione desiderate 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 BASSA RISORSA |
00000020 | CONTROLLI TLS |
00000040 | STACK SPORCHI |
00000200 | API PERICOLOSE |
00001000 | CONTROLLI 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 del verificatore dell'applicazione e le informazioni sulle interruzioni correnti e precedenti del verificatore dell'applicazione.
!avrf –vs { Length | -aAddress }
Visualizza il log delle operazioni dello spazio virtuale. La lunghezza specifica il numero di record da visualizzare a partire dall'ultima. L'indirizzo specifica l'indirizzo virtuale. Verranno visualizzati i record delle operazioni virtuali contenenti questo indirizzo virtuale.
!avrf -hp { Length | -a Address }
Visualizza il log operazioni dell'heap. L'indirizzo specifica l'indirizzo 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. La lunghezza specifica il numero di record da visualizzare a partire dall'ultima. L'indirizzo specifica l'indirizzo della sezione critica. I record per la sezione critica specifica vengono visualizzati quando viene specificato Indirizzo.
!avrf -dlls [ Length ]
Visualizza il log di caricamento/scaricamento della DLL. La lunghezza 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. Verifica applicazione 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 figlio, vengono visualizzati anche le dimensioni dello stack e i flag CreateThread specificati dall'elemento padre. 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à del thread, la modifica della priorità del thread, la registrazione di messaggi di thread, l'inizializzazione di COM e l'inizializzazione di COM dall'interno del 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 Di lettura/writer sottile (SRW). Specificando Indirizzo verranno visualizzati i record relativi all'indirizzo di blocco SRW. Quando la lunghezza viene specificata insieme all'indirizzo , vengono visualizzati tutti i blocchi SRW all'interno dell'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 in sospeso. Queste risorse possono o meno essere perdite in un determinato momento. Se si specifica ModuleName (inclusa l'estensione) vengono visualizzate tutte le risorse in sospeso nel modulo specificato. Se si specifica ResourceType, vengono visualizzate le risorse in sospeso di quel particolare tipo di risorsa. Specificando Address dumps record delle risorse in sospeso con tale 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 prenotazioni virtuali
- BSTR: visualizza le allocazioni BSTR
- Registro di sistema: viene visualizzata la chiave del Registro di sistema
- Alimentazione: visualizza gli oggetti di notifica alimentazione
- 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 un'analisi 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 inserimento degli errori. Quando !avrf -flt
viene usato senza parametri aggiuntivi, vengono visualizzate le impostazioni di inserimento degli errori correnti. EventType specifica il numero di tipo dell'evento. Probabilità specifica la frequenza con cui l'evento avrà esito negativo. Può trattarsi di qualsiasi numero intero compreso tra 0 e 1.000.000 (0xF4240).
!avrf -flt break EventType
Fa sì che Application Verifier si interrompa nel debugger ogni volta che questo errore viene inserito.
!avrf -flt stacks Length
Visualizza il numero di tracce dello stack di lunghezza 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 si usa -trg 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 di destinazione o esclusione. Il modulo deve includere il nome completo del modulo, inclusa l'estensione .exe o .dll. Le informazioni sul percorso non devono essere incluse. Se si specificano tutti, tutti gli intervalli di destinazione o gli intervalli di esclusione devono essere reimpostati.
!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 di destinazione o esclusione. Il modulo deve includere il nome completo del modulo, inclusa l'estensione .exe o .dll. Le informazioni sul percorso non devono essere incluse. Se si specificano tutti, tutti gli intervalli di destinazione o gli intervalli di esclusione devono essere reimpostati. Se si specifica Ora, tutti gli errori vengono eliminati per Time milliseconds after execution resumes (Tempo millisecondi dopo la ripresa dell'esecuzione).
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, visualizza le opzioni di Verifica applicazione correnti.
L'estensione !avrf usa il Exts.dll nel debugger.
Se si è verificato un arresto dell'applicazione, 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.
Viene visualizzato innanzitutto 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 l'arresto dell'applicazione verifier può essere continuato. Dopo aver compreso cosa è emerso, è 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.
È possibile esaminare alcuni log a questo punto. Al termine, usare il comando g (Go) per avviare di nuovo 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'applicazione verifica non può essere continuato. L'errore è troppo grave perché il processo continui a essere in esecuzione e non esiste alcun modo per Application Verifier per salvare il processo.
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 (Restart Target Application). In alternativa, potrebbe essere preferibile terminare la sessione di Application Verifier e iniziare a correggere i bug nel codice.
Errori della sezione critica di debug
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.
È necessario associare i simboli con le informazioni sul tipo, in particolare per ntdll.dll.
La sintassi per questa estensione è:
!cs [-s]: eseguire il dump di tutte le sezioni critiche attive nel processo corrente.
!cs [-s] address - sezione dump critical in questo indirizzo.
!cs [-s] -d address - sezione dump critical corrispondente a DebugInfo in questo indirizzo.
-s eseguirà il dump dell'analisi 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
Eseguire il dump delle informazioni su una sezione critica usando il relativo indirizzo, inclusa l'analisi 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
Eseguire il dump delle informazioni su una sezione critica usando 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 usando l'indirizzo delle informazioni di debug, inclusa l'analisi 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
.....
Eseguire il dump delle informazioni su tutte le sezioni critiche attive del processo corrente, inclusa l'analisi 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
Errori di eccezione di debug
Il log delle eccezioni registra tutte le eccezioni che si sono verificate nel processo di destinazione.
È possibile usare il comando !avrf -ex Length extension per visualizzare le ultime eccezioni; Length specifica il numero di eccezioni. Se Length viene omesso, vengono visualizzate tutte le eccezioni.
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 analisi 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 debugger in modalità utente visualizza sempre informazioni sul processo di debug corrente.
Esempi:
Informazioni sul dump 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.
Informazioni di dump 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.
Informazioni di dump 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 heap
Estensione del debugger di verifica heap
L'estensione del debugger di verifica heap fa parte dell'estensione !heap (estensione del debugger heap NT). È possibile ottenere una semplice guida con !heap -? o più esteso con !heap -p -? . L'estensione corrente non rileva autonomamente se l'heap di pagina è abilitato per un processo e agire di conseguenza. Per ora l'utente dell'estensione deve conoscere che l'heap di pagina è abilitato e usa i comandi preceduti da !heap -p . Per altre informazioni sull'estensione !htrace, vedere !heap nella documentazione del debugger.
!heap -p
Gli indirizzi dump di tutti gli heaps di tutte le pagine complete create nel processo.
!heap -p -h ADDRESS-OF-HEAP
Dump completo dell'heap di pagina completa in ADDRESS-OF-HEAP.
!heap -p -a ADDRESS
Prova a capire se è presente un blocco heap in ADDRESS. Questo valore non deve essere l'indirizzo dell'inizio del blocco. Il comando è utile se non esiste alcun indizio sulla natura di un'area di memoria.
Log operazioni heap
Il log operazioni heap tiene traccia di tutte le routine heap. Questi includono HeapAlloc, HeapReAlloc e HeapFree.
È possibile usare il !avrf -hp Length
comando estensione per visualizzare gli ultimi record; La lunghezza specifica il numero di record.
È possibile usare !avrf -hp -a Address
per visualizzare tutte le operazioni di spazio heap interessate dall'indirizzo specificato. Per un'operazione di allocazione, è sufficiente che l'indirizzo sia contenuto nel blocco heap allocato. Per un'operazione gratuita, è necessario specificare l'indirizzo esatto dell'inizio del blocco.
Per ogni voce del log, vengono visualizzate le informazioni seguenti:
- Funzione heap denominata .
- ID thread del thread che ha chiamato la routine.
- Indirizzo coinvolto nella chiamata: questo è l'indirizzo restituito da una routine di allocazione o passato a una routine gratuita.
- Dimensioni dell'area coinvolte nella chiamata.
- Traccia dello stack della chiamata.
Per prima cosa vengono visualizzate le voci più recenti.
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 essere rilevati. Alcuni di loro richiedono un po 'di lavoro detective per ottenere l'intera immagine.
Violazione di accesso nella pagina non accessibile
Ciò avviene quando l'heap a pagina completa è 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, gratuita dell'utente, reale) il gestore heap della pagina verifica se il blocco ha tutti i modelli di riempimento intatti e l'intestazione del blocco ha dati coerenti. Se questo non è il caso in cui si otterrà un controllore.
Se il blocco è un blocco heap a pagina completa (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 chiaro, è necessario individuare l'indirizzo iniziale per l'intestazione del blocco. È possibile trovare l'indirizzo iniziale scaricando 30-40 byte sotto l'indirizzo segnalato e cercare i modelli magic start/end per un'intestazione di blocco (ABCDAAAA, ABCDBBBBBB, ABCDAAA9, ABCDBBBA).
L'intestazione darà tutte le informazioni necessarie per comprendere l'errore. In particolare, i modelli magici diranno se il blocco è allocato o libero se è un heap di pagina chiaro o un blocco heap a pagina completa. Le informazioni qui devono essere confrontate con attenzione con la chiamata offensiva.
Ad esempio, se viene effettuata una chiamata a HeapFree con l'indirizzo di un blocco più quattro byte, verrà visualizzato 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, quindi quello nella chiamata.
Puntatori di riempimento speciali
Il gestore heap della pagina riempie l'allocazione utente con valori che verranno visualizzati come puntatori del kernel. Ciò accade quando il blocco viene liberato (il valore di riempimento è F0) e quando il blocco viene allocato ma non viene effettuata alcuna richiesta per il blocco da zero (il valore di riempimento è E0 per l'heap della pagina leggera e C0 per l'heap di pagina completa). Le allocazioni non zero sono tipiche per gli utenti malloc/nuovi utenti. Se si verifica un errore (violazione di accesso) in cui viene tentato un tentativo di lettura/scrittura a indirizzi come F0F0F0F0, E0E0E0E0, C0C0C0C0 probabilmente si raggiunge uno di questi casi.
Una lettura/scrittura in F0F0F0F0 significa che un blocco è stato usato dopo che è stato liberato. Purtroppo avrai bisogno di un lavoro di detective per scoprire quale blocco ha causato questo. È necessario ottenere la traccia dello stack dell'errore e quindi esaminare il codice per le funzioni nello stack. Uno di essi potrebbe fare un presupposto sbagliato su un'allocazione in vita.
Una lettura/scrittura in E0E0E0E0/C0C0C0C0 significa che l'applicazione non inizializza correttamente l'allocazione. Ciò richiede anche l'ispezione del codice delle funzioni nella traccia dello stack corrente. Ecco un esempio per questo tipo di errore. In un processo di test una violazione di accesso durante l'esecuzione di un heapFree sull'indirizzo E0E0E0E0 è stato notato. Si è verificato che il test allocato una struttura, non l'inizializzava correttamente e quindi chiamato il distruttore dell'oggetto. Poiché un determinato campo non era null (aveva E0E0E0E0 in esso) ha chiamato eliminarlo.
Dettagli tecnici dell'heap di pagina
Per rilevare i danneggiamenti dell'heap (overflow o sottoflow), AppVerifier modifica il modo in cui la memoria viene allocata spaziando 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à heap della pagina ed è l'impostazione predefinita), AppVerifier utilizzerà una grande quantità di memoria virtuale, ma ha il vantaggio che gli eventi di danneggiamento heap vengono memorizzati nella cache in tempo reale quando si verifica l'overflow o il sottoflow. Tenere presente che la memoria in questa modalità sarà simile a questa [AppVerifier Read-Only pagina Heap (4k)] [Quantità di memoria richiesta dall'applicazione in fase di test] o come questa [Quantità di memoria richiesta dall'applicazione in fase di test] [AppVerifier Read-Only Pagina Heap (4k)].
Il controllo heap inserisce una pagina di protezione all'inizio o alla fine dell'allocazione a seconda della proprietà Indietro. Se l'opzione Indietro è impostata su False, ovvero l'impostazione predefinita, verrà posizionata una pagina di protezione alla fine dell'allocazione per rilevare l'overrun del buffer. Se è impostato su True, la pagina guard viene posizionata all'inizio dell'allocazione per rilevare le sottorun del buffer.
Quando si spazia la memoria richiesta con tag speciali (abilitata cancellando l'elemento della casella di controllo "Full" nelle proprietà heap), AppVerifier controlla e avvisa quando questa memoria viene rilasciata. Il problema principale nell'uso di questa tecnica è che ci sono alcuni casi in cui il danneggiamento della memoria verrà rilevato solo quando la memoria viene rilasciata (la quantità minima di blocco di memoria è 8 byte), quindi quando in una variabile da 3 byte o si verifica un overflow a 5 byte non verrà rilevato immediatamente.
In un evento underflow, verrà eseguito un tentativo di scrittura in una pagina di 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 a pagina completa rileverà anche questi errori perché usa pagine di riempimento+guard. Il motivo per cui si userebbe l'heap della pagina leggera è se il computer non può tollerare i vincoli di memoria elevata dell'heap di pagina completa.
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 di heap normali (leggeri) anziché la modalità completa a causa della riduzione delle prestazioni. Tuttavia, quando si verifica un problema, attivare l'heap a pagina completa per analizzare ulteriormente.
Le applicazioni che usano heaps personalizzate (un heap che ignora l'implementazione del sistema operativo dell'heap) potrebbero non ottenere il vantaggio completo dell'uso dell'heap della pagina o potrebbero anche funzionare correttamente quando è abilitato.
Errori di memoria di debug
Estensione del debugger di verifica della memoria
Il log operazioni dello spazio virtuale tiene traccia di tutte le routine che modificano lo spazio virtuale di un processo in qualsiasi modo. Includono VirtualAlloc, VirtualFree, MapViewOfFile e UnmapViewOfFile.
È possibile usare il !avrf -vs Length
comando estensione per visualizzare gli ultimi record; La lunghezza specifica il numero di record.
È possibile usare !avrf -vs -a Address per visualizzare tutte le operazioni di spazio virtuale che hanno interessato l'indirizzo specificato. Per un'allocazione, è sufficiente che l'indirizzo sia contenuto nel blocco allocato. Per un valore gratuito, è necessario specificare l'indirizzo esatto dell'inizio dell'area.
Per ogni voce del log, vengono visualizzate le informazioni seguenti:
- Funzione denominata
- ID thread del thread che ha chiamato la routine
- Indirizzo coinvolto nella chiamata: questo è l'indirizzo restituito da una routine di allocazione o passato a una routine gratuita
- Dimensioni dell'area coinvolte nella chiamata
- Tipo di operazione di memoria (parametro AllocationType)
- Tipo di protezione richiesta
- Analisi dello stack della chiamata
Esempio
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
Può essere visualizzato dall'output che il thread 0xB4 prima decommettere una pagina e quindi rilasciare 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 dump a partire da quella più recente. Pertanto, questo log mostra che il thread 0xB4 allocato un'area di grandi dimensioni in cui è stato eseguito il commit di una pagina. Successivamente decommette la pagina e quindi rilascia l'intera area virtuale.
Vedere anche
Verifica dell'applicazione - Panoramica
Verifica dell'applicazione - Test delle applicazioni
Verifica dell'applicazione - Test all'interno di Application Verifier