Objets débogueur natifs dans les extensions JavaScript

Les objets de débogueur natifs représentent différents constructions et comportements de l’environnement du débogueur. Les objets peuvent être passés dans (ou acquis dans) des extensions JavaScript pour manipuler l’état du débogueur.

Les exemples d’objets de débogueur sont les suivants.

  • session
  • Threads / Thread
  • Processus / Processus
  • Stack Frames / Stack Frame
  • Variables locales
  • Modules / Module
  • Utilitaire
  • State
  • Paramètres

Par exemple, l’objet host.namespace.Debugger.Utility.Control.ExecuteCommand peut être utilisé pour envoyer la commande u au débogueur avec deux lignes de code JavaScript suivantes.

var ctl = host.namespace.Debugger.Utility.Control;   
var outputLines = ctl.ExecuteCommand("u");

Cette rubrique décrit comment utiliser des objets courants et fournit des informations de référence sur leurs attributs et comportements.

Pour plus d’informations générales sur l’utilisation de JavaScript, consultez Script du débogueur JavaScript. Pour obtenir des exemples JavaScript qui utilisent les objets de débogueur , consultez Exemples de scripts du débogueur JavaScript. Pour plus d’informations sur l’utilisation des objets de paramètres, consultez .settings (Définir les paramètres de débogage).

Pour explorer les objets disponibles dans une session de débogueur, utilisez la commande dx (Afficher l’expression NatVis). Par exemple, vous pouvez afficher certains des objets de débogueur de niveau supérieur avec cette commande dx.

0: kd> dx -r2 Debugger
Debugger                
    Sessions         : [object Object]
        [0x0]            : Remote KD: KdSrv:Server=@{<Local>},Trans=@{NET:Port=50000,Key=1.2.3.4,Target}
    Settings        
        Debug           
        Display         
        EngineInitialization
        Extensions      
        Input           
        Sources         
        Symbols         
        AutoSaveSettings : false
    State           
        DebuggerVariables
        PseudoRegisters 
        Scripts         
        UserVariables   
    Utility         
        Collections     
        Control         
        Objects   

Tous les éléments répertoriés ci-dessus sont cliquables DML et peuvent être récursés plus bas pour afficher la structure de l’objet débogueur.

Extension du débogueur via le modèle de données

Le modèle de données du débogueur permet de créer une interface vers des informations sur les applications et les pilotes dans Windows qui possède les attributs suivants.

  • Est détectable et organisé : un espace de nom logiquement structuré peut être interrogé à l’aide de la commande dx.
  • Peut être interrogé à l’aide de LINQ : cela permet l’extraction et le tri des données à l’aide d’un langage de requête standard.
  • Peut être étendu de manière logique et cohérente : extensible à l’aide des techniques décrites dans cette rubrique avec des fournisseurs de script de débogueur tels que Natvis et JavaScript.

Extension d’un objet Débogueur en JavaScript

En plus de pouvoir créer un visualiseur en JavaScript, les extensions de script peuvent également modifier les concepts de base du débogueur ( sessions, processus, threads, piles, trames de pile, variables locales) et même se publier en tant que points d’extension que d’autres extensions peuvent consommer.

Cette section explique comment étendre un concept de base dans le débogueur. Les extensions qui sont conçues pour être partagées doivent être conformes aux instructions présentées dans Objets débogueur natifs dans les extensions JavaScript - Considérations relatives à la conception et au test.

Inscription d’une extension

Un script peut inscrire le fait qu’il fournit une extension par le biais d’une entrée dans le tableau retournée par la méthode initializeScript.

function initializeScript()
{
    return [new host.namedModelParent(comProcessExtension, "Debugger.Models.Process")];
}

La présence d’un objet host.namedModelParent dans le tableau retourné indique au débogueur qu’un objet prototype ou une classe ES6 donné (comProcessExtension dans ce cas) sera un modèle de données parent pour le modèle qui est inscrit sous le nom Debugger.Models.Process.

Points d’extension d’objet débogueur

Les points d’extension de débogueur suivants font partie intégrante du débogueur et peuvent être utilisés par les fournisseurs de script tels que JavaScript.

Debugger.Models.Sessions : liste des sessions (cibles) auxquelles le débogueur est attaché

Debugger.Models.Session : session individuelle (cible) à laquelle le débogueur est attaché (mode utilisateur actif, KD, etc.)

Debugger.Models.Processes : liste des processus au sein d’une session

Debugger.Models.Threads : liste des threads au sein d’un processus

Debugger.Models.Thread : thread individuel au sein d’un processus (que ce soit en mode utilisateur ou noyau)

Debugger.Models.Stack : pile d’un thread

