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 le déclenchement 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 exceptions de la première 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 de tas détecte un dépassement de mémoire tampon de tas. Dans certains cas, l’option Vérifier l’utilisation du chemin d’accès système peut également provoquer une violation d’accès.
Une exception de handle non valide (0xC0000008) est générée lorsque l’option Détecter l’utilisation du 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 Rechercher une pile adéquate 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 capacité de pile en tant qu’exceptions de deuxième 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é : cela nécessite l’utilisation fréquente des commandes .process et .pagein, mais cela ne vous donne pas plus d’énergie 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 un seul ordinateur : le débogueur est exécuté sur le même ordinateur que l’application qui a é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 de logiciels 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 pour 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 à distance 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 à distance via remote.exe, les fichiers de symboles doivent se trouver sur l’ordinateur avec le débogueur.
Si vous effectuez un débogage à distance 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 diverses variables d’environnement pour indiquer un certain nombre de paramètres importants.
Pour plus d’informations sur les débogueurs, consultez Prise en main avec le débogage Windows
Configuration du vérificateur d’application avec le débogueur à l’aide de la ligne de commande
Pour configurer le vérificateur d’application, 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 de vérificateur d’application souhaitées 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 d’indicateur | Signification |
---|---|
00000001 | VÉRIFICATIONS DU TAS |
00000004 | HANDLE CHECKS |
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 d’Application Verifier 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.
Syntax
!avrf
La commande !avrf sans aucun paramètre 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, le cas échéant.
!avrf –vs { Length | -aAddress }
Affiche le journal des opérations d’espace virtuel. Longueur spécifie le nombre d’enregistrements à afficher à partir du plus récent. Address spécifie l’adresse virtuelle. Les enregistrements des opérations virtuelles contenant cette adresse virtuelle seront affichés.
!avrf -hp { Length | -a Address }
Affiche le journal des opérations de tas. Address spécifie l’adresse du tas. Les enregistrements des opérations de tas contenant cette adresse de tas seront affichés.
!avrf -cs { Length | -a Address }
Affiche le journal de suppression de section critique. Longueur spécifie le nombre d’enregistrements à afficher à partir du plus récent. Address spécifie l’adresse de section critique. Les enregistrements de la section critique particulière s’affichent lorsque l’adresse est spécifiée.
!avrf -dlls [ Length ]
Affiche le journal de chargement/déchargement de DLL. 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. Le vérificateur d’application 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 diverses opérations telles que la modification du masque d’affinité de thread, la modification de la priorité des threads, la publication de messages de thread, l’initialisation de COM et l’annulation de l’initialisation de COM à partir du rappel du pool de threads. La fourniture d’un ID de thread affiche uniquement des informations pour ce thread particulier.
!avrf -srw [ Address | Address Length ] [ -stats ]
Affiche le journal lecteur/enregistreur slim (SRW). La spécification de l’adresse affiche les enregistrements relatifs à cette adresse de verrouillage SRW. Lorsque La longueur est spécifiée avec l’adresse , tous les verrous SRW de cette plage d’adresses s’affichent. 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 être ou non des fuites à un moment donné. La spécification de ModuleName (y compris l’extension) affiche toutes les ressources en suspens dans le module spécifié. La spécification de ResourceType affiche les ressources en suspens de ce type de ressource particulier. La spécification des vidages d’adresses des enregistrements des ressources en attente avec 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
- Virtuel : affiche les réservations virtuelles
- BSTR : affiche les allocations BSTR
- Registre : affiche la clé de Registre s’ouvre
- Power : affiche les objets power notification
- 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 la liste des compteurs globaux.
!avrf -brk [ BreakEventType ]
Spécifie qu’il s’agit d’une commande break-event. Quand !avrf -brk
est utilisé sans paramètres supplémentaires, les paramètres de l’événement d’arrêt s’affichent. 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. Quand !avrf -flt
est utilisé sans paramètres supplémentaires, les paramètres d’injection d’erreur actuels s’affichent. 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 de n’importe quel entier compris entre 0 et 1 000 000 (0xF4240).
!avrf -flt break EventType
Provoque le vérificateur d’application à s’introduire dans le débogueur chaque fois que cette erreur est injectée.
!avrf -flt stacks Length
Affiche le nombre de longueurs de traces de pile pour les opérations d’injection d’erreurs 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 s’affichent. Start spécifie l’adresse de début de la plage cible ou de la plage d’exclusion. End spécifie l’adresse de fin de la plage cible ou de la plage d’exclusion. 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 entraîne la réinitialisation de toutes les plages cibles ou de plages 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. End spécifie l’adresse de fin de la plage cible ou de la plage d’exclusion. 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 entraîne la réinitialisation de toutes les plages cibles ou de plages d’exclusion. La spécification de l’heure entraîne la suppression de toutes les erreurs pendant les millisecondes de temps 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.
!avrf extension commentaires
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 son origine.
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 continu
Voici un exemple d’exception de handle non valide qui a été levée par l’option Détecter l’utilisation du handle 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ésume le problème.
Vous souhaiterez peut-être examiner certains 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 l’arrêt du vérificateur d’application ne peut pas être poursuivi. L’erreur est trop grave pour que le processus continue à s’exécuter, et il n’existe aucun moyen pour Application Verifier de sauver 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ésume le problème.
Vous pouvez également consulter certains 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 De vérificateur d’application et commencer à corriger les bogues dans votre code.
Erreurs de section critique de débogage
Extension de débogueur !cs
!cs peut être utilisé à la fois dans le débogueur en mode utilisateur et dans le débogueur du noyau pour afficher des informations sur les sections critiques du processus en cours. Pour plus d’informations sur l’extension !cs, consultez !cs dans la documentation du débogueur.
La mise en correspondance de symboles avec des informations de type est requise, en particulier pour les ntdll.dll.
La syntaxe de cette extension est la suivante :
!cs [-s] : videz toutes les sections critiques actives du processus actuel.
!cs [-s] address - section critique de vidage à cette adresse.
!cs [-s] -d address - section critique de vidage correspondant à DebugInfo à cette adresse.
-s videra la trace de la pile d’initialisation de section critique si elle est disponible.
Exemples :
Vidage des informations sur une section critique à l’aide de son adresse
0:001> ! cs 0x7803B0F8
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo = 0x6A262080
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Vidage des informations sur une section critique à l’aide de son adresse, y compris le suivi de la pile d’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
Vider des informations sur une section critique à l’aide de 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
Vider des informations sur une section critique à l’aide de son adresse d’informations de débogage, y compris le suivi 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
Informations de vidage 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
.....
Informations de vidage sur toutes les sections critiques actives du processus en cours, y compris le suivi de la 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 de débogage
Le journal des exceptions enregistre toutes les exceptions qui se sont produites dans le processus cible.
Vous pouvez utiliser la commande d’extension !avrf -ex Length pour afficher les dernières exceptions ; Length spécifie le nombre d’exceptions. Si Longueur est omise, toutes les exceptions s’affichent.
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 du noyau pour afficher les informations de trace de pile pour un ou tous les handles d’un processus. Ces informations sont disponibles si le suivi de handle est activé pour le processus , automatiquement activé si la vérification du handle est activée dans le vérificateur d’application. Les traces de pile sont enregistrées chaque fois que le processus ouvre ou ferme un handle ou lorsqu’il fait 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 de noyau pour cette extension est la suivante :
!htrace [ handle [process] ]
Si handle n’est pas spécifié ou a la valeur 0, des informations sur tous les handles du processus s’affichent. 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.
Informations de vidage de 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 des 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 de débogueur de tas de tas NT). Une aide simple peut être obtenue avec !heap - ? ou plus complet avec !heap -p - ? . L’extension actuelle ne détecte pas elle-même si le tas de page est activé pour un processus et agit en conséquence. Pour l’instant, l’utilisateur de l’extension doit savoir que le tas de page est activé et utiliser 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
Vide les adresses de tous les tas de pages entières créés dans le processus.
!heap -p -h ADDRESS-OF-HEAP
Vidage complet du tas de page complète sur ADDRESS-OF-HEAP.
!heap -p -a ADDRESS
Tente de déterminer s’il existe un bloc de tas au niveau de l’adresse. Cette valeur n’a pas besoin d’être l’adresse du début du bloc. La commande est utile s’il n’y a aucun indice sur la nature d’une zone de mémoire.
Journal des opérations de tas
Le journal des opérations de tas suit toutes les routines de tas. Il s’agit notamment de HeapAlloc, HeapReAlloc et HeapFree.
Vous pouvez utiliser la commande d’extension !avrf -hp Length
pour afficher les derniers enregistrements ; 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 du journal, les informations suivantes s’affichent :
- Fonction de tas appelée.
- ID de thread du thread qui a appelé la routine.
- Adresse impliquée dans l’appel : il s’agit de l’adresse qui a été retournée par une routine d’allocation ou qui a été passée à une routine gratuite.
- Taille de la région impliquée dans l’appel.
- Trace de pile de l’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 obtenir l’ensemble de l’image.
Violation d’accès dans la page non accessible
Cela se produit lorsque le tas de page complète est activé si l’application testée accède au-delà de la fin de la mémoire tampon. Cela 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 blocage endommagé
À plusieurs moments de la durée de vie d’une allocation (allocation, gratuit pour l’utilisateur, gratuit réel), le gestionnaire de tas de page vérifie si le bloc a tous les modèles de remplissage intacts et si l’en-tête de bloc contient des données cohérentes. Si ce n’est pas le cas, vous obtiendrez un arrêt du vérificateur.
Si le bloc est un bloc de tas de page complète (par exemple, si vous savez que le tas de page complète est activé pour toutes les allocations), vous pouvez utiliser « !heap –p –a ADDRESS » pour déterminer quelles sont les caractéristiques du bloc.
Si le bloc est un bloc de tas de pages légères, vous devez trouver l’adresse de début de l’en-tête de bloc. Vous pouvez trouver l’adresse de début en plaçant entre 30 et 40 octets sous l’adresse signalée et rechercher les modèles magiques de début/fin pour un en-tête de bloc (ABCDAAAA, ABCDBBBB, ABCDAAA9, ABCDBBBA).
L’en-tête fournit toutes les informations dont vous avez besoin pour comprendre l’échec. En particulier, les modèles magiques indiquent si le bloc est alloué ou libre s’il s’agit d’un tas de pages légère ou d’un bloc de tas de page entière. Les informations ici doivent être soigneusement mises en correspondance avec l’appel incriminé.
Par exemple, si un appel à HeapFree est effectué avec l’adresse d’un bloc plus quatre octets, vous obtenez le message endommagé. L’en-tête de bloc se présente 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 ressembleront à 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 requête n’est effectuée pour que le bloc soit mis à zéro (la valeur de remplissage est E0 pour le tas de pages légères et C0 pour le tas de pages entières). Les allocations non nulles sont typiques pour malloc/nouveaux utilisateurs. En cas d’échec (violation d’accès) où une tentative de lecture/écriture est effectuée à des adresses telles que F0F0F0F0, E0E0E0E0 C0C0C0C0 vous rencontrez probablement l’un de ces cas.
Une lecture/écriture à F0F0F0F0 signifie qu’un bloc a été utilisé après sa libération. 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’échec, puis inspecter le code des fonctions sur la pile. L’un d’entre eux pourrait faire une fausse hypothèse sur une allocation en vie.
Une lecture/écriture à E0E0E0E0/C0C0C0C0 signifie que l’application n’a pas initialisé correctement l’allocation. Cela nécessite également une inspection du code des fonctions dans la trace de pile actuelle. Ici, il s’agit d’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 initialisée correctement, 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é supprimer dessus.
Détails techniques du tas de page
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. Pour ce faire, AppVerifier charge 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 complètes non accessibles en écriture (le paramètre FULL est activé dans la section propriétés du tas de page 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 de ce mode ressemble à ceci [AppVerifier Read-Only Page tas (4k)] [Quantité de mémoire demandée par l’application sous test] ou comme ceci [Quantité de mémoire demandée par l’application sous test] [AppVerifier Read-Only Page tas (4 Ko)].
Le tas case activée place une page de garde au début ou à la fin de l’allocation en fonction de la propriété Backward. Si Backward a la valeur False, qui est la valeur par défaut, il place une page de garde à la fin de l’allocation pour intercepter les dépassements de mémoire tampon. Si elle est définie sur True, la page de garde est placée au début de l’allocation pour intercepter les sous-exécutions de la mémoire tampon.
Lors du remplissage de la mémoire demandée avec des balises spéciales (activé en désactivant l’élément de zone de case activée « Complet » dans les propriétés du tas), AppVerifier case activée et vous avertit quand cette mémoire est libérée. Le main problème dans l’utilisation de cette technique est qu’il existe des cas où l’altération de la mémoire n’est 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, lorsque sur une variable de 3 octets ou un dépassement de capacité de 5 octets se produit, il ne sera pas immédiatement détecté.
Sur un événement underflow, une tentative d’écriture est effectuée dans une page de 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 de page complète détecte également ces erreurs, car il utilise les pages de remplissage et de protection. La raison pour laquelle vous utiliseriez le tas de pages légères est si votre ordinateur ne peut pas tolérer les contraintes de mémoire élevées du tas de page entière.
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) plutôt qu’en mode complet en raison de la dégradation des performances. Toutefois, lorsque vous rencontrez un problème, activez le tas de pages entières pour approfondir vos recherches.
Les applications qui utilisent des tas personnalisés (un tas qui contourne l’implémentation du tas par le système d’exploitation) peuvent ne pas bénéficier pleinement de l’utilisation du tas de pages ou même d’un dysfonctionnement lorsqu’il est activé.
Débogage des erreurs de mémoire
Extension du débogueur du vérificateur de mémoire
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 ; 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 Address soit contenue dans le bloc alloué. Pour une gratuité, l’adresse exacte du début de la région doit être donnée.
Pour chaque entrée du 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 qui a été retournée par une routine d’allocation ou qui a été passée à 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 s’affichent :
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
La sortie permet de voir que le thread 0xB4 d’abord décompressé une page, puis libéré l’ensemble 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 en commençant par la plus récente. Ainsi, ce journal indique que le thread 0xB4 alloué une grande région dans laquelle il a engagé une page. Plus tard, il a décodé la page, puis libéré toute la région virtuelle.
Voir aussi
Vérificateur d’application - Vue d’ensemble
Vérificateur d’applications - Test des applications
Vérificateur d’application - Tests dans Application Verifier