Partager via


Vérificateur d’application - Débogage des arrêts du vérificateur d’application

Installation et configuration du débogueur

Certaines actions du vérificateur d’application peuvent entraîner la levée d’une exception. Le débogueur doit être défini pour intercepter ces exceptions à la deuxième chance, car Application Verifier lui-même gère les premières exceptions de chance.

Les exceptions levées sont de trois types :

  • Une exception de violation d’accès (0xC0000005) est générée si l’option tas détecte un dépassement de mémoire tampon de tas. Dans certains cas, l’option Vérifier l’utilisation du chemin système peut également entraîner une violation de l'accès.

  • Une exception de handle non valide (0xC0000008) est générée lorsque l’option Détecter l’utilisation de handle non valide détecte une opération de handle non valide.

  • Une exception de dépassement de pile (0xC00000FD) est générée lorsque l’option Vérifier la pile appropriée détecte que la pile initiale était trop courte.

Une façon de préparer ces événements consiste à démarrer le débogueur sur une ligne de commande comme suit :

windbg -xd av -xd ch -xd sov ApplicationCommandLine

ou

cdb -xd av -xd ch -xd sov ApplicationCommandLine

Si vous avez déjà démarré le débogueur, vous pouvez utiliser la commande sxd (Définir des exceptions) pour intercepter toutes les violations d’accès, les handles non valides et les dépassements de pile en tant qu’exceptions de seconde chance :

0:000> sxd av 

0:000> sxd ch 

0:000> sxd sov 1

Il est théoriquement possible de contrôler Application Verifier via un débogueur de noyau. Toutefois, cela n’est pas recommandé : il nécessite une utilisation fréquente des commandes .process et .pagein, mais il ne vous donne pas plus de puissance que d’utiliser un débogueur en mode utilisateur.

Installation des outils de débogage

Pour télécharger la dernière version des outils, consultez Télécharger les outils de débogage pour Windows.

Configuration du matériel pour le débogage User-Mode

Le débogage en mode utilisateur est généralement effectué sur une seule machine : le débogueur est exécuté sur le même ordinateur que l’application ayant échoué.

Dans ce cas, aucune configuration matérielle spécifique n’est requise. Tout au long de cette rubrique, les termes ordinateur hôte et ordinateur cible sont interchangeables dans ce cas.

Configuration du logiciel pour le débogage User-Mode

Configuration de base User-Mode : avant de commencer le débogage en mode utilisateur, vous devez télécharger les fichiers de symboles nécessaires et définir certaines variables d’environnement.

Fichiers de symboles

Vous devez télécharger les fichiers de symboles pour le processus en mode utilisateur en cours de débogage. S’il s’agit d’une application que vous avez écrite, elle doit être générée avec des fichiers de symboles complets. S’il s’agit d’une application commerciale, les fichiers de symboles peuvent être disponibles sur un serveur web ou en téléchargement, contactez le fabricant.

si vous effectuez un débogage à distance, l’emplacement du fichier de symboles dépend de la méthode que vous utilisez :

  • Si vous effectuez un débogage distant via le débogueur, les fichiers de symboles doivent se trouver sur l’ordinateur avec le serveur de débogage.

  • Si vous effectuez un débogage distant via remote.exe, les fichiers de symboles doivent se trouver sur l’ordinateur avec le débogueur.

  • Si vous effectuez un débogage distant via un serveur de processus ou un serveur de connexion KD, les fichiers de symboles doivent se trouver sur l’ordinateur avec le client intelligent.

  • Si vous contrôlez le débogueur en mode utilisateur à partir du débogueur du noyau, les fichiers de symboles doivent se trouver sur les deux ordinateurs.

Configuration des variables d’environnement

Le débogueur utilise une variété de variables d’environnement pour indiquer un certain nombre de paramètres importants.

Pour plus d’informations sur les débogueurs, consultez Prise en main du débogage Windows

Configuration du vérificateur d’application avec le débogueur à l’aide de la ligne de commande

Pour configurer Application Verifier, vous pouvez utiliser la ligne de commande CDB ou NTSD.