Debugger.Models.StackFrames : collection d’images qui composent une pile

Debugger.Models.StackFrame : un cadre de pile individuel au sein d’une pile

Debugger.Models.LocalVariables : variables locales dans un frame de pile

Debugger.Models.Parameters : paramètres d’un appel dans un cadre de pile

Debugger.Models.Module : module individuel dans l’espace d’adressage d’un processus

Objets de modèle de données supplémentaires

En outre, il existe d’autres objets de modèle de données qui sont définis par le modèle de données de base.

DataModel.Models.Intrinsic : valeur intrinsèque (ordinals, floats, etc...)

DataModel.Models.String : chaîne

DataModel.Models.Array : tableau natif

DataModel.Models.Guid : GUID

DataModel.Models.Error : objet d’erreur

DataModel.Models.Concepts.Iterable : appliqué à chaque objet itérable

DataModel.Models.Concepts.StringDisplayable : appliqué à chaque objet qui a une conversion de chaîne d’affichage

Exemple de vue d’ensemble de l’extension d’objet de débogueur COM

Prenons un exemple. Imaginez que vous souhaitez créer une extension de débogueur pour afficher des informations spécifiques à COM, telles que la table d’interface globale (GIT).

Dans le passé, il peut y avoir une extension de débogueur existante avec un certain nombre de commandes qui fournissent un moyen d’accéder aux éléments relatifs à COM. Une commande peut afficher des informations centrées sur le processus (la table d’interface globale pour instance). Une autre commande peut fournir des informations centrées sur les threads, telles que le code d’appartement qui s’exécute dans. Vous devrez peut-être connaître et charger une deuxième extension de débogueur pour explorer d’autres aspects de COM.

Au lieu d’avoir un ensemble de commandes difficiles à découvrir, une extension JavaScript peut modifier le concept du débogueur de ce qu’est un processus et un thread, afin d’ajouter ces informations de manière naturelle, explorable et composable avec d’autres extensions de débogueur.

Extension d’objet débogueur en mode utilisateur ou noyau

Le débogueur et les objets débogueur ont un comportement différent en mode utilisateur et en mode noyau. Lorsque vous créez des objets de modèle de débogueur, vous devez décider des environnements dans lesquels vous allez travailler. Étant donné que nous allons utiliser COM en mode utilisateur, nous allons créer et tester cette extension com en mode utilisateur. Dans d’autres situations, vous pouvez créer un débogueur JavaScript qui fonctionnera en mode utilisateur et en mode noyau.

Création d’un sous-espace de noms

Pour revenir à notre exemple, nous pouvons définir un prototype ou une classe ES6, comProcessExtension , qui contient l’ensemble des éléments que nous voulons ajouter à un objet de processus.

Important L’intention avec le sous-espace de noms est de créer un paradigme logiquement structuré et naturellement explorable. Par exemple, évitez de vider des éléments non liés dans le même sous-espace de noms. Passez en revue attentivement les informations décrites dans Native Debugger Objects in JavaScript Extensions - Design and Testing Considerations avant de créer un sous-espace de noms.

Dans cet extrait de code, nous créons un sous-espace de noms appelé « COM » sur à l’objet de débogueur de processus existant.

var comProcessExtension =
{
    //
    // Add a sub-namespace called 'COM' on process.
    //
    get COM()
    {
        //
        // What is 'this' below...?  It's the debugger's process object.  Yes -- this means that there is a cross-language
        // object hierarchy here.  A C++ object implemented in the debugger has a parent model (prototype) which is
        // implemented in JavaScript.
        //
        return new comNamespace(this);
    }
}

Implémentation de l’espace de noms

Ensuite, créez l’objet qui implémente le sous-espace de noms COM sur un processus.

Important Il peut y avoir plusieurs processus (qu’ils soient attachés à ces processus en mode utilisateur ou sous KD). Cette extension ne peut pas supposer que l’état actuel du débogueur correspond à ce que l’utilisateur a prévu. Quelqu’un peut capturer <someProcess.COM> dans une variable et le modifier, ce qui peut entraîner la présentation d’informations à partir d’un contexte de processus incorrect. La solution consiste à ajouter du code dans l’extension afin que chaque instanciation conserve le suivi du processus auquel elle est attachée. Pour cet exemple de code, ces informations sont transmises via le pointeur « this » de la propriété .

this.__process = process;

class comNamespace
{
    constructor(process)
    {
        //
        // This is an entirely JavaScript object.  Each instantiation of a comNamespace will keep track
        // of what process it is attached to (passed via the ''this'' pointer of the property getter
        // we authored above.
        //
        this.__process = process;
    }
    
    get GlobalObjects()
    {
        return new globalObjects(this.__process);
    }
}

