Udostępnij przez


Weryfikator aplikacji — debugowanie weryfikatora aplikacji zatrzymuje się

Instalowanie i konfigurowanie debugera

Niektóre akcje weryfikatora aplikacji mogą spowodować zgłoszenie wyjątku. Debuger musi być ustawiony, aby przechwycić te wyjątki przy drugiej próbie, ponieważ Application Verifier będzie obsługiwał wyjątki pierwszej próby.

Zgłoszone wyjątki to trzy typy:

  • Wyjątek naruszenia dostępu (0xC0000005) jest generowany, jeśli opcja sterty wykryje przepełnienie bufora sterty. W niektórych przypadkach opcja Sprawdź użycie ścieżki systemowej może również spowodować naruszenie dostępu.

  • Nieprawidłowy wyjątek obsługi (0xC0000008) jest generowany, gdy opcja Wykryj nieprawidłowe użycie uchwytu wykryje nieprawidłową operację obsługi.

  • Wyjątek przepełnienia stosu (0xC00000FD) jest generowany, gdy opcja Sprawdź odpowiedni stos wykryje, że początkowy stos był zbyt krótki.

Jednym ze sposobów przygotowania do tych zdarzeń jest uruchomienie debugera w wierszu polecenia w następujący sposób:

windbg -xd av -xd ch -xd sov ApplicationCommandLine

lub

cdb -xd av -xd ch -xd sov ApplicationCommandLine

Jeśli już uruchomiono debuger, możesz użyć polecenia sxd (Konfiguracja wyjątków), aby przechwycić wszystkie naruszenia dostępu, nieprawidłowe uchwyty i przepełnienia stosu jako wyjątki dające drugą szansę:

0:000> sxd av 

0:000> sxd ch 

0:000> sxd sov 1

Teoretycznie możliwe jest sterowanie weryfikatorem aplikacji za pomocą debugera jądra. Nie jest to jednak zalecane — wymaga częstego używania poleceń .process i pagein, ale nie zapewnia więcej możliwości niż korzystanie z debugera trybu użytkownika.

Instalowanie narzędzi do debugowania

Aby pobrać najnowszą wersję narzędzi, zobacz Pobieranie narzędzi debugowania dla systemu Windows.

Konfigurowanie sprzętu na potrzeby debugowania User-Mode

Debugowanie w trybie użytkownika jest zwykle wykonywane na jednej maszynie: debuger jest uruchamiany na tym samym komputerze co aplikacja, która zakończyła się niepowodzeniem.

W takim przypadku nie jest wymagana żadna określona konfiguracja sprzętu. W tym temacie terminy komputer-host i komputer docelowy są wymienne w tym przypadku.

Konfigurowanie oprogramowania na potrzeby debugowania User-Mode

Podstawowa konfiguracja User-Mode — przed rozpoczęciem debugowania w trybie użytkownika należy pobrać niezbędne pliki symboli i ustawić określone zmienne środowiskowe.

Pliki symboli

Musisz pobrać pliki symboli dla procesu trybu użytkownika, który jest debugowany. Jeśli jest to aplikacja, którą napisałeś, powinna zostać skompilowana z pełnymi plikami symboli. Jeśli jest to aplikacja komercyjna, pliki symboli mogą być dostępne na serwerze internetowym lub do pobrania, skontaktuj się z producentem.

Jeśli przeprowadzasz zdalne debugowanie, lokalizacja pliku symboli zależy od używanej metody:

  • Jeśli przeprowadzasz zdalne debugowanie za pośrednictwem debugera, pliki symboli powinny znajdować się na komputerze z serwerem debugowania.

  • Jeśli przeprowadzasz zdalne debugowanie za pośrednictwem remote.exe, pliki symboli powinny znajdować się na komputerze z debugerem.

  • Jeśli przeprowadzasz zdalne debugowanie za pośrednictwem serwera przetwarzania lub serwera połączenia KD, pliki symboli powinny znajdować się na komputerze z klientem inteligentnym.

  • Jeśli kontrolujesz debuger trybu użytkownika z debugera jądra, pliki symboli muszą znajdować się na obu komputerach.