Utilisez la ligne de commande suivante :

cdb OtherOptions -vf:Flags Target

Où Target est le nom de l’application cible, et Indicateurs spécifie les options souhaitées du Vérificateur d'applications qui doivent être appliquées à cette cible.

Les indicateurs doivent être une somme des bits représentant les options souhaitées. Les valeurs de bits individuelles sont les suivantes :

Valeur de repère Sens
00000001 VÉRIFICATIONS DE TAS
00000004 GÉRER LES VÉRIFICATIONS
00000008 VÉRIFICATIONS SIM À FAIBLE RESSOURCE
00000020 VÉRIFICATIONS TLS
00000040 PILES SALES
00000200 API DANGEREUSES
00001000 VÉRIFICATIONS DES EXCEPTIONS
00002000 VÉRIFICATIONS DE LA MÉMOIRE
00020000 VÉRIFICATIONS DIVERSES
00040000 VÉRIFICATIONS DE VERROUILLAGE

Débogage avec !avrf

L’extension !avrf contrôle les paramètres du vérificateur d’application et affiche une variété de sorties produites par Application Verifier. Pour plus d’informations sur l’extension !arvrf, consultez !avrf dans la documentation du débogueur.

Syntaxe

!avrf

La commande !avrf sans paramètres affiche les paramètres du vérificateur d’application et des informations sur les interruptions actuelles et précédentes du vérificateur d’application.

!avrf –vs { Length | -aAddress }

Affiche le journal des opérations d’espace virtuel. La longueur spécifie le nombre d’enregistrements à afficher à partir du plus récent. L’adresse spécifie l’adresse virtuelle. Les enregistrements des opérations virtuelles contenant cette adresse virtuelle sont affichés.

!avrf -hp { Length | -a Address }

Affiche le journal des opérations de tas. L’adresse spécifie l’adresse du tas. Les enregistrements des opérations de tas contenant cette adresse de tas s’affichent.

!avrf -cs { Length | -a Address }

Affiche le journal de suppression des sections critiques. La longueur spécifie le nombre d’enregistrements à afficher à partir du plus récent. L'adresse précise l'emplacement de la section critique. Les enregistrements de la section critique spécifique s’affichent lorsque l’adresse est spécifiée.

!avrf -dlls [ Length ]

Affiche le journal de chargement/déchargement de la DLL. La longueur spécifie le nombre d’enregistrements à afficher à partir du plus récent.

!avrf -trm

Affiche un journal de tous les threads arrêtés et suspendus.

!avrf -ex [ Length ]

Affiche le journal des exceptions. Application Verifier suit toutes les exceptions qui se produisent dans l’application.

!avrf -threads [ ThreadID ]

Affiche des informations sur les threads dans le processus cible. Pour les threads enfants, la taille de la pile et les indicateurs CreateThread spécifiés par le parent sont également affichés. La fourniture d’un ID de thread affiche uniquement des informations pour ce thread particulier.

!avrf -tp [ ThreadID ]

Affiche le journal du pool de threads. Ce journal peut contenir des traces de pile pour différentes opérations telles que la modification du masque d’affinité du fil d'exécution, la modification de la priorité du fil d'exécution, la publication de messages de fil d'exécution, l’initialisation de COM et la désinitialisation de COM à partir du rappel du pool de fils d'exécution. La fourniture d’un ID de thread affiche uniquement des informations pour ce thread particulier.

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

Affiche le journal du lecteur/enregistreur Slim (SRW). La spécification de l’adresse affichera les enregistrements correspondant à cette adresse de verrouillage SRW. Lorsque la longueur est spécifiée avec l’adresse, tous les verrous SRW de cette plage d’adresses sont affichés. L’option -stats vide les statistiques de verrou SRW.

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