Logique d’implémentation pour la table d’interface globale COM

Pour séparer plus clairement la logique d’implémentation de la table d’interface globale COM, nous allons définir une classe ES6, gipTable qui extrait la table COM GIP et une autre, globalObjects, qui sera renvoyée à partir du getter GlobalObjects() défini dans le code d’implémentation de l’espace de noms illustré ci-dessus. Tous ces détails peuvent être masqués à l’intérieur de la fermeture d’initializeScript pour éviter la publication de ces détails internes dans l’espace de noms du débogueur.

// gipTable:
//
// Internal class which abstracts away the GIP Table.  It iterates objects of the form
// {entry : GIPEntry, cookie : GIT cookie}
//
class gipTable
{
    constructor(gipProcess)
    {
        //
        // Windows 8 through certain builds of Windows 10, it's in CGIPTable::_palloc.  In certain builds
        // of Windows 10 and later, this has been moved to GIPEntry::_palloc.  We need to check which.
        //
        var gipAllocator = undefined;
        try
        {
            gipAllocator = host.getModuleSymbol("combase.dll", "CGIPTable::_palloc", "CPageAllocator", gipProcess)._pgalloc;
        }
        catch(err)
        {
        }

        if (gipAllocator == undefined)
        {
            gipAllocator = host.getModuleSymbol("combase.dll", "GIPEntry::_palloc", "CPageAllocator", gipProcess)._pgalloc;
        }

        this.__data = {
            process : gipProcess,
            allocator : gipAllocator,
            pageList : gipAllocator._pPageListStart,
            pageCount : gipAllocator._cPages,
            entriesPerPage : gipAllocator._cEntriesPerPage,
            bytesPerEntry : gipAllocator._cbPerEntry,
            PAGESHIFT : 16,
            PAGEMASK : 0x0000FFFF,
            SEQNOMASK : 0xFF00
        };
    }

    *[Symbol.iterator]()
    {
        for (var pageNum = 0; pageNum < this.__data.pageCount; ++pageNum)
        {
            var page = this.__data.pageList[pageNum];
            for (var entryNum = 0; entryNum < this.__data.entriesPerPage; ++entryNum)
            {
                var entryAddress = page.address.add(this.__data.bytesPerEntry * entryNum);
                var gipEntry = host.createPointerObject(entryAddress, "combase.dll", "GIPEntry *", this.__data.process);
                if (gipEntry.cUsage != -1 && gipEntry.dwType != 0)
                {
                    yield {entry : gipEntry, cookie : (gipEntry.dwSeqNo | (pageNum << this.__data.PAGESHIFT) | entryNum)};
                }
            }
        }
    }

    entryFromCookie(cookie)
    {
        var sequenceNo = (cookie & this.__data.SEQNOMASK);
        cookie = cookie & ~sequenceNo;
        var pageNum = (cookie >> this.__data.PAGESHIFT);
        if (pageNum < this.__data.pageCount)
        {
            var page = this.__data.pageList[pageNum];
            var entryNum = (cookie & this.__data.PAGEMASK);
            if (entryNum < this.__data.entriesPerPage)
            {
                var entryAddress = page.address.add(this.__data.bytesPerEntry * entryNum);
                var gipEntry = host.createPointerObject(entryAddress, "combase.dll", "GIPEntry *", this.__data.process);
                if (gipEntry.cUsage != -1 && gipEntry.dwType != 0 && gipEntry.dwSeqNo == sequenceNo)
                {
                    return {entry : gipEntry, cookie : (gipEntry.dwSeqNo | (pageNum << this.__data.PAGESHIFT) | entryNum)};
                }
            }
        }

        //
        // If this exception flows back to C/C++, it will be a failed HRESULT (according to the type of error -- here E_BOUNDS)
        // with the message being encapsulated by an error object.
        //
        throw new RangeError("Unable to find specified value");
    }
}
// globalObjects:
//
// The class which presents how we want the GIP table to look to the data model.  It iterates the actual objects
// in the GIP table indexed by their cookie.
//
class globalObjects
{
    constructor(process)
    {
        this.__gipTable = new gipTable(process);
    }

    *[Symbol.iterator]()
    {
        for (var gipCombo of this.__gipTable)
        {
            yield new host.indexedValue(gipCombo.entry.pUnk, [gipCombo.cookie]);
        }
    }

    getDimensionality()
    {
        return 1;
    }

    getValueAt(cookie)
    {
        return this.__gipTable.entryFromCookie(cookie).entry.pUnk;
    }
}

Enfin, utilisez host.namedModelRegistration pour inscrire la nouvelle fonctionnalité COM.