Konfigurowanie zmiennych środowiskowych

Debuger używa różnych zmiennych środowiskowych, aby wskazać wiele ważnych ustawień.

Aby uzyskać więcej informacji na temat debugerów, zobacz Wprowadzenie do debugowania systemu Windows

Konfigurowanie weryfikatora aplikacji przy użyciu debugera przy użyciu wiersza polecenia

Aby skonfigurować Weryfikator aplikacji, możesz użyć wiersza polecenia CDB lub NTSD.

Użyj następującego wiersza polecenia:

cdb OtherOptions -vf:Flags Target

Gdzie target jest nazwą aplikacji docelowej, a flagi określa żądane opcje weryfikatora aplikacji, które mają być stosowane do tego obiektu docelowego.

Flagi powinny być sumą bitów reprezentujących żądane opcje. Poszczególne wartości bitów są następujące:

Wartość flagi Znaczenie
00000001 KONTROLE STERTOWE
00000004 OBSŁUGA SPRAWDZEŃ
00000008 SPRAWDZANIE KARTY SIM PRZY NISKICH ZASOBACH
00000020 WERYFIKACJA PROTOKOŁU TLS
00000040 BRUDNE STOSY
00000200 NIEBEZPIECZNE INTERFEJSY API
00001000 KONTROLE WYJĄTKÓW
00002000 KONTROLE PAMIĘCI
00020000 RÓŻNE KONTROLE
00040000 SPRAWDZANIE BLOKAD

Debugowanie za pomocą programu !avrf

Rozszerzenie !avrf steruje ustawieniami weryfikatora aplikacji i wyświetla różne dane wyjściowe generowane przez weryfikatora aplikacji. Aby uzyskać dodatkowe informacje o rozszerzeniu !arvrf, zobacz !avrf w dokumentacji debugera.

Składnia

!avrf

Polecenie !avrf bez żadnych parametrów pokazuje ustawienia weryfikatora aplikacji i informacje o bieżących i poprzednich weryfikatorach aplikacji, jeśli istnieją.

!avrf –vs { Length | -aAddress }

Wyświetla dziennik operacji przestrzeni wirtualnej. Długość określa liczbę rekordów do wyświetlenia począwszy od najnowszej. Adres określa adres wirtualny. Zostaną wyświetlone rekordy operacji wirtualnych zawierających ten adres wirtualny.

!avrf -hp { Length | -a Address }

Wyświetla dziennik operacji obszaru sterty. Adres określa adres stosu. Zostaną wyświetlone rekordy operacji sterty zawierające ten adres sterty.

!avrf -cs { Length | -a Address }

Wyświetla dziennik usuwania sekcji krytycznej. Długość określa liczbę rekordów do wyświetlenia począwszy od najnowszej. Adres określa adres sekcji krytycznej. Rekordy dla określonej sekcji krytycznej są wyświetlane po określeniu adresu.

!avrf -dlls [ Length ]

Wyświetla dziennik ładowania/zwalniania bibliotek DLL. Długość określa liczbę rekordów do wyświetlenia począwszy od najnowszej.

!avrf -trm

Wyświetla dziennik wszystkich zakończonych i zawieszonych wątków.

!avrf -ex [ Length ]

Wyświetla dziennik wyjątków. Weryfikator aplikacji śledzi wszystkie występujące w aplikacji wyjątki.

!avrf -threads [ ThreadID ]

Wyświetla informacje o wątkach w procesie docelowym. W przypadku wątków podrzędnych również są wyświetlane rozmiar stosu i flagi CreateThread określone przez wątek nadrzędny. Podanie identyfikatora wątku spowoduje wyświetlenie informacji tylko dla tego konkretnego wątku.

!avrf -tp [ ThreadID ]

