Bagikan melalui


Objek Debugger Asli di Ekstensi JavaScript

Objek debugger asli mewakili berbagai konstruksi dan perilaku lingkungan debugger. Objek JavaScript dapat diteruskan ke ekstensi JavaScript (atau diperoleh dalam ekstensi) untuk memanipulasi status debugger.

Contoh objek debugger mencakup yang berikut ini.

  • Sesi
  • Utas / Utas
  • Proses / Proses
  • Bingkai Tumpukan
  • Variabel Lokal
  • Modul / Modul
  • Utilitas
  • Negara
  • Pengaturan

Misalnya objek host.namespace.Debugger.Utility.Control.ExecuteCommand dapat digunakan untuk mengirim perintah u ke debugger dengan dua baris kode JavaScript berikut.

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

Topik ini menjelaskan cara bekerja dengan objek umum dan menyediakan informasi referensi tentang atribut dan perilaku mereka.

Untuk informasi umum tentang bekerja dengan JavaScript, lihat Pembuatan Skrip JavaScript Debugger. Untuk contoh JavaScript yang menggunakan objek debugger, lihat Contoh Skrip JavaScript Debugger. Untuk informasi tentang bekerja dengan objek pengaturan, lihat .settings (Atur Pengaturan Debug).

Untuk menjelajahi objek yang tersedia dalam sesi debugger, gunakan perintah dx (Tampilkan Ekspresi NatVis). Misalnya, Anda dapat menampilkan beberapa objek debugger tingkat atas dengan perintah dx ini.

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   

Semua item yang tercantum di atas adalah DML yang dapat diklik dan dapat diulang lebih jauh ke bawah untuk melihat struktur objek debugger.

Memperluas Debugger melalui Model Data

Model data debugger memungkinkan pembuatan antarmuka ke informasi tentang aplikasi dan driver di Windows yang memiliki atribut berikut.

  • Dapat ditemukan dan diatur- ruang nama yang terstruktur secara logis dapat dikueri menggunakan perintah dx.
  • Dapat dikueri menggunakan LINQ- Ini memungkinkan ekstraksi dan pengurutan data menggunakan bahasa kueri standar.
  • Dapat diperluas secara logis dan konsisten - Dapat diperluas menggunakan teknik yang dijelaskan dalam topik ini dengan penyedia skrip debugger seperti Natvis dan JavaScript.

Memperluas Objek Debugger di JavaScript

Selain dapat membuat visualizer dalam JavaScript, ekstensi skrip juga dapat mengubah konsep inti dari debugger - sesi, proses, utas, tumpukan, bingkai tumpukan, variabel lokal - dan bahkan memublikasikan diri mereka sebagai titik ekstensi yang dapat dimanfaatkan oleh ekstensi lain.

Bagian ini menjelaskan cara memperluas konsep inti dalam debugger. Ekstensi yang dibuat untuk dibagikan harus sesuai dengan pedoman yang disajikan dalam Objek Debugger Asli di Ekstensi JavaScript - Pertimbangan Desain dan Pengujian.

Mendaftarkan Ekstensi

Skrip dapat mendaftarkan fakta bahwa skrip menyediakan ekstensi melalui entri dalam array yang dikembalikan dari metode initializeScript.

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

Kehadiran objek host.namedModelParent dalam array yang dikembalikan menunjukkan kepada debugger bahwa objek prototipe tertentu atau kelas ES6 (comProcessExtension dalam hal ini) akan menjadi model data induk ke model yang terdaftar dengan nama Debugger.Models.Process.

Titik Ekstensi Objek Debugger

Titik ekstensi debugger berikut terintegrasi dengan debugger dan tersedia untuk digunakan oleh penyedia skrip seperti JavaScript.

Debugger.Models.Sessions: Daftar sesi (target) tempat debugger dilampirkan

Debugger.Models.Session: Sesi individu (target) yang terpasang debugger (mode pengguna langsung aktif, KD, dll...)

Debugger.Models.Processes: Daftar proses dalam sesi

Debugger.Models.Threads: Daftar utas dalam proses

Debugger.Models.Thread: Sebuah thread individu dalam proses (terlepas dari apakah dalam mode pengguna atau mode kernel)

Debugger.Models.Stack: Tumpukan utas

Debugger.Models.StackFrames: Kumpulan bingkai yang membentuk tumpukan

Debugger.Models.StackFrame: Bingkai tumpukan individu dalam tumpukan

Debugger.Models.LocalVariables: Variabel lokal dalam bingkai tumpukan

Debugger.Models.Parameters: Parameter-parameter untuk pemanggilan dalam bingkai tumpukan

Debugger.Models.Module: Modul individual dalam ruang alamat proses

Objek Model Data Tambahan

Selain itu, ada beberapa objek model data tambahan yang ditentukan oleh model data inti.

DataModel.Models.Intrinsic: Nilai intrinsik (ordinal, float, dll...)

DataModel.Models.String: Sebuah string

DataModel.Models.Array: Array asli

DataModel.Models.Guid: GUID

DataModel.Models.Error: Objek kesalahan

DataModel.Models.Concepts.Iterable: Diterapkan ke setiap objek yang dapat diulang

DataModel.Models.Concepts.StringDisplayable: Diterapkan ke setiap objek yang memiliki konversi string tampilan

Contoh Gambaran Umum Ekstensi Objek Debugger COM

