Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Instalace a nastavení ladicího programu
Některé akce ověřovatele aplikace můžou způsobit vyvolání výjimky. Ladicí program musí být nastaven k zachycení těchto výjimek při druhé příležitosti, protože samotný Application Verifier bude zpracovávat výjimky při první příležitosti.
Výjimky vyvolané jsou ze tří typů:
Pokud volba haldy zjistí přetečení vyrovnávací paměti haldy, dojde k vygenerování výjimky porušení přístupu (0xC0000005). V některých případech může možnost zkontrolovat použití systémové cesty také způsobit narušení přístupu.
Pokud možnost Zjistit neplatné použití popisovače zjistí neplatnou operaci popisovače, vygeneruje se neplatná výjimka popisovače (0xC0000008).
Výjimka přetečení zásobníku (0xC00000FD) je vygenerována, když možnost kontroly dostatečné velikosti zásobníku zjistí, že počáteční zásobník byl příliš krátký.
Jedním ze způsobů, jak se připravit na tyto události, je spustit ladicí program na příkazovém řádku následujícím způsobem:
windbg -xd av -xd ch -xd sov ApplicationCommandLine
nebo
cdb -xd av -xd ch -xd sov ApplicationCommandLine
Pokud jste už ladicí program spustili, můžete pomocí příkazu sxd (Set Exceptions) zachytit všechna porušení přístupu, neplatné popisovače a přetečení zásobníku jako výjimky druhé šance:
0:000> sxd av
0:000> sxd ch
0:000> sxd sov 1
Teoreticky je možné ovládat Application Verifier prostřednictvím debuggeru jádra. To se ale nedoporučuje – vyžaduje časté použití příkazů .process a .pagein, ale neposkytuje vám větší možnosti než použití ladicího programu v uživatelském režimu.
Instalování nástrojů pro ladění
Pokud si chcete stáhnout nejnovější verzi nástrojů, přečtěte si téma Stažení nástrojů pro ladění pro Windows.
Konfigurace hardwaru pro ladění User-Mode
Ladění v uživatelském režimu se obvykle provádí na jednom počítači: Ladicí program se spouští ve stejném počítači jako aplikace, která selhala.
V takovém případě se nevyžaduje žádné konkrétní nastavení hardwaru. V tomto tématu jsou v tomto případě zaměnitelné termíny hostitelského počítače a cílového počítače.
Konfigurace softwaru pro ladění User-Mode
Základní User-Mode Konfigurace – Před zahájením ladění v uživatelském režimu je nutné stáhnout potřebné soubory symbolů a nastavit určité proměnné prostředí.
Soubory symbolů
Je nutné stáhnout soubory symbolů pro proces uživatelského režimu, který je laděn. Pokud se jedná o aplikaci, kterou jste napsali, měla by být sestavena se soubory s úplnými symboly. Pokud se jedná o komerční aplikaci, mohou být soubory symbolů k dispozici na webovém serveru nebo ke stažení, obraťte se na výrobce.
Pokud provádíte vzdálené ladění, umístění souboru symbolů závisí na metodě, kterou používáte:
Pokud provádíte vzdálené ladění prostřednictvím ladicího programu, měly by být soubory symbolů na počítači s ladicím serverem.
Pokud provádíte vzdálené ladění prostřednictvím remote.exe, soubory symbolů by měly být v počítači s ladicím programem.
Pokud provádíte vzdálené ladění prostřednictvím procesového serveru nebo serveru připojení KD, soubory symbolů by měly být v počítači s inteligentním klientem.
Pokud ovládáte debugger uživatelského režimu z jádra, musí být soubory symbolů na obou počítačích.
Konfigurace proměnných prostředí
Ladicí program používá různé proměnné prostředí k určení několika důležitých nastavení.
Další informace o ladicích programech naleznete v tématu Začínáme s laděním systému Windows.
Konfigurace Application Verifier s Debuggerem pomocí příkazového řádku
Ke konfiguraci nástroje Application Verifier můžete použít příkazový řádek CDB nebo NTSD.
Použijte následující příkazový řádek:
cdb OtherOptions -vf:Flags Target
Kde Target je název cílové aplikace a příznaky určuje požadované možnosti ověření aplikace, které se mají použít pro tento cíl.
Příznaky by měly být součtem bitů představujících požadované možnosti. Jednotlivé bitové hodnoty jsou následující:
| Hodnota příznaku | Význam |
|---|---|
| 00000001 | KONTROLY HALDY |
| 00000004 | ZPRACOVÁNÍ KONTROL |
| 00000008 | Nízké prostředky kontroly SIM karet |
| 00000020 | Kontroly TLS |
| 00000040 | NEČISTÉ ZÁSOBNÍKY |
| 00000200 | NEBEZPEČNÉ API |
| 00001000 | KONTROLY VÝJIMEK |
| 00002000 | KONTROLY PAMĚTI |
| 00020000 | RŮZNÉ KONTROLY |
| 00040000 | Kontrola zámků |
Ladění pomocí !avrf
Rozšíření !avrf řídí nastavení aplikace Verifier a zobrazuje řadu výstupů vytvořených ověřovatelem aplikací. Další informace o rozšíření !arvrf najdete v dokumentaci pro ladicí program, viz !avrf.
Syntaxe
!avrf
Příkaz !avrf bez parametrů zobrazí nastavení Ověřovatele aplikace a informace o aktuálních a předchozích přerušeních Ověřovatele aplikace, pokud existují.
!avrf –vs { Length | -aAddress }
Zobrazí protokol operací virtuálního prostoru. Délka určuje počet záznamů, které se mají zobrazit od nejnovějšího. Adresa určuje virtuální adresu. Zobrazí se záznamy virtuálních operací obsahujících tuto virtuální adresu.
!avrf -hp { Length | -a Address }
Zobrazí protokol operací haldy. Adresa určuje adresu haldy. Zobrazí se záznamy úloh haldy obsahující tuto adresu haldy.
!avrf -cs { Length | -a Address }
Zobrazí protokol odstranění kritické sekce. Délka určuje počet záznamů, které se mají zobrazit od nejnovějšího. Adresa určuje kritickou adresu oddílu. Záznamy pro konkrétní kritický oddíl se zobrazí, když je zadána adresa.
!avrf -dlls [ Length ]
Zobrazí protokol načítání a uvolnění knihovny DLL. Délka určuje počet záznamů, které se mají zobrazit od nejnovějšího.
!avrf -trm
Zobrazí protokol všech ukončených a pozastavených vláken.
!avrf -ex [ Length ]
Zobrazí protokol výjimek. Ověřovatel aplikace sleduje všechny výjimky, ke které v aplikaci dochází.
!avrf -threads [ ThreadID ]
Zobrazí informace o vláknech v cílovém procesu. U podřízených vláken se zobrazí také velikost zásobníku a příznaky CreateThread určené nadřazeným objektem. Zadání ID vlákna zobrazí informace pouze pro dané vlákno.
!avrf -tp [ ThreadID ]
Zobrazí protokol fondu vláken. Tento protokol může obsahovat zásobníkové stopy pro různé operace, jako je změna masky spřažení vláken, změna priority vlákna, odesílání zpráv vláknům, inicializace COM a zrušení inicializace COM v rámci zpětného volání fondu vláken. Zadání ID vlákna zobrazí informace pouze pro dané vlákno.
!avrf -srw [ Address | Address Length ] [ -stats ]
Zobrazí protokol zařízení Slim Reader/Writer (SRW). Zadáním adresy se zobrazí záznamy týkající se dané adresy zámku SRW. Pokud je zadána délka spolu s adresou , zobrazí se všechny SRW zámky v daném rozsahu adres. Možnost -stats vypíše statistiky zámku SRW.
!avrf -leak [ -m ModuleName ] [ -r ResourceType ] [ -a Address ] [ -t ]
Zobrazí protokol nevyřízených prostředků. Tyto prostředky mohou nebo nemusí být úniky v kterémkoli okamžiku. Zadání ModuleName (včetně přípony) zobrazí všechny nevyřízené prostředky v zadaném modulu. Určení typu ResourceType zobrazí vynikající prostředky daného typu prostředku. Specifikování adresy vyhotoví záznamy zbývajících prostředků s danou adresou. ResourceType může být jedna z následujících možností:
- Halda: Zobrazí přidělení haldy pomocí rozhraní API haldy Win32.
- Místní: Zobrazí místní nebo globální přidělení.
- CRT: Zobrazuje přidělení pomocí CRT API.
- Virtuální: Zobrazí virtuální rezervace.
- BSTR: Zobrazí alokace BSTR.
- Registr: Zobrazí se otevřený klíč registru.
- Napájení: Zobrazí objekty oznámení o napájení.
- Popisovač: Zobrazí přidělení popisovačů pro vlákna, soubory a události.
!avrf –trace TraceIndex
Zobrazí trasování zásobníku pro zadaný index trasování. Některé struktury používají toto 16bitové číslo indexu k identifikaci trasování zásobníku. Tento index odkazuje na umístění v databázi sledování zásobníku. Pokud takovou strukturu analyzujete, bude tato syntaxe užitečná.
!avrf -cnt
Zobrazí seznam globálních čítačů.
!avrf -brk [ BreakEventType ]
Určuje, že se jedná o příkaz typu break-event. Pokud !avrf -brk se použije bez dalších parametrů, zobrazí se nastavení události přerušení. BreakEventType určuje typ události přerušení. Pro seznam možných typů použijte !avrf -brk.
!avrf -flt [ EventTypeProbability ]
Určuje, že je to příkaz pro vstřikování chyb. Pokud je !avrf -flt použito bez dalších parametrů, zobrazí se aktuální nastavení injekce chyb. Typ události určuje číslo typu události. Pravděpodobnost určuje frekvenci selhání události. Může to být libovolné celé číslo od 0 do 1 000 000 (0xF4240).
!avrf -flt break EventType
Způsobí, že nástroj Application Verifier spustí ladicí program pokaždé, když je tato chyba injektována.
!avrf -flt stacks Length
Zobrazí počet délek trasování zásobníku pro nejnovější operace s injekcí chyb.
!avrf -trg [ StartEnd | dll Module | all ]
Určuje, že jde o příkaz pro cílový rozsah. Pokud se -trg použije bez dalších parametrů, zobrazí se aktuální cílové oblasti. Začátek určuje počáteční adresu cílového rozsahu nebo rozsahu vyloučení. End určuje koncovou adresu cílového rozsahu nebo rozsahu vyloučení. Modul určuje název modulu, který se má cílit nebo vyloučit. Modul by měl obsahovat úplný název modulu, včetně .exe nebo rozšíření .dll. Informace o cestě by neměly být zahrnuty. Určení všech příčin resetování všech cílových rozsahů nebo rozsahů vyloučení
!avrf -skp [ StartEnd | dll Module | all | Time ]
Určuje, že se jedná o příkaz rozsahu vyloučení. Začátek určuje počáteční adresu cílového rozsahu nebo rozsahu vyloučení. End určuje koncovou adresu cílového rozsahu nebo rozsahu vyloučení. Modul určuje název modulu, který se má cílit nebo vyloučit. Modul by měl obsahovat úplný název modulu, včetně .exe nebo rozšíření .dll. Informace o cestě by neměly být zahrnuty. Určení všech příčin resetování všech cílových rozsahů nebo rozsahů vyloučení Určení času způsobí potlačení všech chyb na dobu měřenou v milisekundách od doby, kdy se provádění obnoví.
Následuje výstup poskytnutý příkazem !avrf v ladicím programu.
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 rozšíření komentáře
Pokud se rozšíření !avrf používá bez parametrů, zobrazí aktuální možnosti ověření aplikace.
Rozšíření !avrf používá Exts.dll v ladicím programu.
Pokud došlo k zastavení ověřovatele aplikace, rozšíření !avrf bez parametrů odhalí povahu zastavení a jeho příčinu.
Pokud chybí symboly pro ntdll.dll a verifier.dll, rozšíření !avrf vygeneruje chybovou zprávu.
Souvislé a neskontinuovatelné zastávky
Ladění přerušitelných zastavení
Tady je příklad neplatné výjimky popisovače, která byla vyvolána možností Zjistit neplatné použití popisovače.
Nejprve se zobrazí následující zpráva:
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
Všimněte si, že zpráva vám oznámí, že zastavení ověřovatele aplikace může pokračovat. Jakmile pochopíte, co se stalo, můžete pokračovat ve spuštění cílové aplikace.
Nejprve byste měli použít příponu !avrf. Tím získáte informace o aktuálním selhání:
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).
Poslední řádek tohoto zobrazení shrnuje problém.
Možná se v tomto okamžiku budete chtít podívat na některé protokoly. Po dokončení spusťte aplikaci znovu pomocí příkazu g (Go):
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
V tomto případě vám zpráva oznámí, že nelze pokračovat s touto chybou programu Application Verifier. Chyba je příliš závažná, aby proces mohl pokračovat, a neexistuje způsob, jak by mohl Application Verifier proces zachránit.
Rozšíření !avrf lze použít k poskytnutí informací o aktuálním selhání:
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.
Poslední řádek tohoto zobrazení shrnuje problém.
V tuto chvíli se můžete také podívat na některé protokoly. V tuto chvíli můžete použít příkaz .restart (Restartovat cílovou aplikaci). Nebo možná budete chtít ukončit relaci Ověření aplikace a začít opravovat chyby v kódu.
Ladění chyb kritických sekcí
!cs – rozšíření ladicího programu
!cs lze použít v ladicím programu v uživatelském režimu i v ladicím programu jádra k zobrazení informací o důležitých oddílech v aktuálním procesu. Další informace o rozšíření !cs najdete v dokumentaci ladicího programu.
Jsou vyžadovány odpovídající symboly s informacemi o typu, zejména pro ntdll.dll.
Syntaxe pro toto rozšíření je:
!cs [-s] – vypište všechny aktivní důležité oddíly v aktuálním procesu.
!cs [-s] adresa – výpis kritické sekce na této adrese.
!cs [-s] -d adresu – výpis kritické sekce odpovídající DebugInfo na této adrese.
-s vypíše výpis zásobníku inicializace kritické sekce, pokud je k dispozici.
Příklady:
Výpis informací o kritickém oddílu s použitím jeho adresy
0:001> ! cs 0x7803B0F8
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo = 0x6A262080
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Výpis informací o kritické části pomocí adresy, včetně trasování zásobníku inicializace
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
Výpis informací o kritické sekci pomocí adresy ladicích informací.
0:001> !cs -d 0x6A262080
DebugInfo = 0x6A262080
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Výpis informací o kritické sekci pomocí adresy ladicích informací, včetně zásobníkového trasování inicializace.
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
Výpis informací o všech aktivních kritických sekcích v aktuálním procesu
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
.....
Výpis informací o všech aktivních kritických sekcích v aktuálním procesu, včetně trasování inicializačního zásobníku
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
Vyhledávání chyb způsobených výjimkami
Protokol výjimek zaznamenává všechny výjimky, ke kterým došlo v cílovém procesu.
K zobrazení posledních několika výjimek můžete použít příkaz rozšíření !avrf -ex Length; Délka určuje počet výjimek. Pokud je délka vynechána, zobrazí se všechny výjimky.
Tady je příklad:
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.
Ladění řeší chyby
Nástroj !htrace lze použít jak v uživatelském režimu ladicího programu, tak v ladicím programu jádra k zobrazení informací o trasování zásobníku pro jednotlivé nebo všechny popisovače v procesu. Tyto informace jsou k dispozici, pokud je pro proces povolené trasování popisovačů – pokud je v ověřovateli aplikace povolená kontrola popisovačů. Výpisy zásobníku se ukládají pokaždé, když proces otevírá nebo zavírá popisovač, nebo pokud se odkazuje na neplatný popisovač. Další informace o rozšíření !htrace naleznete v dokumentaci k ladicímu programu.
Syntaxe ladicího programu jádra pro toto rozšíření je:
!htrace [ handle [process] ]
Pokud popisovač není zadaný nebo je 0, zobrazí se informace o všech popisovačích procesu. Pokud není zadaný proces, použije se aktuální proces.
Syntaxe ladicího programu v uživatelském režimu je:
!htrace [handle]
Rozšíření debuggeru v uživatelském režimu vždy zobrazuje informace o aktuálním procesu debuggovaného programu.
Příklady:
Výpis informací o zpracování 7CC v procesu 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.
Výpis informací o všech handlech v procesu 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.
Výpis informací o zpracování 7DC v aktuálním procesu
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.
Ladění chyb haldy
Rozšíření ladicího programu pro ověřování paměťové haldy
Rozšíření ladicího programu pro ověření haldy je součástí rozšíření !haldy (rozšíření ladicího programu haldy NT). Jednoduchou nápovědu lze získat pomocí !heap -? nebo rozsáhlejší pomocí !heap -p -? . Aktuální rozšíření samo nezjistí, zda je pro proces povolená halda stránky, ani podle toho nejedná. Prozatím musí uživatel rozšíření vědět, že page heap je aktivována a používat příkazy s předponou !heap -p . Další informace o rozšíření !htrace najdete v dokumentaci k ladicímu programu u příkazu !heap.
!heap -p
Výpisy adres všech celostránkových hald vytvořených během procesu.
!heap -p -h ADDRESS-OF-HEAP
Úplný výpis haldy celé stránky na ADRESE-OF-HEAP.
!heap -p -a ADDRESS
Snaží se zjistit, jestli je blok haldy na ADRESE. Tato hodnota nemusí být adresou začátku bloku. Příkaz je užitečný, pokud neexistuje žádná stopa o povaze paměťové oblasti.
Protokol operací haldy
Protokol operace haldy sleduje všechny rutiny haldy. Patří mezi ně HeapAlloc, HeapReAlloc a HeapFree.
Pomocí příkazu rozšíření můžete !avrf -hp Length zobrazit posledních několik záznamů; Délka určuje počet záznamů.
Můžete použít !avrf -hp -a Address k zobrazení všech operací haldového prostoru, které ovlivnily zadanou adresu. U operace přidělení stačí, aby adresa byla obsažena v bloku přidělené haldy. Pro bezplatnou operaci musí být uvedena přesná adresa začátku bloku.
Pro každou položku v protokolu se zobrazí následující informace:
- Volá se funkce haldy.
- ID vlákna, které volalo rutinu.
- Adresa zapojená do volání – to je adresa vrácená rutinou přidělení nebo předána do bezplatné rutiny.
- Velikost oblasti zapojené do hovoru.
- Výpis zásobníku volání.
Jako první se zobrazí nejnovější položky.
V tomto příkladu se zobrazí dvě nejnovější položky:
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
Typické scénáře ladění
Může docházet k několika scénářům selhání. Některé z nich vyžadují poměrně hodně pátrání, aby získaly ucelený přehled.
Narušení přístupu na nepřístupné stránce
K tomu dochází, když je povolena celostránková halda a testovaná aplikace přistupuje mimo konec vyrovnávací paměti. Může se také stát, když se dotkne uvolněného bloku. Abyste pochopili, jaká je povaha adresy, na které došlo k výjimce, musíte použít:
!heap –p –a ADDRESS-OF-AV
Poškozená bloková zpráva
V několika okamžicích během životnosti přidělení (přidělení, uvolnění uživatelem, skutečné uvolnění) správce vyrovnávací paměti stránky zkontroluje, zda blok zachovává všechny výplňové vzory a záhlaví bloku má konzistentní data. Pokud tomu tak není, dojde k zastavení ověřování.
Pokud je blok blok haldy pro celou stránku (například pokud víte, že je pro všechna přidělení povolena halda pro celou stránku), můžete pomocí "!heap –p –a ADDRESS" zjistit, jaké jsou vlastnosti bloku.
Pokud je blok blokem haldy lehké stránky, musíte zjistit počáteční adresu záhlaví bloku. Počáteční adresu můžete zjistit analýzou 30-40 bajtů pod hlášenou adresou a hledáním specifických vzorů začátku/konce pro hlavičku bloku (ABCDAAAA, ABCDBBBB, ABCDAAA9, ABCDBBBA).
Hlavička poskytne všechny informace, které potřebujete k pochopení selhání. Magické vzory ukáží, zda je blok přidělen nebo volný, a zda se jedná o lehkou stránkovou haldu nebo celostránkový blok haldy. Zde uvedené informace musí být pečlivě sladěny s problematickým hovorem.
Pokud je například volání heapFree provedeno s adresou bloku plus čtyři bajty, dostanete poškozenou zprávu. Záhlaví bloku bude vypadat dobře, ale budete si muset všimnout, že první bajt za koncem záhlaví (první bajt po magické hodnotě 0xDCBAXXXX) má jinou adresu než ten ve volání.
Speciální ukazatele výplně
Správce stránkovací haldy vyplní přidělení uživatele hodnotami, které budou vypadat jako ukazatele jádra. K tomu dochází, když se blok uvolní (hodnota výplně je F0) a když se blok přidělí, ale neexistuje žádný požadavek, aby blok byl nulový (hodnota výplně je E0 pro světelný zásobník stránek a C0 pro úplný zásobník stránek). Nenulová přidělení jsou typická pro uživatele funkcí malloc nebo new. Pokud dojde k selhání (narušení přístupu), kdy se pokusíte o čtení a zápis na adresy, jako je F0F0F0F0, E0E0E0E0, C0C0C0C0 pak pravděpodobně dosáhnete jednoho z těchto případů.
Čtení/zápis na adrese F0F0F0F0 znamená, že blok byl použit poté, co byl uvolněn. Bohužel budete potřebovat nějakou detektivovou práci, abyste zjistili, který blok to způsobil. Potřebujete získat výpis zásobníku selhání a pak zkontrolovat kód funkcí v rámci zásobníku. Jeden z nich může udělat špatný předpoklad o tom, že přidělení je stále platné.
Čtení/zápis v E0E0E0E0/C0C0C0C0 znamená, že aplikace správně neinicializovala alokaci. To také vyžaduje kontrolu kódu funkcí v aktuálním trasování zásobníku. Tady je příklad pro tento druh selhání. V testovacím procesu bylo zaznamenáno narušení přístupu při provádění HeapFree na adrese E0E0E0E0. Ukázalo se, že test přidělil strukturu, neinicializoval ji správně a pak volal destruktor objektu. Protože určité pole nemělo hodnotu null (obsahovalo E0E0E0E0), bylo na ně voláno odstranění.
Technické podrobnosti Page Heap
Aby bylo možné zjistit poškození paměti haldy (přetečení nebo podtečení), AppVerifier upraví způsob přidělování paměti tím, že vyplní požadovanou paměť buď úplnými nezapisovatelnými stránkami, nebo speciálními značkami před i za přidělenou pamětí. AppVerifier to provede načtením Verifier.dll do procesu, který se ověřuje, a přesměrováním některých rozhraní API haldy Win32 volané aplikací do odpovídajících rozhraní API Verifier.dll.
Při odsazení požadované paměti úplnými nezapisovatelnými stránkami (nastavení FULL je povoleno ve vlastnostech stránkovací haldy a je výchozím nastavením) bude AppVerifier využívat velké množství virtuální paměti. Má však výhodu, že události poškození haldy ukládá do mezipaměti v reálném čase při výskytu přetečení nebo podtečení. Vzpomeňte si, že paměť v tomto režimu bude vypadat buď takto [AppVerifier Read-Only stránka haldy (4k)] [Množství paměti požadované aplikací v rámci testu] nebo takto [Množství paměti požadované aplikací v testu] [AppVerifier Read-Only stránka haldy (4k)].
Kontrola haldy umístí ochrannou stránku na začátek nebo konec alokace v závislosti na vlastnosti Zpětný. Pokud je Backward nastaven na False, což je výchozí nastavení, umístí strážnou stránku na konec přidělení pro zachycení přetečení vyrovnávací paměti. Pokud je nastavena na hodnotu True, ochranná stránka se umístí na začátek přidělení, aby bylo možné zachytit podtečení vyrovnávací paměti.
Při vyplňování požadované paměti speciálními značkami (umožněno zrušením zaškrtnutí položky "Úplná" ve vlastnostech haldy), AppVerifier zkontroluje a upozorní vás při uvolnění této paměti. Hlavním problémem při použití této techniky je, že existují některé případy, kdy k poškození paměti dojde pouze při uvolnění paměti (minimální velikost paměťového bloku je 8 bajtů), takže pokud dojde k přetečení na 3-bajtové proměnné nebo na 5-bajtovém přetečení, nedojde okamžitě k jeho zjištění.
Při události podtečení se provede pokus o zápis na stránku Read-Only. Tím se aktivuje výjimka. Všimněte si, že tuto výjimku lze zachytit pouze v případě, že se cílová aplikace spouští v rámci ladicího programu. Všimněte si, že režim haldy celé stránky také rozpozná tyto chyby, protože používá odsazení+ strážné stránky. Důvodem, proč byste použili lehkou haldu stránek, je to, že váš počítač nedokáže tolerovat vysoké paměťové nároky haldy celé stránky.
U aplikací náročných na paměť, nebo v případě, že je nutné používat AppVerifier po delší časové úseky (například zátěžové testování), je lepší spustit lehké testy haldy místo plného režimu kvůli zhoršení výkonu. Pokud ale narazíte na problém, zapněte funkci "Full Page Heap" a prozkoumejte ji podrobněji.
Aplikace, které používají vlastní haldu (haldu, která obchází implementaci haldy operačního systému), nemusí získat plnou výhodu použití stránkové haldy nebo mohou dokonce selhávat, když je povolena.
Ladění chyb paměti
Rozšíření ověřovatele paměti v ladicím programu
Protokol operací virtuálního prostoru sleduje všechny rutiny, které upravují virtuální prostor procesu jakýmkoli způsobem. Patří mezi ně VirtualAlloc, VirtualFree, MapViewOfFile a UnmapViewOfFile.
Pomocí příkazu rozšíření můžete !avrf -vs Length zobrazit posledních několik záznamů; Délka určuje počet záznamů.
Pomocí funkce !avrf -vs -a Address můžete zobrazit všechny operace virtuálního prostoru, které ovlivnily zadanou adresu. Pro přidělení stačí, aby adresa byla obsažena v přiděleném bloku. Pro volnou musí být uvedena přesná adresa začátku oblasti.
Pro každou položku v protokolu se zobrazí následující informace:
- Volá se funkce.
- ID vlákna, které volalo rutinu
- Adresa zapojená do volání – jedná se o adresu vrácenou rutinou přidělení nebo předanou do bezplatné rutiny.
- Velikost oblasti zapojené do hovoru
- Typ operace paměti (parametr AllocationType)
- Požadovaný typ ochrany
- Trasování zásobníku volání
Příklady
Jako první se zobrazí nejnovější položky.
V následujícím příkladu jsou zobrazeny dvě nejnovější položky:
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
Je vidět z výstupu, že vlákno 0xB4 nejprve uvolnilo stránku a pak uvolnilo celou virtuální oblast.
Tady je zobrazení všech operací ovlivňujících 0x4BB1000 adresy:
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
Pokud si chcete tento výstup přečíst, mějte na paměti, že položky jsou vypisovány počínaje tou nejnovější. Tento protokol tedy ukazuje, že vlákno 0xB4 přidělilo velkou oblast, ve které potvrdilo stránku. Později uvolnil stránku a pak uvolnil celou virtuální oblast.
Viz také
Application Verifier – přehled
Application Verifier – Testování aplikací
Application Verifier – testy v rámci