Wyświetla dziennik puli wątków. Ten dziennik może zawierać ślady stosu dla różnych operacji, takich jak zmiana maski koligacji wątku, zmiana priorytetu wątku, publikowanie komunikatów wątków, inicjowanie modelu COM i niezainicjowanie modelu COM z poziomu wywołania zwrotnego puli wątków. Podanie identyfikatora wątku spowoduje wyświetlenie informacji tylko dla tego konkretnego wątku.

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

Wyświetla dziennik Slim Reader/Writer (SRW). Określenie adresu spowoduje wyświetlenie rekordów odnoszących się do tego adresu blokady SRW. Po określeniu długości wraz z adresem zostaną wyświetlone wszystkie blokady SRW w tym zakresie adresów. Opcja -stats wyświetla statystyki blokady SRW.

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

Wyświetla dziennik zaległych zasobów. W danym momencie te zasoby mogą być lub nie być przeciekami. Określenie parametru ModuleName (w tym rozszerzenia) powoduje wyświetlenie wszystkich zaległych zasobów w określonym module. Wybranie parametru ResourceType powoduje wyświetlenie zaległych zasobów tego określonego typu zasobu. Specyfikacja adresu tworzy zrzut rekordów zaległych zasobów pod tym adresem. Typ zasobu może być jednym z następujących elementów:

  • Sterta: wyświetla alokacje sterty przy użyciu interfejsów API sterty Win32
  • Lokalne: Wyświetla alokacje lokalne/globalne
  • CRT: Wyświetla alokacje przy użyciu funkcji API CRT
  • Wirtualne: wyświetla rezerwacje wirtualne
  • BSTR: Wyświetla alokacje BSTR
  • Rejestr: Wyświetla otwierany klucz rejestru
  • Zasilanie: wyświetla obiekty powiadomień o zasilaniu
  • Obsługa: wyświetla alokacje wątków, plików i zdarzeń

!avrf –trace TraceIndex

Wyświetla ślad stosu dla określonego indeksu śledzenia. Niektóre struktury używają tego 16-bitowego numeru indeksu do identyfikowania śledzenia stosu. Ten indeks wskazuje lokalizację w bazie danych śledzenia stosu. Jeśli analizujesz taką strukturę, ta składnia będzie przydatna.

!avrf -cnt

Wyświetla listę liczników globalnych.

!avrf -brk [ BreakEventType ]

Określa, że jest to polecenie przerwania zdarzenia. Gdy !avrf -brk jest używane bez dodatkowych parametrów, wyświetlane są ustawienia zdarzeń przerwania. BreakEventType określa numer typu zdarzenia przerwania. Aby uzyskać listę możliwych typów, użyj polecenia !avrf -brk.

!avrf -flt [ EventTypeProbability ]

Określa, że jest to polecenie iniekcji błędów. Gdy !avrf -flt jest używany bez dodatkowych parametrów, wyświetlane są bieżące ustawienia iniekcji błędów. EventType określa numer typu zdarzenia. Prawdopodobieństwo określa częstotliwość, z jaką zdarzenie zakończy się niepowodzeniem. Może to być dowolna liczba całkowita z zakresu od 0 do 1000 000 (0xF4240).

!avrf -flt break EventType

Powoduje, że weryfikator aplikacji włamuje się do debugera przy każdym wstrzyknięciu tego błędu.

!avrf -flt stacks Length

Wyświetla długość śladów stosu dla najnowszych operacji wstrzykniętych przez błędy.

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

Określa, że jest to polecenie zakresu docelowego. Gdy -trg jest używana bez dodatkowych parametrów, wyświetlane są bieżące zakresy docelowe. Start określa początkowy adres zakresu docelowego lub zakresu wykluczeń. Koniec określa końcowy adres zakresu docelowego lub zakresu wykluczeń. Moduł określa nazwę modułu, który ma być docelowy lub wykluczony. Moduł powinien zawierać pełną nazwę modułu, w tym rozszerzenie .exe lub .dll. Informacje o ścieżce nie powinny być uwzględniane. Określanie wszystkich przyczyn resetowania wszystkich zakresów docelowych lub zakresów wykluczeń.

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