function initializeScript()
{
    return [new host.namedModelParent(comProcessExtension, "Debugger.Models.Process"),
            new host.namedModelRegistration(comNamespace, "Debugger.Models.ComProcess")];
}

Enregistrez le code dans GipTableAbstractor.js à l’aide d’une application telle que le bloc-notes.

Voici les informations de processus disponibles en mode utilisateur avant le chargement de cette extension.

0:000:x86> dx @$curprocess
@$curprocess                 : DataBinding.exe
    Name             : DataBinding.exe
    Id               : 0x1b9c
    Threads         
    Modules  

Chargez l’extension JavaScript.

0:000:x86> .scriptload C:\JSExtensions\GipTableAbstractor.js
JavaScript script successfully loaded from 'C:\JSExtensions\GipTableAbstractor.js'

Ensuite, utilisez la commande dx pour afficher des informations sur le processus à l’aide du @$curprocess prédéfini.

0:000:x86> dx @$curprocess
@$curprocess                 : DataBinding.exe
    Name             : DataBinding.exe
    Id               : 0x1b9c
    Threads         
    Modules         
    COM              : [object Object]
0:000:x86> dx @$curprocess.COM
@$curprocess.COM                 : [object Object]
    GlobalObjects    : [object Object]
0:000:x86> dx @$curprocess.COM.GlobalObjects
@$curprocess.COM.GlobalObjects                 : [object Object]
    [0x100]          : 0x12f4fb0 [Type: IUnknown *]
    [0x201]          : 0x37cfc50 [Type: IUnknown *]
    [0x302]          : 0x37ea910 [Type: IUnknown *]
    [0x403]          : 0x37fcfe0 [Type: IUnknown *]
    [0x504]          : 0x12fe1d0 [Type: IUnknown *]
    [0x605]          : 0x59f04e8 [Type: IUnknown *]
    [0x706]          : 0x59f0eb8 [Type: IUnknown *]
    [0x807]          : 0x59f5550 [Type: IUnknown *]
    [0x908]          : 0x12fe340 [Type: IUnknown *]
    [0xa09]          : 0x5afcb58 [Type: IUnknown *]

Cette table est également accessible par programme via le cookie GIT.

0:000:x86> dx @$curprocess.COM.GlobalObjects[0xa09]
@$curprocess.COM.GlobalObjects[0xa09]                 : 0x5afcb58 [Type: IUnknown *]
    [+0x00c] __abi_reference_count [Type: __abi_FTMWeakRefData]
    [+0x014] __capture        [Type: Platform::Details::__abi_CapturePtr]

Extension des concepts d’objet débogueur avec LINQ

En plus de pouvoir étendre des objets tels que le processus et le thread, JavaScript peut également étendre les concepts associés au modèle de données. Par exemple, il est possible d’ajouter une nouvelle méthode LINQ à chaque itérable. Prenons un exemple d’extension, « DuplicateDataModel » qui duplique chaque entrée dans un N fois itérable. Le code suivant montre comment l’implémenter.

function initializeScript()
{
    var newLinqMethod =
    {
        Duplicate : function *(n)
        {
            for (var val of this)
            {
                for (var i = 0; i < n; ++i)
                {
                    yield val;
                }
            };
        }
    };

    return [new host.namedModelParent(newLinqMethod, "DataModel.Models.Concepts.Iterable")];
}

Enregistrez le code dans DuplicateDataModel.js à l’aide d’une application telle que le bloc-notes.

Chargez le fournisseur de scripts JavaScript si nécessaire, puis chargez l’extension DuplicateDataModel.js.

0:000:x86> !load jsprovider.dll
0:000:x86> .scriptload C:\JSExtensions\DuplicateDataModel.js
JavaScript script successfully loaded from 'C:\JSExtensions\DuplicateDataModel.js'

Utilisez la commande dx pour tester la nouvelle fonction Duplicate.

0: kd> dx -r1 Debugger.Sessions.First().Processes.First().Threads.Duplicate(2),d
Debugger.Sessions.First().Processes.First().Threads.Duplicate(2),d                 : [object Generator]
    [0]              : nt!DbgBreakPointWithStatus (fffff800`9696ca60) 
    [1]              : nt!DbgBreakPointWithStatus (fffff800`9696ca60) 
    [2]              : intelppm!MWaitIdle+0x18 (fffff805`0e351348) 
    [3]              : intelppm!MWaitIdle+0x18 (fffff805`0e351348) 
…

Voir aussi

Objets débogueur natifs dans les extensions JavaScript - Détails de l’objet débogueur

Objets débogueur natifs dans les extensions JavaScript - Considérations relatives à la conception et aux tests

Script du débogueur JavaScript

Exemples de scripts de débogueur JavaScript