Affiche le journal des ressources en attente. Ces ressources peuvent ou non être considérées comme des fuites à un moment quelconque. La spécification de ModuleName (y compris l’extension) affiche toutes les ressources en attente dans le module spécifié. La spécification de ResourceType affiche les ressources exceptionnelles de ce type de ressource particulier. Indiquer l'adresse crée des vidages des enregistrements des ressources en cours à cette adresse. ResourceType peut être l’un des éléments suivants :

  • Tas : affiche les allocations de tas à l’aide des API de tas Win32
  • Local : affiche les allocations locales/globales
  • CRT : affiche les allocations à l’aide des API CRT
  • Virtual : affiche les réservations virtuelles
  • BSTR : affiche les allocations BSTR
  • Registre : affiche l’ouverture de la clé de Registre
  • Power : affiche les objets de notification d’alimentation
  • Handle : affiche les allocations de thread, de fichier et de handle d’événement

!avrf –trace TraceIndex

Affiche une trace de pile pour l’index de trace spécifié. Certaines structures utilisent ce numéro d’index 16 bits pour identifier une trace de pile. Cet index pointe vers un emplacement dans la base de données de trace de pile. Si vous analysez une telle structure, vous trouverez cette syntaxe utile.

!avrf -cnt

Affiche une liste de compteurs globaux.

!avrf -brk [ BreakEventType ]

Spécifie qu’il s’agit d’une commande d'événement d'interruption. Lorsqu’on utilise !avrf -brk sans paramètres supplémentaires, les paramètres de l'événement de pause sont affichés. BreakEventType spécifie le numéro de type de l’événement d’arrêt. Pour obtenir la liste des types possibles, utilisez !avrf -brk.

!avrf -flt [ EventTypeProbability ]

Spécifie qu’il s’agit d’une commande d’injection d’erreur. Lorsqu’il !avrf -flt est utilisé sans paramètres supplémentaires, les paramètres d’injection d’erreur actuels sont affichés. EventType spécifie le numéro de type de l’événement. La probabilité spécifie la fréquence à laquelle l’événement échouera. Il peut s’agir d’un entier compris entre 0 et 1 000 000 (0xF4240).

!avrf -flt break EventType

Provoque Application Verifier à déclencher le débogueur chaque fois que cette erreur est injectée.

!avrf -flt stacks Length

Affiche le nombre de traces de longueur pour les opérations injectées par erreur les plus récentes.

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

Spécifie qu’il s’agit d’une commande de plage cible. Lorsque -trg est utilisé sans paramètres supplémentaires, les plages cibles actuelles sont affichées. Start spécifie l’adresse de début de la plage cible ou de la plage d’exclusion. Fin spécifie l’adresse de fin de la plage cible ou de la plage d’exclusion. Le module spécifie le nom d’un module à cibler ou exclure. Le module doit inclure le nom complet du module, y compris l’extension .exe ou .dll. Les informations de chemin d’accès ne doivent pas être incluses. Spécifier toutes réinitialise toutes les plages cibles ou d’exclusion.

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

Spécifie qu’il s’agit d’une commande de plage d’exclusion. Start spécifie l’adresse de début de la plage cible ou de la plage d’exclusion. Fin spécifie l’adresse de fin de la plage cible ou de la plage d’exclusion. Le module spécifie le nom d’un module à cibler ou exclure. Le module doit inclure le nom complet du module, y compris l’extension .exe ou .dll. Les informations de chemin d’accès ne doivent pas être incluses. La spécification de tous les paramètres entraîne la réinitialisation de toutes les plages cibles ou de toutes les plages d’exclusion. La spécification du temps entraîne la suppression de toutes les fautes pendant les millisecondes spécifiées après la reprise de l'exécution.

Voici la sortie fournie par la commande !avrf dans le débogueur.

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.

Commentaires sur l’extension !avrf

Lorsque l’extension !avrf est utilisée sans paramètres, elle affiche les options actuelles du vérificateur d’application.

L'extension !avrf utilise le Exts.dll dans le débogueur.

Si un arrêt du vérificateur d’application s’est produit, l’extension !avrf sans paramètre révélera la nature de l’arrêt et ce qui l’a provoqué.

Si des symboles pour ntdll.dll et verifier.dll sont manquants, l’extension !avrf génère un message d’erreur.

Arrêts continuables et non continuables

Débogage d’un arrêt continuable

Voici un exemple d’exception de descripteur non valide levée par l’option détecter l’utilisation du descripteur non valide.