Określa, że jest to polecenie zakresu wykluczenia. Start określa początkowy adres zakresu docelowego lub zakresu wykluczeń. Koniec określa końcowy adres zakresu docelowego lub zakresu wykluczeń. Moduł określa nazwę modułu, który ma być docelowy lub wykluczony. Moduł powinien zawierać pełną nazwę modułu, w tym rozszerzenie .exe lub .dll. Informacje o ścieżce nie powinny być uwzględniane. Określanie wszystkich przyczyn resetowania wszystkich zakresów docelowych lub zakresów wykluczeń. Określanie czasu powoduje, że wszystkie błędy są tłumione przez określony czas w milisekundach po wznowieniu wykonywania.

Poniżej przedstawiono dane wyjściowe dostarczone przez polecenie !avrf w debugerze.

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 komentarze rozszerzenia

Gdy rozszerzenie !avrf jest używane bez parametrów, wyświetla bieżące opcje weryfikatora aplikacji.

Rozszerzenie !avrf używa Exts.dll w debugerze.

Jeśli wystąpił zatrzymanie weryfikatora aplikacji, rozszerzenie !avrf bez parametrów ujawni charakter zatrzymania i przyczynę.

Jeśli brakuje symboli dla ntdll.dll i verifier.dll, rozszerzenie !avrf wygeneruje komunikat o błędzie.

Ciągłe i niewstrzymalne przystanki

Debugowanie ciągłego zatrzymywania

Oto przykład wyjątku nieprawidłowego uchwytu, który został zgłoszony przez opcję Wykryj nieprawidłowe użycie uchwytu.

Najpierw zostanie wyświetlony następujący komunikat:

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

Zwróć uwagę, że komunikat informuje cię, że zatrzymanie weryfikatora aplikacji można kontynuować. Po zapoznaniu się z tym, co się stało, możesz kontynuować uruchamianie aplikacji docelowej.

Najpierw należy użyć rozszerzenia !avrf. Zapewnia to informacje o bieżącej awarii:

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

Ostatni wiersz tego wyświetlania zawiera podsumowanie problemu.

W tym momencie warto przyjrzeć się niektórym dziennikom. Po zakończeniu użyj polecenia g (Go), aby ponownie uruchomić aplikację:

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

W takim przypadku komunikat informuje o tym, że nie można kontynuować tego zatrzymania weryfikatora aplikacji. Błąd jest zbyt poważny, aby proces nadal działał i nie ma możliwości, aby weryfikator aplikacji uratował proces.

Rozszerzenie !avrf może służyć do podania informacji o bieżącej awarii:

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.

Ostatni wiersz tego wyświetlania zawiera podsumowanie problemu.

W tym momencie możesz również przeglądać niektóre dzienniki. W tym momencie możesz użyć polecenia .restart (Uruchom ponownie aplikację docelową). A może wolisz zakończyć sesję weryfikatora aplikacji i rozpocząć naprawianie usterek w kodzie.

Debugowanie błędów sekcji krytycznej

!rozszerzenie debugera cs

!cs może służyć zarówno w debugerze trybu użytkownika, jak i debugerze jądra do wyświetlania informacji o krytycznych sekcjach w bieżącym procesie. Aby uzyskać dodatkowe informacje o rozszerzeniu !cs, zobacz !cs w dokumentacji debugera.

Dopasowywanie symboli z informacjami o typie jest wymagane, zwłaszcza w przypadku ntdll.dll.

Składnia tego rozszerzenia to:

!cs [-s] — zrzut wszystkich aktywnych sekcji krytycznych w bieżącym procesie.

!cs [-s] adres — zrzut sekcji krytycznej pod tym adresem.

!cs [-s] -d adres — zrzut sekcji krytycznej odpowiadającej DebugInfo na tym adresie.

