Écriture d’un plug-in d’extension d’analyse pour Étendre !analyze
Vous pouvez étendre les fonctionnalités de la commande !analyze débogueur en écrivant un plug-in d’extension d’analyse. En fournissant un plug-in d’extension d’analyse, vous pouvez participer à l’analyse d’un bogue case activée ou d’une exception d’une manière spécifique à votre propre composant ou application.
Lorsque vous écrivez un plug-in d’extension d’analyse, vous écrivez également un fichier de métadonnées qui décrit les situations pour lesquelles vous souhaitez que votre plug-in soit appelé. Lorsque !analyze s’exécute, il localise, charge et exécute les plug-ins d’extension d’analyse appropriés.
Pour écrire un plug-in d’extension d’analyse et le mettre à la disposition de !analyze, procédez comme suit.
- Créez une DLL qui exporte une fonction _EFN_Analyze .
- Créez un fichier de métadonnées portant le même nom que votre DLL et une extension de .alz. Par exemple, si votre DLL est nommée MyAnalyzer.dll, votre fichier de métadonnées doit être nommé MyAnalyzer.alz. Pour plus d’informations sur la création d’un fichier de métadonnées, consultez Fichiers de métadonnées pour les extensions d’analyse. Placez le fichier de métadonnées dans le même répertoire que votre DLL.
- Dans le débogueur, utilisez la commande .extpath pour ajouter votre répertoire au chemin du fichier d’extension. Par exemple, si votre DLL et votre fichier de métadonnées se trouvent dans le dossier nommé c :\MyAnalyzer, entrez la commande .extpath+ c :\MyAnalyzer.
Lorsque la commande !analyze s’exécute dans le débogueur, le moteur d’analyse recherche dans le chemin du fichier d’extension les fichiers de métadonnées qui ont l’extension .alz. Le moteur d’analyse lit les fichiers de métadonnées pour déterminer quels plug-ins d’extension d’analyse doivent être chargés. Par exemple, supposons que le moteur d’analyse s’exécute en réponse à La vérification des bogues 0xA IRQL_NOT_LESS_OR_EQUAL et qu’il lit un fichier de métadonnées nommé MyAnalyzer.alz qui contient les entrées suivantes.
PluginId MyPlugin
DebuggeeClass Kernel
BugCheckCode 0xA
BugCheckCode 0xE2
L’entrée BugCheckCode 0x0A
spécifie que ce plug-in souhaite participer à l’analyse des 0xA de vérification des bogues, de sorte que le moteur d’analyse charge MyAnalyzer.dll (qui doit se trouver dans le même répertoire que MyAnalyzer.alz) et appelle sa fonction _EFN_Analyze .
Note La dernière ligne du fichier de métadonnées doit se terminer par un caractère de nouvelle ligne.
Exemple squelette
Voici un exemple de squelette que vous pouvez utiliser comme point de départ.
Créez une DLL nommée MyAnalyzer.dll qui exporte la fonction _EFN_Analyze illustrée ici.
#include <windows.h> #define KDEXT_64BIT #include <wdbgexts.h> #include <dbgeng.h> #include <extsfns.h> extern "C" __declspec(dllexport) HRESULT _EFN_Analyze(_In_ PDEBUG_CLIENT4 Client, _In_ FA_EXTENSION_PLUGIN_PHASE CallPhase, _In_ PDEBUG_FAILURE_ANALYSIS2 pAnalysis) { HRESULT hr = E_FAIL; PDEBUG_CONTROL pControl = NULL; hr = Client->QueryInterface(__uuidof(IDebugControl), (void**)&pControl); if(S_OK == hr && NULL != pControl) { IDebugFAEntryTags* pTags = NULL; pAnalysis->GetDebugFATagControl(&pTags); if(NULL != pTags) { if(FA_PLUGIN_INITILIZATION == CallPhase) { pControl->Output(DEBUG_OUTPUT_NORMAL, "My analyzer: initialization\n"); } else if(FA_PLUGIN_STACK_ANALYSIS == CallPhase) { pControl->Output(DEBUG_OUTPUT_NORMAL, "My analyzer: stack analysis\n"); } else if(FA_PLUGIN_PRE_BUCKETING == CallPhase) { pControl->Output(DEBUG_OUTPUT_NORMAL, "My analyzer: prebucketing\n"); } else if(FA_PLUGIN_POST_BUCKETING == CallPhase) { pControl->Output(DEBUG_OUTPUT_NORMAL, "My analyzer: post bucketing\n"); FA_ENTRY_TYPE entryType = pTags->GetType(DEBUG_FLR_BUGCHECK_CODE); pControl->Output(DEBUG_OUTPUT_NORMAL, "The data type for the DEBUG_FLR_BUGCHECK_CODE tag is 0x%x.\n\n", entryType); } } pControl->Release(); } return hr; }
Créez un fichier de métadonnées nommé MyAnalyzer.alz qui contient les entrées suivantes.
PluginId MyPlugin DebuggeeClass Kernel BugCheckCode 0xE2
Note La dernière ligne du fichier de métadonnées doit se terminer par un caractère de nouvelle ligne.
Établissez une session de débogage en mode noyau entre un hôte et l’ordinateur cible.
Sur l’ordinateur hôte, placez MyAnalyzer.dll et MyAnalyzer.alz dans le dossier c :\MyAnalyzer.
Sur l’ordinateur hôte, dans le débogueur, entrez ces commandes.
.extpath+ c :\MyAnalyzer
.Crash
La commande .crash génère des 0xE2 MANUALLY_INITIATED_CRASH de vérification des bogues sur l’ordinateur cible, ce qui provoque une interruption du débogueur sur l’ordinateur hôte. Le moteur d’analyse du bogue case activée (en cours d’exécution dans le débogueur sur l’ordinateur hôte) lit MyAnalyzer.alz et voit que MyAnalyzer.dll est en mesure de participer à l’analyse des case activée 0xE2 de bogues. Ainsi, le moteur d’analyse charge MyAnalyzer.dll et appelle sa fonction _EFN_Analyze .
Vérifiez que vous voyez une sortie similaire à ce qui suit dans le débogueur.
* Bugcheck Analysis * * * ******************************************************************************* Use !analyze -v to get detailed debugging information. BugCheck E2, {0, 0, 0, 0} My analyzer: initialization My analyzer: stack analysis My analyzer: prebucketing My analyzer: post bucketing The data type for the DEBUG_FLR_BUGCHECK_CODE tag is 0x1.
La sortie du débogueur précédent montre que le moteur d’analyse a appelé la fonction _EFN_Analyze quatre fois : une fois pour chaque phase de l’analyse. Le moteur d’analyse transmet à la fonction _EFN_Analyze deux pointeurs d’interface. Le client est une interface IDebugClient4 et pAnalysis est une interface IDebugFailureAnalysis2 . Le code de l’exemple de squelette précédent montre comment obtenir deux pointeurs d’interface supplémentaires. Client->QueryInterface
obtient une interface IDebugControl et pAnalysis->GetDebugFATagControl
une interface IDebugFAEntryTags .
Entrées, étiquettes et types de données d’analyse des échecs
Le moteur d’analyse crée un objet DebugFailureAnalysis pour organiser les données liées à une défaillance de code particulière. Un objet DebugFailureAnalysis a une collection d’entrées d’analyse des défaillances (entrées FA), chacune d’elles étant représentée par une structure FA_ENTRY . Un plug-in d’extension d’analyse utilise l’interface IDebugFailureAnalysis2 pour accéder à cette collection d’entrées FA. Chaque entrée FA a une étiquette qui identifie le type d’informations que contient l’entrée. Par exemple, une entrée FA peut avoir la balise DEBUG_FLR_BUGCHECK_CODE, ce qui nous indique que l’entrée contient un bogue case activée code. Les balises sont des valeurs de l’énumération DEBUG_FLR_PARAM_TYPE (définie dans extsfns.h), qui est également appelée énumération FA_TAG .
typedef enum _DEBUG_FLR_PARAM_TYPE {
...
DEBUG_FLR_BUGCHECK_CODE,
...
DEBUG_FLR_BUILD_VERSION_STRING,
...
} DEBUG_FLR_PARAM_TYPE;
typedef DEBUG_FLR_PARAM_TYPE FA_TAG;
La plupart des entrées FA ont un bloc de données associé. Le membre DataSize de la structure FA_ENTRY contient la taille du bloc de données. Certaines entrées FA n’ont pas de bloc de données associé ; toutes les informations sont transmises par la balise. Dans ce cas, le membre DataSize a la valeur 0.
typedef struct _FA_ENTRY
{
FA_TAG Tag;
USHORT FullSize;
USHORT DataSize;
} FA_ENTRY, *PFA_ENTRY;
Chaque balise a un ensemble de propriétés : par exemple, nom, description et type de données. Un objet DebugFailureAnalysis est associé à un objet DebugFailureAnalysisTags , qui contient une collection de propriétés de balise. Le diagramme suivant illustre cette association.
Un objet DebugFailureAnalysis a une collection d’entrées FA qui appartiennent à une session d’analyse particulière. L’objet DebugFailureAnalysisTags associé a une collection de propriétés de balise qui inclut uniquement les balises utilisées par cette même session d’analyse. Comme le montre le diagramme précédent, le moteur d’analyse dispose d’une table de balises globale qui contient des informations limitées sur un grand ensemble de balises qui sont généralement disponibles pour une utilisation par les sessions d’analyse.
En règle générale, la plupart des balises utilisées par une session d’analyse sont des balises standard ; autrement dit, les balises sont des valeurs dans l’énumération FA_TAG . Toutefois, un plug-in d’extension d’analyse peut créer des balises personnalisées. Un plug-in d’extension d’analyse peut ajouter une entrée FA à un objet DebugFailureAnalysis et spécifier une balise personnalisée pour l’entrée. Dans ce cas, les propriétés de la balise personnalisée sont ajoutées à la collection de propriétés de balise dans l’objet DebugFailureAnalysisTags associé.
Vous pouvez accéder à un DebugFailureAnalysisTags via une interface de balises IDebugFAEntry. Pour obtenir un pointeur vers une interface IDebugFAEntry, appelez la méthode GetDebugFATagControl de l’interface IDebugFailureAnalysis2 .
Chaque balise a une propriété de type de données que vous pouvez inspecter pour déterminer le type de données des données dans une entrée d’analyse des échecs. Un type de données est représenté par une valeur dans l’énumération FA_ENTRY_TYPE .
La ligne de code suivante obtient le type de données de la balise DEBUG_FLR_BUILD_VERSION_STRING . Dans ce cas, le type de données est DEBUG_FA_ENTRY_ANSI_STRING. Dans le code, pAnalysis
est un pointeur vers une interface IDebugFailureAnalysis2 .
IDebugFAEntryTags* pTags = pAnalysis->GetDebugFATagControl(&pTags);
if(NULL != pTags)
{
FA_ENTRY_TYPE entryType = pTags->GetType(DEBUG_FLR_BUILD_VERSION_STRING);
}
Si une entrée d’analyse des défaillances n’a pas de bloc de données, le type de données de la balise associée est DEBUG_FA_ENTRY_NO_TYPE.
Rappelez-vous qu’un objet DebugFailureAnalysis a une collection d’entrées FA. Pour inspecter toutes les entrées FA de la collection, utilisez la méthode NextEntry . L’exemple suivant montre comment itérer dans l’ensemble de la collection d’entrées FA. Supposons que pAnalysis est un pointeur vers une interface IDebugFailureAnalysis2 . Notez que nous obtenons la première entrée en passant NULL à NextEntry.
PFA_ENTRY entry = pAnalysis->NextEntry(NULL);
while(NULL != entry)
{
// Do something with the entry
entry = pAnalysis->NextEntry(entry);
}
Une balise peut avoir un nom et une description. Dans le code suivant, pAnalysis est un pointeur vers une interface IDebugFailureAnalysis , pControl est un pointeur vers une interface IDebugControl et pTags
un pointeur vers une interface IDebugFAEntryTags . Le code montre comment utiliser la méthode GetProperties pour obtenir le nom et la description de la balise associée à une entrée FA.
#define MAX_NAME_LENGTH 64
#define MAX_DESCRIPTION_LENGTH 512
CHAR name[MAX_NAME_LENGTH] = {0};
ULONG nameSize = MAX_NAME_LENGTH;
CHAR desc[MAX_DESCRIPTION_LENGTH] = {0};
ULONG descSize = MAX_DESCRIPTION_LENGTH;
PFA_ENTRY pEntry = pAnalysis->NextEntry(NULL);
pTags->GetProperties(pEntry->Tag, name, &nameSize, desc, &descSize, NULL);
pControl->Output(DEBUG_OUTPUT_NORMAL, "The name is %s\n", name);
pControl->Output(DEBUG_OUTPUT_NORMAL, "The description is %s\n", desc);
Voir aussi
Écriture d’extensions de débogueur d’analyse personnalisée
Fichiers de métadonnées pour les plug-ins d’extension d’analyse