Tout d’abord, le message suivant s’affiche :

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

Notez que le message vous indique que cet arrêt du vérificateur d’application peut être poursuivi. Une fois que vous avez compris ce qui s’est passé, vous pouvez continuer à exécuter l’application cible.

Tout d’abord, vous devez utiliser l’extension !avrf. Cela fournit des informations sur l’échec actuel :

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 dernière ligne de cet affichage récapitule le problème.

Vous pouvez examiner certains fichiers journaux à ce stade. Une fois que vous avez terminé, utilisez la commande g (Go) pour redémarrer l’application :

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

Dans ce cas, le message vous indique que cet arrêt du vérificateur d'applications ne peut pas être repris. L’erreur est trop grave pour que le processus continue à s’exécuter, et il n’existe aucun moyen pour le vérificateur d’application de récupérer le processus.

L’extension !avrf peut être utilisée pour fournir des informations sur l’échec actuel :

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 dernière ligne de cet affichage récapitule le problème.

Vous pouvez également examiner certains fichiers journaux à ce stade. Vous pouvez utiliser la commande .restart (Redémarrer l’application cible) à ce stade. Ou peut-être préférez-vous mettre fin à votre session Application Verifier et commencer à corriger les bogues dans votre code.

Débogage des erreurs de section critique

Extension du débogueur !cs

!cs peut être utilisé dans le débogueur en mode utilisateur et le débogueur de noyau pour afficher des informations sur les sections critiques du processus actuel. Pour plus d’informations sur l’extension !cs, consultez !cs dans la documentation du débogueur.

Associer les symboles aux informations de type est nécessaire, en particulier pour ntdll.dll.

La syntaxe de cette extension est la suivante :

!cs [-s] : videz toutes les sections critiques actives dans le processus actuel.

!cs [-s] adresse - vider la section critique à cette adresse.

!cs [-s] -d à l'adresse : vidage de section critique correspondant à DebugInfo à cette adresse.

-s produit la trace de la pile d'initialisation de la section critique si elle est disponible.

Exemples:

Information sur l'exportation d'une section critique en utilisant son adresse

0:001> ! cs 0x7803B0F8

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

Vidage d'informations sur une section critique pour son adresse, y compris la trace de pile lors de l'initialisation.

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

Extraire des informations sur une section critique en utilisant son adresse d'informations de débogage

0:001> !cs -d 0x6A262080

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

Informations de vidage sur une section critique à l’aide de son adresse d’informations de débogage, y compris la trace de la pile d’initialisation

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

Générer des informations sur toutes les sections critiques actives dans le processus actuel

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

Fournir les informations sur toutes les sections critiques actives dans le processus actuel, y compris la trace de pile d'initialisation.


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

Erreurs d'exception lors du débogage

Le journal des exceptions enregistre toutes les exceptions qui se sont produites dans le processus cible.

Vous pouvez utiliser la commande !avrf -ex Length extension pour afficher les dernières exceptions ; La longueur spécifie le nombre d’exceptions. Si la longueur est omise, toutes les exceptions sont affichées.

Voici un exemple :

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.

Le débogage gère les erreurs

!htrace peut être utilisé à la fois dans le débogueur en mode utilisateur et dans le débogueur de noyau pour afficher les informations de trace de pile pour un handle ou pour tous les handles d’un processus. Ces informations sont disponibles si le suivi des descripteurs est activé pour le processus, automatiquement activé si la vérification des descripteurs est activée dans le vérificateur d'applications. Les traces de pile sont enregistrées chaque fois que le processus ouvre ou ferme un handle ou lorsqu’il référence un handle non valide. Pour plus d’informations sur l’extension !htrace, consultez !htrace dans la documentation du débogueur.

La syntaxe du débogueur du noyau pour cette extension est la suivante :

!htrace [ handle [process] ]

Si le handle n’est pas spécifié ou est égal à 0, des informations sur tous les handles du processus sont affichées. Si le processus n’est pas spécifié, le processus actuel sera utilisé.

La syntaxe du débogueur en mode utilisateur est la suivante :