-s spowoduje zrzut stosu śledzenia inicjowania sekcji krytycznej, jeśli jest dostępny.

Przykłady:

Zrzut informacji o sekcji krytycznej przy użyciu jego adresu

0:001> ! cs 0x7803B0F8

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

Zrzut informacji o sekcji krytycznej przy użyciu jej adresu, w tym śledzenie stosu inicjalizacji

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

Zrzut informacji o sekcji krytycznej przy użyciu adresu informacji o debugowaniu

0:001> !cs -d 0x6A262080

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

Zrzut informacji o sekcji krytycznej przy użyciu adresu informacji o debugowaniu, w tym śledzenia stosu inicjowania

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

Zrzut informacji o wszystkich aktywnych sekcjach krytycznych w bieżącym procesie

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

Zrzut informacji o wszystkich aktywnych sekcjach krytycznych w bieżącym procesie, w tym ślad stosu inicjalizacji


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

Debugowanie błędów wyjątków

Dziennik wyjątków rejestruje wszystkie wyjątki, które wystąpiły w procesie docelowym.

Aby wyświetlić kilka ostatnich wyjątków, możesz użyć polecenia rozszerzenia !avrf -ex Length; Długość określa liczbę wyjątków. Jeśli parametr Length zostanie pominięty, zostaną wyświetlone wszystkie wyjątki.

Oto przykład:

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.

Debugowanie obsługuje błędy

!htrace może być używany zarówno w debugerze trybu użytkownika, jak i w debugerze jądra do wyświetlania informacji o śledzeniu stosu dla jednego lub wszystkich uchwytów w procesie. Te informacje są dostępne, jeśli śledzenie uchwytów jest włączone dla procesu — automatycznie włączone w przypadku włączenia sprawdzania uchwytów w weryfikatorze aplikacji. Ślady stosu są zapisywane za każdym razem, gdy proces otwiera lub zamyka uchwyt lub gdy odwołuje się do nieprawidłowego uchwytu. Aby uzyskać dodatkowe informacje o rozszerzeniu !htrace, zobacz !htrace w dokumentacji debugera.

Składnia debugera jądra dla tego rozszerzenia to:

!htrace [ handle [process] ]

Jeśli uchwyt nie zostanie określony lub ma wartość 0, zostaną wyświetlone informacje o wszystkich uchwytach w procesie. Jeśli proces nie zostanie określony, zostanie użyty bieżący proces.

Składnia debugera trybu użytkownika to:

!htrace [handle]

Rozszerzenie debugera trybu użytkownika zawsze wyświetla informacje o bieżącym procesie debugowania.

Przykłady:

Zrzut informacji o uchwycie 7CC w procesie 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.

Zrzut informacji o wszystkich uchwytach w procesie 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.

Zrzut informacji o uchwycie 7DC w bieżącym procesie


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.

Debugowanie błędów stert

Rozszerzenie debugera weryfikatora stertowego

Rozszerzenie debugera weryfikatora sterty jest częścią rozszerzenia sterty NT (rozszerzenie debugera sterty NT). Podstawowe informacje uzyskasz dzięki !heap -? lub bardziej obszerny z !heap -p -? . Bieżące rozszerzenie nie samodzielnie wykrywa, czy sterta stron jest włączona dla procesu, ani nie podejmuje odpowiednich działań. Na razie użytkownik rozszerzenia musi wiedzieć, że sterta stron jest włączona i używać poleceń poprzedzonych prefiksem !heap -p. Aby uzyskać dodatkowe informacje o rozszerzeniu !htrace, zobacz !heap w dokumentacji debuggera.

!heap -p

Zrzuty adresów wszystkich pełnych stosów stron utworzonych w procesie.

!heap -p -h ADDRESS-OF-HEAP

Pełny zrzut sterty w pełnym obszarze strony na ADRES-OF-HEAP.

!heap -p -a ADDRESS

