Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Natywne obiekty debugera reprezentują różne konstrukcje i zachowania środowiska debugera. Obiekty można przekazać do rozszerzeń języka JavaScript (lub uzyskanych w nich), aby zarządzać stanem debugera.
Przykładowe obiekty debugera obejmują następujące elementy.
- Sesja
- Wątki/Wątek
- Procesy/proces
- Ramki stosu/ramka stosu
- Zmienne lokalne
- Moduły/moduł
- Użyteczność
- Państwo
- Ustawienia
Na przykład obiekt host.namespace.Debugger.Utility.Control.ExecuteCommand może służyć do wysyłania polecenia u do debugera z dwoma wierszami kodu JavaScript.
var ctl = host.namespace.Debugger.Utility.Control;
var outputLines = ctl.ExecuteCommand("u");
W tym temacie opisano sposób pracy z typowymi obiektami i zawiera informacje referencyjne dotyczące ich atrybutów i zachowań.
Aby uzyskać ogólne informacje na temat pracy z językiem JavaScript, zobacz JavaScript Debugger Scripting (Skrypty debugera języka JavaScript). Przykłady języka JavaScript korzystające z obiektów debugera można znaleźć w temacie JavaScript Debugger Example Scripts (Przykładowe skrypty debugera Języka JavaScript). Aby uzyskać informacje o pracy z obiektami ustawień, zobacz .settings (Ustawianie ustawień debugowania).
Aby eksplorować obiekty dostępne w sesji debugera, użyj polecenia dx (Display NatVis Expression). Na przykład można wyświetlić niektóre obiekty debugera najwyższego poziomu za pomocą tego polecenia 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
Wszystkie wymienione powyżej elementy są klikalne jako DML i można je zagłębiać dalej, w celu wyświetlenia struktury obiektu debugera.
Rozszerzanie debugera za pomocą modelu danych
Model danych debugera umożliwia utworzenie interfejsu w celu uzyskania informacji o aplikacjach i sterownikach w systemie Windows, które mają następujące atrybuty.
- Jest możliwe do odnalezienia i zorganizowane — logicznie ustrukturyzowaną przestrzeń nazw można przeszukiwać przy użyciu polecenia dx.
- Można wykonywać zapytania przy użyciu LINQ — umożliwia wyodrębnianie i sortowanie danych przy użyciu standardowego języka zapytań.
- Może być logicznie i spójnie rozszerzone — rozszerzalne przy użyciu technik opisanych w tym temacie z dostawcami skryptów debugera, takimi jak Natvis i JavaScript.
Rozszerzanie obiektu debugera w języku JavaScript
Oprócz możliwości tworzenia wizualizatora w języku JavaScript, skrypty rozszerzeń mają również możliwość modyfikowania podstawowych pojęć debugera — sesji, procesów, wątków, stosów, ramek stosu, zmiennych lokalnych — a nawet mogą udostępniać się jako punkty rozszerzenia, które mogą być wykorzystywane przez inne rozszerzenia.
W tej sekcji opisano sposób rozszerzania podstawowej koncepcji w debugerze. Rozszerzenia, które są tworzone do udostępniania, powinny być zgodne z wytycznymi przedstawionymi w temacie Native Debugger Objects in JavaScript Extensions ( Zagadnienia dotyczące projektowania i testowania).
Rejestrowanie rozszerzenia
Skrypt może zarejestrować fakt, że udostępnia rozszerzenie, poprzez wpis w tablicy, która jest zwracana przez metodę initializeScript.
function initializeScript()
{
return [new host.namedModelParent(comProcessExtension, "Debugger.Models.Process")];
}
Obecność obiektu host.namedModelParent w zwróconej tablicy wskazuje debugerowi, że dany obiekt prototypowy lub klasa ES6 (comProcessExtension w tym przypadku) będzie nadrzędnym modelem danych, który jest zarejestrowany pod nazwą Debugger.Models.Process.
Punkty rozszerzenia obiektu debugera
Następujące punkty rozszerzenia debugera są integralną częścią debugera i są dostępne dla dostawców skryptów, takich jak JavaScript.
Debugger.Models.Sessions: lista sesji (obiektów docelowych), do których jest dołączony debuger
Debugger.Models.Session: pojedyncza sesja (docelowy proces), do której jest dołączony debuger (interaktywny tryb użytkownika, KD itp.)
Debugger.Models.Processes: lista procesów w ramach sesji
Debugger.Models.Threads: lista wątków w procesie
Debugger.Models.Thread: pojedynczy wątek w procesie (niezależnie od tego, czy jest to tryb użytkownika, czy jądra)
Debugger.Models.Stack: stos wątku
Debugger.Models.StackFrames: kolekcja ramek tworzących stos
Debugger.Models.StackFrame: pojedyncza ramka stosu
Debugger.Models.LocalVariables: zmienne lokalne w ramce stosu
Debugger.Models.Parameters: parametry wywołania w ramce stosu
Debugger.Models.Module: pojedynczy moduł w przestrzeni adresowej procesu
Dodatkowe obiekty modelu danych
Ponadto istnieją dodatkowe obiekty modelu danych zdefiniowane przez podstawowy model danych.
DataModel.Models.Intrinsic: wartość intrynsyczna (ordynaly, liczby zmiennoprzecinkowe itp.)
DataModel.Models.String: ciąg
DataModel.Models.Array: macierz natywna
DataModel.Models.Guid: identyfikator GUID
DataModel.Models.Error: obiekt błędu
DataModel.Models.Concepts.Iterable: Zastosowano do każdego obiektu, który jest iterowalny
DataModel.Models.Concepts.StringDisplayable: zastosowano do każdego obiektu, który ma konwersję ciągu wyświetlania
Przykładowe rozszerzenie obiektu debugera COM — omówienie
Rozważmy przykład. Załóżmy, że chcesz utworzyć rozszerzenie debugera, aby wyświetlić informacje specyficzne dla modelu COM, takie jak tabela interfejsu globalnego (GIT).
W przeszłości mogło istnieć rozszerzenie debugera z wieloma poleceniami, które umożliwiały dostęp do aspektów modelu COM. Jedno polecenie może wyświetlać informacje skoncentrowane na procesie (na przykład globalna tabela interfejsu). Inne polecenie może dostarczać informacje skoncentrowane na wątkach, takie jak jaki kod apartamentu jest wykonywany. Aby zapoznać się z innymi aspektami modelu COM, może być konieczne zapoznanie się z drugim rozszerzeniem debugera i załadowanie go.
Zamiast posiadania zestawu trudnych do odkrycia poleceń, rozszerzenie JavaScript może zmodyfikować koncepcję debugera dotyczącą procesu i wątku, aby dodać te informacje w sposób naturalny, łatwy do eksploracji i komponowania z innymi rozszerzeniami debugera.
Rozszerzenie obiektu debugera trybu użytkownika lub trybu jądra
Debuger i obiekty debugera mają różne zachowanie w trybie użytkownika i jądra. Podczas tworzenia obiektów modelu debugera należy zdecydować, w których środowiskach będziesz pracować. Ponieważ będziemy pracować z com w trybie użytkownika, utworzymy i przetestujemy to rozszerzenie com w trybie użytkownika. W innych sytuacjach może być możliwe utworzenie debugera JavaScript, który będzie działać zarówno w debugowaniu w trybie użytkownika, jak i jądra.
Tworzenie przestrzeni nazw podrzędnych
Wracając do naszego przykładu, możemy zdefiniować prototyp lub klasę ES6 comProcessExtension zawierającą zestaw rzeczy, które chcemy dodać do obiektu procesu.
Ważny Intencją przestrzeni nazw podrzędnych jest utworzenie logicznie ustrukturyzowanego i naturalnie eksploratywnego paradygmatu. Na przykład unikaj wyrzucania niepowiązanych elementów do tej samej przestrzeni nazw podrzędnej. Przed utworzeniem przestrzeni nazw podrzędnej dokładnie przejrzyj informacje omówione w temacie Native Debugger Objects in JavaScript Extensions (Rozszerzenia języka JavaScript — zagadnienia dotyczące projektowania i testowania ).
W tym fragmencie kodu utworzymy przestrzeń nazw podrzędnych o nazwie "COM" do istniejącego obiektu debugera procesu.
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);
}
}
Implementacja przestrzeni nazw
Następnie utwórz obiekt, który implementuje podnazwę COM w procesie.
Ważny Może istnieć wiele procesów (niezależnie od tego, czy są dołączone do nich w trybie użytkownika, czy w obszarze KD). To rozszerzenie nie może zakładać, że obecny stan debugera jest tym, co użytkownik zamierzał. Ktoś może przechwycić <someProcess>.COM w zmiennej i ją zmodyfikować, co może prowadzić do prezentowania informacji z nieprawidłowego kontekstu procesu. Rozwiązaniem jest dodanie kodu w rozszerzeniu, dzięki czemu każde wystąpienie będzie śledzić proces, do którego jest dołączony. W tym przykładzie kodu informacje te są przekazywane za pośrednictwem wskaźnika "this" właściwości.
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 implementacji globalnej tablicy interfejsu COM
Aby oddzielić tę logikę implementacji dla tabeli interfejsu globalnego COM bardziej wyraźnie, zdefiniujemy jedną klasę ES6, gipTable , która oddziela tabelę COM GIP i inną, globalObjects, która będzie zwracana z metody getter GlobalObjects() zdefiniowanej w powyższym fragmencie kodu implementacji przestrzeni nazw. Wszystkie te szczegóły mogą być ukryte w zamknięciu funkcji initializeScript, aby uniknąć publikowania tych wewnętrznych szczegółów w przestrzeni nazw debugera.
// 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;
}
}
Na koniec użyj host.namedModelRegistration, aby zarejestrować nową funkcjonalność COM.
function initializeScript()
{
return [new host.namedModelParent(comProcessExtension, "Debugger.Models.Process"),
new host.namedModelRegistration(comNamespace, "Debugger.Models.ComProcess")];
}
Zapisz kod do GipTableAbstractor.js przy użyciu aplikacji, na przykład Notatnik.
Poniżej przedstawiono informacje o procesie dostępne w trybie użytkownika przed załadowaniem tego rozszerzenia.
0:000:x86> dx @$curprocess
@$curprocess : DataBinding.exe
Name : DataBinding.exe
Id : 0x1b9c
Threads
Modules
Załaduj rozszerzenie JavaScript.
0:000:x86> .scriptload C:\JSExtensions\GipTableAbstractor.js
JavaScript script successfully loaded from 'C:\JSExtensions\GipTableAbstractor.js'
Następnie użyj polecenia dx, aby wyświetlić informacje o procesie przy użyciu wstępnie zdefiniowanego @$curprocess.
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 *]
Ta tabela jest również programowo dostępna za pośrednictwem pliku 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]
Rozszerzanie pojęć dotyczących obiektów debugera za pomocą LINQ
Oprócz możliwości rozszerzania obiektów, takich jak proces i wątek, język JavaScript może również rozszerzać koncepcje skojarzone z modelem danych. Na przykład można dodać nową metodę LINQ do każdej iteracji. Rozważmy przykładowe rozszerzenie "DuplicateDataModel", które powiela każdy element w iterowalnym obiekcie N razy. Poniższy kod pokazuje, jak można to zaimplementować.
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")];
}
Zapisz kod do DuplicateDataModel.js przy użyciu aplikacji takiej jak Notatnik.
W razie potrzeby załaduj dostawcę skryptów JavaScript, a następnie załaduj rozszerzenie 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'
Użyj polecenia dx, aby przetestować nową funkcję 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)
…
Zobacz także
Natywne obiekty debugera w rozszerzeniach Języka JavaScript — szczegóły obiektu debugera