Partager via


Objets du débogueur natif dans les extensions JavaScript

Les objets débogueur natifs représentent différentes constructions et comportements de l’environnement du débogueur. Les objets peuvent être transmis à des extensions JavaScript (ou acquis dans celles-ci) pour manipuler l’état du débogueur.

Les exemples d’objets débogueur incluent les éléments suivants.

  • session
  • Threads / Thread
  • Processus / Processus
  • Cadres de pile / Cadre de pile
  • Variables locales
  • Modules / Module
  • Utilité
  • État
  • Paramètres

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

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

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

Pour obtenir des informations générales sur l’utilisation de JavaScript, consultez Débogueur de scripts JavaScript. Pour obtenir des exemples JavaScript qui utilisent les objets débogueur, consultez les 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 (Display NatVis Expression). Par exemple, vous pouvez afficher certains 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 dans la DML et peuvent être explorés davantage pour afficher la structure de l'objet du 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 pour obtenir 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 de données à l’aide d’un langage de requête standard.
  • Peut être étendu de manière logique et cohérente : extensible à l’aide de 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 fondamentaux 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 utiliser.

Cette section explique comment étendre un concept principal au sein du débogueur. Les extensions qui sont générées à partager doivent être conformes aux instructions présentées dans les objets débogueur natifs dans les extensions JavaScript - Considérations relatives à la conception et aux tests.

Inscription d’une extension

Un script peut inscrire le fait qu’il fournit une extension via une entrée dans le tableau retourné 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 donné ou une classe ES6 (comProcessExtension dans ce cas) sera un modèle de données parent au modèle inscrit sous le nom Debugger.Models.Process.

Points d’extension d’objet du débogueur

Les points d’extension du débogueur suivants sont intégraux au débogueur et disponibles pour être utilisés par les fournisseurs de script tels que JavaScript.

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

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

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

Debugger.Models.Threads : liste de 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 : la collection de frames qui composent une pile

Debugger.Models.StackFrame : trame de pile individuelle dans une pile

Debugger.Models.LocalVariables : les variables locales dans un cadre de pile

Debugger.Models.Parameters : paramètres d’un appel dans une trame 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 des objets de modèle de données supplémentaires définis par le modèle de données de base.

DataModel.Models.Intrinsic : une valeur intrinsèque (ordinaux, flottants, etc...).

DataModel.Models.String : chaîne

DataModel.Models.Array : tableau natif

DataModel.Models.Guid : un 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 d’extension d’objet du 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 aurait pu y avoir une extension de débogueur existante avec un certain nombre de commandes qui fournissent un moyen d’accéder à des aspects liés à COM. Une commande peut afficher des informations centrées sur le processus (la table d’interface globale par exemple). Une autre commande peut fournir des informations centrées sur les threads, telles que le code d’appartement en cours d’exécution. 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, pour ajouter ces informations d’une manière naturelle, explorable et composable avec d’autres extensions de débogueur.

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

Le débogueur et les objets de débogueur ont un comportement différent en mode utilisateur et noyau. Lorsque vous créez vos objets de modèle de débogueur, vous devez déterminer les environnements dans lesquels vous allez travailler. Étant donné que nous allons travailler avec 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 à la fois en mode utilisateur et en mode noyau.

Création d’un sous-espace de noms

Retour à 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 l’espace de noms secondaire est de créer un paradigme logiquement structuré et naturellement explorable. Par exemple, évitez de placer des éléments non liés dans le même sous-espace de noms. Examinez attentivement les informations présentées dans les objets débogueur natifs dans les extensions JavaScript - Considérations relatives à la conception et au test 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 à ce type 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. Une personne peut capturer <someProcess>.COM dans une variable et la modifier, ce qui peut entraîner la présentation d’informations à partir du contexte de processus incorrect. La solution consiste à ajouter du code dans l’extension afin que chaque instanciation effectue le suivi du processus auquel il est attaché. 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 cette logique d’implémentation de la table d’interface globale COM plus clairement, nous allons définir une classe ES6, gipTable qui extrait la table GIP COM et une autre, globalObjects, qui est ce qui sera retourné par le getter GlobalObjects() défini dans le code d’implémentation de l’espace de noms indiqué ci-dessus. Tous ces détails peuvent être encapsulés à l’intérieur de la clôture d'`initializeScript` afin d'é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 de charger 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'

Utilisez ensuite 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 programmation 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 du 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 nombre N itérable. Le code suivant montre comment cela peut être implémenté.

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 script 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 dupliquée.

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 au test

Scriptage du débogueur JavaScript

Exemples de scripts du débogueur JavaScript