Próbuje dowiedzieć się, czy istnieje blok stert pod adresem ADDRESS. Ta wartość nie musi wskazywać na adres początku bloku. Polecenie jest przydatne, jeśli nie ma pojęcia o charakterze obszaru pamięci.

Dziennik operacji kopca

Dziennik operacji sterta śledzi wszystkie procedury stert. Należą do nich HeapAlloc, HeapReAlloc i HeapFree.

Możesz użyć !avrf -hp Length polecenia rozszerzenia, aby wyświetlić kilka ostatnich rekordów; Długość określa liczbę rekordów.

Można użyć !avrf -hp -a Address , aby wyświetlić wszystkie operacje obszaru sterty, które miały wpływ na określony adres. W przypadku operacji alokacji wystarczy, że adres znajduje się w przydzielonym bloku sterty. Aby uzyskać bezpłatną operację, należy podać dokładny adres początku bloku.

Dla każdego wpisu w dzienniku są wyświetlane następujące informacje:

  • Wywołana funkcja sterta.
  • Identyfikator wątku, który wywołał rutynę.
  • Adres zaangażowany w wywołanie — jest to adres zwrócony przez procedurę alokacji lub przekazany do bezpłatnej procedury.
  • Rozmiar obszaru zaangażowanego w procesie wywoływania.
  • Ślad stosu wywołania.

Najnowsze wpisy są wyświetlane jako pierwsze.

W tym przykładzie zostaną wyświetlone dwa najnowsze wpisy:

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

Typowe scenariusze debugowania

Istnieje kilka scenariuszy awarii, które mogą wystąpić. Niektóre z nich wymagają sporo pracy detektywów, aby uzyskać cały obraz.

Naruszenie dostępu na stronie niedostępnej

Dzieje się tak, gdy sterta pełnej strony jest włączona, jeśli przetestowana aplikacja uzyskuje dostęp poza koniec buforu. Może się to również zdarzyć, jeśli dotyka uwolnionego bloku. Aby zrozumieć, jaki jest charakter adresu, w którym wystąpił wyjątek, należy użyć:

!heap –p –a ADDRESS-OF-AV

Uszkodzony komunikat bloku

W kilku momentach w okresie istnienia alokacji (alokacja, zwolnienie przez użytkownika, zwolnienie rzeczywiste) menedżer sterty stron sprawdza, czy wszystkie wzorce wypełnienia są zachowane, a nagłówek bloku ma spójne dane. Jeśli tak nie jest, nastąpi zatrzymanie weryfikatora.

Jeśli blok jest blokiem pełnostronicowej sterty (na przykład jeśli wiesz, że pełnostronicowa sterta jest włączona dla wszystkich alokacji), możesz użyć "!sterty –p –a ADDRESS", aby dowiedzieć się, jakie są cechy bloku.

Jeśli blok jest blokiem sterty strony lekkiej, musisz znaleźć adres początkowy nagłówka bloku. Adres początkowy można znaleźć, zrzucając 30-40 bajtów poniżej wskazanego adresu i szukając wzorców magicznych początkowych/końcowych nagłówka bloku (ABCDAAAA, ABCDBBBB, ABCDAAA9, ABCDBBBA).

Nagłówek będzie zawierać wszystkie informacje potrzebne do zrozumienia błędu. Szczególnie wzorce magiczne wskazują, czy blok jest przydzielony czy wolny oraz czy pochodzi z lekkiej sterty stron, czy pełnej sterty stron. Informacje w tym miejscu muszą być dokładnie dopasowane do obraźliwego połączenia.

Jeśli na przykład wywołanie funkcji HeapFree zostanie wykonane z adresem bloku powiększonym o cztery bajty, otrzymasz komunikat o uszkodzeniu. Nagłówek bloku będzie wyglądał dobrze, ale należy zwrócić uwagę, że pierwszy bajt po końcu nagłówka (pierwszy bajt po wartości magicznej 0xDCBAXXXX) ma inny adres niż ten w wywołaniu.

Specjalne wskaźniki wypełnienia