!htrace [handle]

L’extension de débogueur en mode utilisateur affiche toujours des informations sur le processus de débogage actuel.

Exemples:

Informations de vidage sur le handle 7CC dans le processus 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.

Exporter des informations sur tous les handles dans le processus 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.

Informations de vidage sur le handle 7DC dans le processus actuel


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.

Débogage d’erreurs de tas

Extension du débogueur du vérificateur de tas

L’extension du débogueur du vérificateur de tas fait partie de l’extension !heap (extension du débogueur de tas NT). Une aide simple peut être obtenue avec !heap - ? ou plus complexe avec !heap -p -? . L’extension actuelle ne détecte pas elle-même si le tas de pages est activé pour un processus et agit en conséquence. Pour l’instant, l’utilisateur de l’extension doit savoir que le tas de pages est activé et qu’il utilise des commandes préfixées par !heap -p. Pour plus d’informations sur l’extension !htrace, consultez !heap dans la documentation du débogueur.

!heap -p

Affiche les adresses de tous les tas de pages entières créées dans le processus.

!heap -p -h ADDRESS-OF-HEAP

Vidage complet du tas de pages complètes sur ADDRESS-OF-HEAP.

!heap -p -a ADDRESS

Tente de déterminer s’il existe un bloc de tas à ADDRESS. Cette valeur n'est pas nécessairement l’adresse du début du bloc. La commande est utile s’il n’y a aucun indice sur la nature d’une zone mémoire.

Journal des opérations du tas

Le journal des opérations de tas de mémoire suit toutes les routines de tas de mémoire. Il s’agit notamment de HeapAlloc, HeapReAlloc et HeapFree.

Vous pouvez utiliser la commande d’extension !avrf -hp Length pour afficher les derniers enregistrements ; La longueur spécifie le nombre d’enregistrements.

Vous pouvez utiliser !avrf -hp -a Address pour afficher toutes les opérations d’espace de tas qui ont affecté l’adresse spécifiée. Pour une opération d’allocation, il suffit que l’Adresse soit contenue dans le bloc de tas alloué. Pour une opération gratuite, l’adresse exacte du début du bloc doit être donnée.

Pour chaque entrée dans le journal, les informations suivantes s’affichent :

  • Fonction de gestion de pile appelée.
  • ID de thread du thread qui a appelé la routine.
  • Adresse impliquée dans l’appel : il s’agit de l’adresse retournée par une routine d’allocation ou transmise à une routine gratuite.
  • Taille de la région impliquée dans l’appel.
  • Trace de la pile d'appel.

Les entrées les plus récentes sont affichées en premier.

Dans cet exemple, les deux entrées les plus récentes sont affichées :

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

Scénarios de débogage classiques

Plusieurs scénarios d’échec peuvent être rencontrés. Certains d'entre eux nécessitent pas mal de travail de détective pour avoir une vue d'ensemble.

Violation d’accès dans la page non accessible

Cela se produit lorsque le tas de pages complètes est activé si l’application testée accède au-delà de la fin de la mémoire tampon. Il peut également se produire s’il touche un bloc libéré. Pour comprendre la nature de l’adresse sur laquelle l’exception s’est produite, vous devez utiliser :

!heap –p –a ADDRESS-OF-AV

Message de bloc endommagé

À plusieurs moments pendant la durée de vie d’une allocation (allocation, libération par l’utilisateur, libération effective), le gestionnaire de tas de pages vérifie si le bloc a tous les motifs de remplissage intacts et si l’en-tête du bloc contient des données cohérentes. Si ce n’est pas le cas, vous obtiendrez un arrêt de vérificateur.

Si le bloc est un bloc de tas de pages complet (par exemple, si vous savez que le tas de pages complet est activé pour toutes les allocations), vous pouvez utiliser « !heap –p –a ADDRESS » pour découvrir quelles sont les caractéristiques du bloc.