Mari lihat contoh berikut. Bayangkan Anda ingin membuat ekstensi debugger untuk menampilkan informasi khusus untuk COM, seperti tabel antarmuka global (GIT).

Di masa lalu, mungkin ada ekstensi debugger yang ada dengan sejumlah perintah yang menyediakan sarana untuk mengakses hal-hal tentang COM. Satu perintah mungkin menampilkan informasi sentris proses (tabel antarmuka global misalnya). Perintah lain mungkin memberikan informasi sentris utas seperti kode apartemen apa yang dijalankan di dalamnya. Anda mungkin perlu mengetahui dan memuat ekstensi debugger kedua untuk menjelajahi aspek COM lainnya.

Alih-alih memiliki serangkaian perintah yang sulit ditemukan, ekstensi JavaScript dapat memodifikasi konsep debugger tentang apa proses dan utasnya, untuk menambahkan informasi ini dengan cara yang alami, dapat dieksplorasi, dan dapat dikomposisikan dengan ekstensi debugger lainnya.

Ekstensi Objek Debugger untuk Mode Pengguna atau Kernel

Debugger dan objek debugger memiliki perilaku yang berbeda dalam mode pengguna dan kernel. Saat membuat objek model debugger, Anda perlu memutuskan lingkungan mana yang akan Anda kerjakan. Karena kita akan bekerja dengan COM dalam mode pengguna, kita akan membuat dan menguji ekstensi com ini dalam mode pengguna. Dalam situasi lain, Anda mungkin dapat membuat debugger JavaScript yang akan berfungsi dalam debugging mode pengguna maupun mode kernel.

Membuat Sub-Namespace

Kembali ke contoh kami, kita dapat menentukan prototipe atau kelas ES6, comProcessExtension yang berisi serangkaian hal yang ingin kita tambahkan ke objek proses.

Penting Niat dengan sub-namespace adalah untuk membuat paradigma yang terstruktur dan dapat dieksplorasi secara logis. Misalnya, hindari membuang item yang tidak terkait ke sub-namespace yang sama. Tinjau dengan cermat informasi yang dibahas dalam Objek Debugger Native di Ekstensi JavaScript - Pertimbangan Desain dan Pengujian sebelum membuat sub-namespace.

Dalam cuplikan kode ini, kami membuat tambahkan sub-namespace yang disebut 'COM' ke objek debugger proses yang ada.

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);
    }
}

Implementasi Namespace

Selanjutnya, buat objek yang mengimplementasikan COM sub-namespace pada proses.

Penting Terdapat kemungkinan bahwa ada beberapa proses, baik yang terhubung pada mode pengguna atau di bawah KD. Ekstensi ini tidak dapat mengasumsikan bahwa status debugger saat ini adalah apa yang dimaksudkan pengguna. Seseorang dapat mengambil <someProcess>.COM dalam variabel dan memodifikasinya, yang dapat menyebabkan menyajikan informasi dari konteks proses yang salah. Solusinya adalah menambahkan kode dalam ekstensi sehingga setiap instansiasi akan memantau proses yang terhubung dengannya. Untuk sampel kode ini, informasi ini diteruskan melalui penunjuk 'this' dari properti.

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);
    }
}

Logika implementasi untuk tabel antarmuka global COM

Untuk memisahkan logika implementasi ini untuk tabel antarmuka global COM dengan lebih jelas, kita akan menentukan satu kelas ES6, gipTable yang mengabstraksi tabel GIP COM dan kelas lainnya, globalObjects, yang akan dikembalikan dari getter GlobalObjects() seperti yang didefinisikan dalam cuplikan kode Implementasi Namespace yang sebelumnya ditunjukkan. Semua detail ini dapat disembunyikan di dalam penutupan initializeScript untuk menghindari penerbitan salah satu detail internal ini ke dalam namespace layanan debugger.

// 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;
    }
}

Terakhir, gunakan host.namedModelRegistration untuk mendaftarkan fungsionalitas COM baru.

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

Simpan kode ke GipTableAbstractor.js menggunakan aplikasi seperti notepad.

Berikut adalah informasi proses yang tersedia dalam mode pengguna sebelum memuat ekstensi ini.

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

Muat ekstensi JavaScript.

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

Kemudian gunakan perintah dx untuk menampilkan informasi tentang proses menggunakan @$curprocess yang telah ditentukan sebelumnya.

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 *]

Tabel ini juga dapat diakses secara terprogram melalui 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]

Memperluas Konsep Objek Debugger dengan LINQ

Selain dapat memperluas objek seperti proses dan utas, JavaScript juga dapat memperluas konsep yang terkait dengan model data. Misalnya, dimungkinkan untuk menambahkan metode LINQ baru ke setiap iterable. Pertimbangkan contoh ekstensi, "DuplicateDataModel" yang menduplikasi setiap entri dalam waktu N yang dapat diulang. Kode berikut menunjukkan bagaimana hal ini dapat diimplementasikan.

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")];
}

Simpan kode ke DuplicateDataModel.js menggunakan aplikasi seperti notepad.

Muat penyedia skrip JavaScript jika perlu lalu muat ekstensi 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'

Gunakan perintah dx untuk menguji fungsi Duplikat baru.

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) 
…

Lihat juga

Objek Debugger Asli di Ekstensi JavaScript - Detail Objek Debugger

Objek Debugger Asli di Ekstensi JavaScript - Pertimbangan Desain dan Pengujian

Pemrograman Debugger JavaScript

Contoh Skrip JavaScript Debugger