Menedżer strony sterty wypełnia alokację użytkownika wartościami, które będą przypominać wskaźniki jądra. Dzieje się tak, gdy blok zostanie zwolniony (wartość wypełnienia to F0) oraz gdy blok zostanie przydzielony, ale nie zostanie zgłoszone żądanie jego wyzerowania (wartość wypełnienia to E0 dla lekkiego stosu stron i C0 dla pełnego stosu stron). Alokacje niezerowe są typowe dla użytkowników malloc/new. Jeśli wystąpi błąd (naruszenie dostępu), w którym próba odczytu/zapisu jest podejmowana na adresach, takich jak F0F0F0F0, E0E0E0E0, C0C0C0C0 najprawdopodobniej trafisz w jeden z tych przypadków.

Odczyt/zapis w F0F0F0F0 oznacza, że blok został użyty po jego uwolnieniu. Niestety konieczne będzie wykonanie pewnych działań detektywisyjnych, aby dowiedzieć się, który blok spowodował ten problem. Należy uzyskać ślad stosu awarii, a następnie sprawdzić kod funkcji w stosie. Jeden z nich może błędnie założyć, że alokacja jest aktywna.

Odczyt/zapis w E0E0E0E0/C0C0C0C0 oznacza, że aplikacja nie zainicjowała prawidłowo procesu alokacji. Wymaga to również inspekcji kodu funkcji w bieżącym śladzie stosu. Oto przykład tego rodzaju awarii. W procesie testowym zaobserwowano naruszenie zasad dostępu podczas wykonywania aplikacji HeapFree na adresie E0E0E0E0. Okazało się, że test przydzielił strukturę, nie zainicjował jej poprawnie, a następnie wywołał destruktor obiektu. Ponieważ określone pole nie było null (zawierało E0E0E0E0), wywołało funkcję usuwania.

Szczegóły techniczne sterty stron

Aby wykryć uszkodzenia stert (przepełnienia lub podpływy), program AppVerifier zmodyfikuje sposób przydzielania pamięci przez wypełnienie żądanej pamięci pełnymi stronami niepisanymi lub specjalnymi tagami przed i po przydzielonej pamięci. AppVerifier wykonuje to przez załadowanie Verifier.dll do procesu weryfikowanego i przekierowanie niektórych wywołań interfejsów API sterty Win32 wywoływanych przez aplikację do odpowiednich interfejsów API Verifier.dll.

Podczas uzupełniania żądanej pamięci pełnymi niewypisywalnymi stronami (ustawienie PEŁNE jest włączone w sekcji właściwości sterty strony i jest ustawieniem domyślnym), program AppVerifier będzie zużywać dużą ilość pamięci wirtualnej, ale ma tę zaletę, że zdarzenia uszkodzenia sterty są buforowane w czasie rzeczywistym, gdy występuje przepełnienie lub niedopełnienie. Pamiętaj, że pamięć w tym trybie będzie wyglądać następująco: [AppVerifier Read-Only strona sterty (4k)] [Ilość pamięci żądanej przez aplikację w ramach testu] lub tak: [Ilość pamięci żądanej przez aplikację w ramach testu] [AppVerifier Read-Only strona sterty (4k)].

Kontrola sterty spowoduje umieszczenie strony ochronnej na początku lub na końcu alokacji, w zależności od właściwości Wstecz. Jeśli parametr Backward jest ustawiony na False, co jest wartością domyślną, na końcu alokacji zostanie umieszczona strona ochronna, aby przechwycić przepełnienia bufora. Jeśli jest ustawiona wartość True, strona ochrony zostanie umieszczona na początku alokacji w celu przechwycenia podbiegów buforu.