Si le bloc est un bloc de tas de pages clair, vous devez connaître l’adresse de démarrage de l’en-tête de bloc. Vous trouverez l’adresse de début en vidant 30 à 40 octets sous l’adresse signalée et recherchez les modèles de début/fin magiques pour un en-tête de bloc (ABCDAAAA, ABCDBBBB, ABCDAAA9, ABCDBBBA).

L’en-tête donne toutes les informations dont vous avez besoin pour comprendre l’échec. En particulier, les motifs magiques indiquent si le bloc est alloué ou libre et s'il s'agit d'un tas de pages léger ou d'un bloc de tas de pages complet. Les renseignements ici doivent être mis en correspondance avec soin avec l’appel incriminé.

Par exemple, si un appel à HeapFree est effectué avec l’adresse d’un bloc plus quatre octets, vous obtiendrez le message endommagé. L’en-tête de bloc s’affichera correctement, mais vous devrez remarquer que le premier octet après la fin de l’en-tête (premier octet après la valeur magique 0xDCBAXXXX) a une adresse différente de celle de l’appel.

Pointeurs de remplissage spéciaux

Le gestionnaire de tas de pages remplit l’allocation utilisateur avec des valeurs qui ressemblent à des pointeurs de noyau. Cela se produit lorsque le bloc est libéré (la valeur de remplissage est F0) et lorsque le bloc est alloué, mais qu’aucune demande n’est effectuée pour que le bloc soit zéro (la valeur de remplissage est E0 pour le tas de pages clair et C0 pour le tas de pages complètes). Les allocations non nullées sont typiques pour les utilisateurs de malloc/new. S’il existe un échec (violation d’accès) où une tentative de lecture/écriture est effectuée à des adresses telles que F0F0F0F0, E0E0E0E0, C0C0C0C0, vous avez probablement atteint l’un de ces cas.

Une lecture/écriture à F0F0F0F0 signifie qu’un bloc a été utilisé après avoir été libéré. Malheureusement, vous aurez besoin d’un travail de détective pour savoir quel bloc a provoqué cela. Vous devez obtenir la trace de la pile de l'erreur, puis inspecter le code des fonctions dans la pile. L’une d’elles pourrait faire une mauvaise hypothèse concernant une allocation active.

Une lecture/écriture à E0E0E0E0/C0C0C0C0 signifie que l'application n'a pas correctement initialisé l'allocation. Cela nécessite également l’inspection du code des fonctions dans la trace de pile actuelle. Voici un exemple pour ce type d’échec. Dans un processus de test, une violation d’accès lors de l’exécution d’un HeapFree sur l’adresse E0E0E0E0 a été remarquée. Il s’est avéré que le test a alloué une structure, ne l’a pas correctement initialisée, puis a appelé le destructeur de l’objet. Étant donné qu’un certain champ n’était pas null (il avait E0E0E0E0 dedans), il a appelé la fonction supprimer dessus.

Détails techniques du tas de pages

Pour détecter les altérations de tas (dépassements de capacité ou sous-flux), AppVerifier modifie la façon dont la mémoire est allouée en remplissant la mémoire demandée avec des pages complètes non accessibles en écriture ou avec des balises spéciales avant et après la mémoire allouée. AppVerifier procède en chargeant Verifier.dll dans le processus en cours de vérification et redirige certaines des API de tas Win32 appelées par l'application vers les API Verifier.dll correspondantes.

Lors du remplissage de la mémoire demandée avec des pages non accessibles en écriture complètes (le paramètre FULL est activé dans la section propriétés du tas de pages et est le paramètre par défaut), AppVerifier consomme une grande quantité de mémoire virtuelle, mais présente l’avantage que les événements d’altération du tas sont mis en cache en temps réel lorsque le dépassement ou le sous-flux se produit. N’oubliez pas que la mémoire dans ce mode ressemble à celle-ci [AppVerifier Read-Only page tas (4k)] [Quantité de mémoire demandée par l’application sous test] ou comme suit [Quantité de mémoire demandée par l’application sous test] [AppVerifier Read-Only page tas (4k)].

