Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Os objetos nativos do depurador representam várias construções e comportamentos do ambiente do depurador. Os objetos podem ser passados para (ou adquiridos em) extensões JavaScript para manipular o estado do depurador.
Exemplos de objetos do depurador são os seguintes.
- Sessão
- Tópicos / Thread
- Processos / Processo
- Quadros de pilha / Quadro de pilha
- Variáveis locais
- Módulos / Módulo
- Utilidade
- Estado
- Configurações
Por exemplo, o objeto host.namespace.Debugger.Utility.Control.ExecuteCommand pode ser usado para enviar o comando u para o depurador com as seguintes duas linhas de código JavaScript.
var ctl = host.namespace.Debugger.Utility.Control;
var outputLines = ctl.ExecuteCommand("u");
Este tópico descreve como trabalhar com objetos comuns e fornece informações de referência sobre seus atributos e comportamentos.
Para obter informações gerais sobre como trabalhar com JavaScript, consulte JavaScript Debugger Scripting. Para obter exemplos de JavaScript que usam os objetos do depurador, consulte Scripts de exemplo do depurador JavaScript. Para obter informações sobre como trabalhar com os objetos settings, consulte .settings (set Debug Settings).
Para explorar os objetos disponíveis em uma sessão de depurador, use o comando dx (Display NatVis Expression ). Por exemplo, você pode exibir alguns dos objetos do depurador de nível superior com este comando 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
Todos os itens listados acima são DML clicáveis e podem ser recursados mais abaixo para visualizar a estrutura do objeto do depurador.
Estendendo o depurador por meio do modelo de dados
O modelo de dados do depurador permite a criação de uma interface para informações sobre aplicativos e drivers no Windows que tem os seguintes atributos.
- É detetável e organizado - um espaço de nome logicamente estruturado pode ser consultado usando o comando dx.
- Pode ser consultado usando LINQ- Isso permite a extração e classificação de dados usando uma linguagem de consulta padrão.
- Pode ser estendido de forma lógica e consistente - Extensível usando técnicas descritas neste tópico com provedores de scripts para depuração, como o Natvis e o JavaScript.
Estendendo um objeto depurador em JavaScript
Além de poder criar um visualizador em JavaScript, as extensões de script também podem modificar os principais conceitos do depurador - sessões, processos, threads, pilhas, quadros de pilha, variáveis locais - e até mesmo publicar-se como pontos de extensão que outras extensões podem consumir.
Esta secção descreve como expandir um conceito fundamental no depurador. As extensões que são construídas para serem compartilhadas devem estar em conformidade com as diretrizes apresentadas em Native Debugger Objects in JavaScript Extensions - Design and Testing Considerations.
Registrando uma extensão
Um script pode registrar o fato de que ele fornece uma extensão através de uma entrada na matriz retornada do método initializeScript.
function initializeScript()
{
return [new host.namedModelParent(comProcessExtension, "Debugger.Models.Process")];
}
A presença de um objeto host.namedModelParent dentro da matriz retornada indica ao depurador que um determinado objeto protótipo ou classe ES6 (comProcessExtension neste caso) será um modelo de dados pai para o modelo registrado sob o nome Debugger.Models.Process.
Pontos de extensão de objeto do depurador
Os seguintes pontos de extensão do depurador são parte integrante do depurador e estão disponíveis para serem usados por provedores de script, como JavaScript.
Debugger.Models.Sessions: A lista de sessões (destinos) às quais o depurador está ligado
Debugger.Models.Session: Uma sessão individual (destino) à qual o depurador está conectado (modo de utilizador em tempo real, KD, etc...)
Debugger.Models.Processes: A lista de processos dentro de uma sessão
Debugger.Models.Threads: A lista de threads dentro de um processo
Debugger.Models.Thread: Um thread individual dentro de um processo (independentemente do modo de usuário ou kernel)
Debugger.Models.Stack: A pilha de um thread
Debugger.Models.StackFrames: A coleção de frames que compõem uma pilha.
Debugger.Models.StackFrame: Um quadro de pilha individual dentro de uma pilha
Debugger.Models.LocalVariables: As variáveis locais dentro de um frame de pilha
Debugger.Models.Parameters: Os parâmetros para uma chamada dentro de um frame de pilha
Debugger.Models.Module: Um módulo individual dentro do espaço de endereço de um processo
Objetos de modelo de dados adicionais
Além disso, existem alguns objetos de modelo de dados adicionais que são definidos pelo modelo de dados principal.
DataModel.Models.Intrinsic: Um valor intrínseco (ordinais, floats, etc...)
DataModel.Models.String: Uma cadeia de caracteres
DataModel.Models.Array: Uma matriz nativa
DataModel.Models.Guid: Um GUID (Identificador Único Global)
DataModel.Models.Error: Um objeto de erro
DataModel.Models.Concepts.Iterable: Aplicado a todos os objetos que são iteráveis
DataModel.Models.Concepts.StringDisplayable: Aplicado a todos os objetos que têm uma conversão de cadeia de caracteres de exibição
Exemplo de visão geral da extensão de objeto do depurador COM
Vamos considerar um exemplo. Imagine que você deseja criar uma extensão do depurador para exibir informações específicas do COM, como a tabela de interface global (GIT).
No passado, poderia haver uma extensão de depurador existente com vários comandos que fornecem um meio de acessar informações sobre COM. Um comando pode exibir informações centradas no processo (a tabela de interface global, por exemplo). Outro comando pode fornecer informações centradas em threads, como qual código de apartamento está sendo executado. Talvez seja necessário conhecer e carregar uma segunda extensão do debugger para explorar outros aspetos do COM.
Em vez de ter um conjunto de comandos difíceis de descobrir, uma extensão JavaScript pode modificar o conceito do depurador do que é um processo e um thread, para adicionar essas informações de uma forma natural, explorável e compostável com outras extensões do depurador.
Extensão de objeto do depurador em modo de utilizador ou de kernel
O depurador e os objetos do depurador têm comportamento diferente no modo de usuário e kernel. Ao criar seus objetos de modelo do depurador, você precisa decidir em quais ambientes você trabalhará. Como trabalharemos com COM no modo de usuário, criaremos e testaremos esta extensão com no modo de usuário. Em outras situações, poderá criar um script de depuração JavaScript que funcionará na depuração do modo de utilizador e do modo kernel.
Criando um subnamespace
Voltando ao nosso exemplo, podemos definir um protótipo ou classe ES6, comProcessExtension , que contém o conjunto de coisas que queremos adicionar a um objeto de processo.
Importante A intenção com o sub-namespace é criar um paradigma logicamente estruturado e naturalmente explorável. Por exemplo, evite despejar itens não relacionados no mesmo subnamespace. Analise cuidadosamente as informações discutidas em Native Debugger Objects in JavaScript Extensions - Design and Testing Considerations antes de criar um subnamespace.
Neste trecho de código, criamos adicionar um subnamespace chamado 'COM' ao objeto depurador de processo existente.
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);
}
}
Implementação de namespace
Em seguida, crie o objeto que implementa o COM do subnamespace em um processo.
Importante Pode haver vários processos (se anexados a tais no modo de usuário ou sob KD). Esta extensão não pode assumir que o estado atual do depurador é o que o usuário pretendia. Alguém pode capturar <someProcess>.COM em uma variável e modificá-la, o que pode levar à apresentação de informações do contexto errado do processo. A solução é adicionar código na extensão para que cada instanciação acompanhe o processo ao qual está anexada. Para este exemplo de código, essas informações são passadas através do ponteiro 'this' da propriedade.
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);
}
}
Lógica de implementação para a tabela de interface global COM
Para separar mais claramente a lógica de implementação da tabela de interface global COM, definiremos uma classe ES6, gipTable, que abstrai a tabela GIP COM e outra, globalObjects, que será a que será retornada pelo getter GlobalObjects() definido no trecho de código da Implementação de Namespace. Todos esses detalhes podem ser ocultos dentro do fechamento de initializeScript para evitar a publicação de qualquer um desses detalhes internos no namespace do depurador.
// 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;
}
}
Por fim, use host.namedModelRegistration para registrar a nova funcionalidade COM.
function initializeScript()
{
return [new host.namedModelParent(comProcessExtension, "Debugger.Models.Process"),
new host.namedModelRegistration(comNamespace, "Debugger.Models.ComProcess")];
}
Salve o código para GipTableAbstractor.js usando um aplicativo como o bloco de notas.
Aqui estão as informações do processo disponíveis no modo de usuário antes de carregar esta extensão.
0:000:x86> dx @$curprocess
@$curprocess : DataBinding.exe
Name : DataBinding.exe
Id : 0x1b9c
Threads
Modules
Carregue a extensão JavaScript.
0:000:x86> .scriptload C:\JSExtensions\GipTableAbstractor.js
JavaScript script successfully loaded from 'C:\JSExtensions\GipTableAbstractor.js'
Em seguida, use o comando dx para exibir informações sobre o processo usando o @$curprocess predefinido.
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 *]
Esta tabela também é acessível programaticamente através do 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]
Alargando os conceitos de objetos do depurador com o LINQ
Além de ser capaz de estender objetos como processo e thread, o JavaScript também pode estender conceitos associados ao modelo de dados. Por exemplo, é possível adicionar um novo método LINQ a cada iterável. Considere uma extensão de exemplo, "DuplicateDataModel", que duplica cada entrada em um N vezes iterável. O código a seguir mostra como isso pode ser implementado.
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")];
}
Salve o código para DuplicateDataModel.js usando um aplicativo como o bloco de notas.
Carregue o provedor de scripts JavaScript, se necessário, e carregue a extensão 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'
Use o comando dx para testar a nova função 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)
…
Ver também
Objetos do depurador nativo em extensões JavaScript - Detalhes do objeto do depurador
Objetos de depurador nativos em extensões JavaScript - Considerações de design e teste
Depuração com Script em JavaScript
Scripts de exemplo do depurador JavaScript