Bagikan melalui


Objek Debugger Asli di Ekstensi JavaScript

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

Contoh objek debugger termasuk yang berikut ini.

  • Sesi
  • Utas / Utas
  • Proses/ Proses
  • Bingkai Tumpukan / Bingkai Tumpukan
  • Variabel Lokal
  • Modul / Modul
  • Utilitas
  • Provinsi
  • 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 memberikan informasi referensi tentang atribut dan perilakunya.

Untuk informasi umum tentang bekerja dengan JavaScript, lihat Scripting 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 di JavaScript, ekstensi skrip juga dapat memodifikasi konsep inti debugger - sesi, proses, utas, tumpukan, bingkai tumpukan, variabel lokal - dan bahkan menerbitkan diri mereka sebagai titik ekstensi yang dapat dikonsumsi 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 adalah integral dari debugger dan tersedia untuk digunakan oleh penyedia skrip seperti JavaScript.

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

Debugger.Models.Session: Sesi individual (target) yang dilampirkan debugger (mode pengguna langsung, KD, dll...)

Debugger.Models.Processes: Daftar proses dalam sesi

Debugger.Models.Threads: Daftar utas dalam proses

Debugger.Models.Thread: Utas individual dalam proses (terlepas dari apakah pengguna atau mode kernel)

Debugger.Models.Stack: Tumpukan utas

Debugger.Models.StackFrames: Kumpulan bingkai yang membentuk tumpukan

Debugger.Models.StackFrame: Bingkai tumpukan individual dalam tumpukan

Debugger.Models.LocalVariables: Variabel lokal dalam bingkai tumpukan

Debugger.Models.Parameters: Parameter untuk panggilan 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: 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 diperulang

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 proses dan utas, untuk menambahkan informasi ini dengan cara yang alami, dapat dieksplorasi, dan dapat dikomposisikan dengan ekstensi debugger lainnya.

Ekstensi Objek Debugger 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 JavaScript debugger yang akan berfungsi dalam penelusuran kesalahan mode pengguna dan kernel.

Membuat Sub-Namespace

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

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

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 Mungkin ada beberapa proses (apakah dilampirkan ke seperti itu dalam mode pengguna atau di bawah KD). Ekstensi ini tidak dapat mengasumsikan bahwa status debugger saat ini adalah apa yang dimaksudkan pengguna. Seseorang dapat mengambil <beberapaProcess.COM> dalam variabel dan memodifikasinya, yang dapat menyebabkan penyajian informasi dari konteks proses yang salah. Solusinya adalah menambahkan kode dalam ekstensi sehingga setiap instansiasi akan melacak proses apa yang dilampirkan. Untuk sampel kode ini, informasi ini diteruskan melalui penunjuk properti 'ini'.

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 ini logika implementasi untuk tabel antarmuka global COM dengan lebih jelas, kita akan menentukan satu kelas ES6, gipTable yang mengabstraksi tabel COM GIP dan yang lain, globalObjects, yang akan dikembalikan dari getter GlobalObjects() yang ditentukan dalam cuplikan kode Implementasi Namespace yang ditunjukkan di atas. Semua detail ini dapat disembunyikan di dalam penutupan initializeScript untuk menghindari penerbitan salah satu detail internal ini ke dalam namespace 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 untuk 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 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 untuk 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

Pembuatan Skrip JavaScript Debugger

Contoh Skrip JavaScript Debugger