La vérification du tas place une page de garde au début ou à la fin de l’allocation en fonction de la propriété Descendante. Si l’option Arrière est définie sur False, qui est la valeur par défaut, elle place une page de garde à la fin de l’allocation pour détecter les dépassements de mémoire tampon. S’il est défini sur True, la page de garde est placée au début de l’allocation pour détecter les sous-débordements de mémoire tampon.

Lorsque la mémoire demandée est remplie avec des balises spéciales (ce qui s'active en décochant la case « Full » dans les paramètres du tas), AppVerifier vérifie et vous alerte lorsque cette mémoire est libérée. Le principal problème de l’utilisation de cette technique est qu’il existe certains cas où l’altération de la mémoire ne sera détectée que lorsque la mémoire est libérée (la quantité minimale de bloc de mémoire est de 8 octets), par conséquent, lorsqu’une variable de 3 octets ou un dépassement de capacité de 5 octets se produit, elle ne sera pas immédiatement détectée.

Lors d'un événement de débordement, une tentative d’écriture sera effectuée dans une page Read-Only. Cela déclenche une exception. Notez que cette exception ne peut être interceptée que si l’application cible est exécutée sous un débogueur. Notez que le mode tas complet de pages détecte également ces erreurs, car il utilise des pages de remplissage-et-garde. La raison pour laquelle vous utiliseriez le tas de pages léger est si votre ordinateur ne peut pas tolérer les contraintes de mémoire élevée du tas de pages complet.

Pour les applications gourmandes en mémoire ou lorsqu’il est nécessaire d’utiliser AppVerifier pendant de longues périodes (par exemple, les tests de contrainte), il est préférable d’exécuter des tests de tas normaux (légers) au lieu du mode plein en raison de la dégradation des performances. Toutefois, lorsque vous rencontrez un problème, activez le full page heap pour examiner plus en détail.

Les applications qui utilisent des tas personnalisés (des tas qui contournent l’implémentation du système d’exploitation des tas) pourraient ne pas tirer pleinement parti de l'utilisation du tas de pages ou pourraient même dysfonctionner lorsque celui-ci est activé.

Débogage des erreurs de mémoire

Extension de vérification de la mémoire pour le débogueur

Le journal des opérations d’espace virtuel suit toutes les routines qui modifient l’espace virtuel d’un processus de quelque manière que ce soit. Il s’agit notamment de VirtualAlloc, VirtualFree, MapViewOfFile et UnmapViewOfFile.

Vous pouvez utiliser la commande d’extension !avrf -vs Length pour afficher les derniers enregistrements ; La longueur spécifie le nombre d’enregistrements.

Vous pouvez utiliser !avrf -vs -a Address pour afficher toutes les opérations d’espace virtuel qui ont affecté l’adresse spécifiée. Pour une allocation, il suffit que l’adresse soit contenue dans le bloc alloué. Pour un « free », l’adresse exacte du début de la région doit être spécifiée.

Pour chaque entrée dans le journal, les informations suivantes s’affichent :

  • La fonction appelée
  • ID de thread du thread qui a appelé la routine
  • Adresse impliquée dans l’appel : il s’agit de l’adresse retournée par une routine d’allocation ou transmise à une routine gratuite.
  • Taille de la région impliquée dans l’appel
  • Type d’opération de mémoire (paramètre AllocationType)
  • Type de protection demandé
  • Trace de pile de l’appel

Exemples

Les entrées les plus récentes sont affichées en premier.

Dans l’exemple suivant, les deux entrées les plus récentes sont affichées :

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

On peut voir dans le résultat que le thread 0xB4 a d'abord libéré une page, puis a libéré l'intégralité de la région virtuelle.

Voici un affichage de toutes les opérations affectant l’adresse 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

Pour lire cette sortie, n’oubliez pas que les entrées sont vidées à partir de la version la plus récente. Ainsi, ce journal montre que le thread 0xB4 a alloué une grande région où il a validé une page. Plus tard, il a omis la page, puis a libéré toute la région virtuelle.

Voir aussi

Application Verifier - Vue d’ensemble

vérificateur d’applications - Test des applications

Application Verifier - Tests au sein du vérificateur d’application

Vérificateur d’application - Codes d’arrêt et définitions

Application Verifier - Forum aux questions