Podczas uzupełniania żądanej pamięci za pomocą tagów specjalnych (włączone przez wyczyszczenie elementu pola wyboru "Pełna" we właściwościach sterty) program AppVerifier sprawdzi i powiadomi Cię o zwolnieniu tej pamięci. Głównym problemem podczas korzystania z tej techniki jest to, że w niektórych przypadkach uszkodzenie pamięci zostanie wykryte tylko wtedy, gdy pamięć zostanie zwolniona (minimalna ilość bloku pamięci wynosi 8 bajtów), więc w przypadku 3-bajtowej zmiennej lub przepełnienia 5 bajtów nie zostanie natychmiast wykryta.

W przypadku zdarzenia przepełnienia dolnego zostanie podjęta próba zapisania na stronę Read-Only. Spowoduje to wyzwolenie wyjątku. Należy pamiętać, że ten wyjątek można przechwycić tylko wtedy, gdy aplikacja docelowa jest wykonywana w debugerze. Należy pamiętać, że tryb pełnej sterty stron wykryje również te błędy, ponieważ używa on stron wyrównania i ochrony. Powodem, dla którego używa się lekkiej sterty strony, jest to, że komputer nie może tolerować wysokich wymagań pamięciowych pełnej sterty strony.

W przypadku aplikacji intensywnie korzystających z pamięci lub gdy wymagane jest korzystanie z narzędzia AppVerifier przez dłuższe okresy (na przykład podczas testów obciążeniowych), lepiej jest uruchamiać normalne (lekkie) testy stosu, a nie w trybie pełnym, ze względu na spadek wydajności. Jeśli jednak napotkasz problem, włącz pełną stertę strony, aby zbadać go dokładniej.

Aplikacje korzystające z niestandardowych stert (sterta, która omija implementację systemową sterty) mogą nie uzyskać pełnej korzyści z używania sterty strony lub nawet działać nieprawidłowo, gdy sterta strony jest włączona.

Debugowanie błędów pamięci

Rozszerzenie debugera weryfikatora pamięci

Dziennik operacji przestrzeni wirtualnej śledzi wszystkie procedury modyfikujące przestrzeń wirtualną procesu w dowolny sposób. Należą do nich: VirtualAlloc, VirtualFree, MapViewOfFile i UnmapViewOfFile.

Możesz użyć !avrf -vs Length polecenia rozszerzenia, aby wyświetlić kilka ostatnich rekordów; Długość określa liczbę rekordów.

Możesz użyć !avrf -vs -a Address, aby wyświetlić wszystkie operacje przestrzeni wirtualnej, które miały wpływ na określony adres. W przypadku alokacji wystarczy, że adres znajduje się w przydzielonym bloku. Aby uzyskać bezpłatny, należy podać dokładny adres początku regionu.

Dla każdego wpisu w dzienniku są wyświetlane następujące informacje:

  • Funkcja o nazwie
  • Identyfikator wątku, który wywołał procedurę
  • Adres użyty w wywołaniu — jest to adres zwrócony przez rutynę alokacji lub przekazany do rutynę zwalniania pamięci
  • Rozmiar regionu zaangażowanego w wywołanie
  • Typ operacji pamięci (parametr AllocationType)
  • Żądany typ ochrony
  • Ślad stosu wywołania

Przykłady

Najnowsze wpisy są wyświetlane jako pierwsze.

W poniższym przykładzie zostaną wyświetlone dwa najnowsze wpisy:

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

Można zauważyć z wyników, że wątek 0xB4 najpierw zdekomitował stronę, a następnie zwolnił cały region wirtualny.

Poniżej przedstawiono wyświetlanie wszystkich operacji wpływających na adres 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

Aby odczytać ten wynik, pamiętaj, że wpisy są wypisane, zaczynając od najnowszego. W związku z tym ten dziennik pokazuje, że wątek 0xB4 przydzielił duży region, w którym zatwierdził stronę. Później odblokowała stronę, a następnie uwolniła cały region wirtualny.

Zobacz też

Application Verifier — omówienie

Application Verifier — testowanie aplikacji

Application Verifier — testy w Weryfikatora aplikacji

Application Verifier — kody zatrzymania i definicje

Application Verifier — często zadawane pytania