Compartilhar via


Usando LINQ com os objetos do depurador

A sintaxe LINQ pode ser usada com os objetos do depurador para pesquisar e manipular dados. O uso da sintaxe LINQ com o comando dx permite uma experiência mais consistente em comparação com o uso de comandos do depurador. A saída e as opções são consistentes, independentemente do objeto de depurador que você está examinando. As consultas LINQ permitem que você faça perguntas como "Quais são os cinco principais processos que estão executando a maioria dos threads?".

Os objetos do depurador são projetados em um namespace com raiz em "Depurador". Processos, módulos, threads, pilhas, quadros de pilha e variáveis locais estão todos disponíveis para serem usados em uma consulta LINQ.

O LINQ é conceitualmente semelhante ao linguagem SQL (SQL) usado para consultar bancos de dados. Você pode usar vários métodos LINQ para pesquisar, filtrar e analisar dados de depuração. A sintaxe do método LINQ C# é usada. Para obter mais informações sobre LINQ e a sintaxe LINQ C#, consulte Introdução com LINQ em C#

O LINQ usado no suporte ao depurador usa a "sintaxe de método" do LINQ e não a "sintaxe de consulta". Você pode encontrar mais detalhes sobre as diferenças no LINQ (Consulta Integrada à Linguagem).

Comandos LINQ, como os seguintes, podem ser usados com os objetos do depurador. Todos. Qualquer. Contar. Primeiro. Achatar. Groupby. Última. Orderby. Orderbydescending. Selecione e . Onde. Esses métodos seguem (o mais próximo possível) o formulário de método LINQ do C#.

Objetos nativos do depurador

Objetos de depurador nativos representam vários constructos e comportamentos do ambiente do depurador. Os objetos de depurador de exemplo incluem o seguinte.

  • Session
  • Threads/Thread
  • Processos/Processo
  • Stack Frames / Stack Frame
  • Variáveis locais
  • Módulos/Módulo
  • Utilitário
  • Estado
  • Configurações

Você também pode trabalhar com os objetos do depurador com NatVis. Para obter mais informações, consulte Objetos nativos do depurador no NatVis. Para obter informações sobre como usar objetos de depurador com JavaScript, consulte Native Debugger Objects in JavaScript Extensions. Para obter informações sobre como trabalhar com O C++ e os objetos de driver, consulte Visão geral do modelo de dados do depurador C++.

Comando Dx

Os exemplos mostrados aqui usam o comando dx, para obter mais informações sobre como trabalhar com o comando dx, consulte dx (Exibir expressão de modelo de objeto do depurador).

Desenvolvendo uma consulta LINQ

Uma maneira de desenvolver uma consulta de objeto de depurador LINQ é usar os links DML exibidos para explorar o modelo de dados para primeiro localizar o objeto de depurador que será usado na consulta.

Para este exemplo, gostaríamos de exibir uma lista de processos em uma sessão de depuração de kernel e o número de threads para cada um desses processos.

Para iniciar nossa exploração, podemos usar o comando dx para exibir o objeto de depurador de nível superior.

0: kd> dx Debugger
Debugger
    Sessions
    Settings
    State
    Utility

Depois de selecionar os tópicos de nível superior, determinamos que Sessions parece mais interessante, portanto, selecionamos o link DML para revelar que ele contém Processos.

0: kd> dx -r1 Debugger.Sessions[0]
Debugger.Sessions[0]                 : Remote KD: KdSrv:Server=@{<Local>},Trans=@{NET:Port=50005,Key=MyKey}
    Processes
    Id               : 0
    Attributes

Em seguida, selecionamos mais para baixo para examinar um processo específico e vemos que os Threads associados a esse processo estão disponíveis. Quando selecionamos Threads para um dos processos, vemos que todos os threads associados a esse processo estão disponíveis.

0: kd> dx -r1 Debugger.Sessions[0].Processes[1428].Threads
Debugger.Sessions[0].Processes[1428].Threads
    [0x598]          : <Unable to get stack trace> [Switch To]
    [0x1220]         : <Unable to get stack trace> [Switch To]
    [0x6f8]          : nt!KiSwapContext+0x76 (fffff806`4466a186)  [Switch To]
    [0x128c]         : <Unable to get stack trace> [Switch To]
    [0x27e4]         : nt!KiSwapContext+0x76 (fffff806`4466a186)  [Switch To] 

Agora sabemos que os dados que precisamos para exibir o número de threads associados a um processo estão disponíveis no modelo de objeto do depurador.

Para tornar a consulta LINQ um pouco mais curta, podemos usar as Variáveis Definidas pelo Sistema descritas posteriormente neste tópico para exibir os processos associados à sessão atual.

0: kd> dx @$cursession.Processes
@$cursession.Processes                
    [0x0]            : Idle [Switch To]
    [0x4]            : System [Switch To]
    [0x90]           : Registry [Switch To]
...

Em seguida, adicione uma instrução select. Para começar, podemos especificar o campo Nome.

0: kd> dx @$cursession.Processes.Select(p => p.Name)
@$cursession.Processes.Select(p => p.Name)                
    [0x0]            : Idle
    [0x4]            : System
    [0x90]           : Registry
...

Para nosso cenário, também precisamos do número de threads. Como há dois campos, crie um tipo anônimo usando novo, semelhante à sintaxe de tipo anônimo do C#descrita abaixo em Variáveis Definidas pelo Usuário.

dx @$cursession.Processes.Select(p => new {Name = p.Name, Threads = p.Threads})

Com esse comando, 'dx' não imprime mais o nome, portanto, adicione -r2 (recurse dois níveis) para exibir Nome e Threads.

dx -r2 @$cursession.Processes.Select(p => new {Name = p.Name, Threads = p.Threads})
@$cursession.Processes.Select(p => new {Name = p.Name, Threads = p.Threads})                
    [0x0]           
        Name             : Idle
        Threads         
    [0x4]           
        Name             : System
        Threads         
    [0x90]          
        Name             : Registry
        Threads       

Neste ponto, estamos exibindo o nome do processo e uma lista de threads. Para exibir o ThreadCount, use o . Método Count( ).

0: kd> dx -r2 @$cursession.Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count()})
@$cursession.Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count()})                
    [0x0]           
        Name             : Idle
        ThreadCount      : 0x4
    [0x4]           
        Name             : System
        ThreadCount      : 0xe7
    [0x90]          
        Name             : Registry
        ThreadCount      : 0x4
...

Para ver quais processos têm um grande número de threads, solicite a lista por contagem de threads usando OrderByDescending.

0: kd> dx -r2 @$cursession.Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count()}).OrderByDescending(p => p.ThreadCount)
@$cursession.Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count()}).OrderByDescending(p => p.ThreadCount)                
    [0x4]           
        Name             : System
        ThreadCount      : 0xe7
    [0xa38]         
        Name             : svchost.exe
        ThreadCount      : 0x45
    [0x884]         
        Name             : MemCompression
        ThreadCount      : 0x3e

Para renderizar em uma grade formatada, altere o '-r2' para '-g'. O nível de recursão não precisa ser especificado, pois a opção de grade exibe as colunas adequadamente. Por fim, adicione o especificador de formato ',d' para gerar valores decimais.

0: kd> dx -g @$cursession.Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count()}).OrderByDescending(p => p.ThreadCount),d
===========================================================================================
=            = Name                                                         = ThreadCount =
===========================================================================================
= [4]        - System                                                       - 231         =
= [2616]     - svchost.exe                                                  - 69          =
= [2180]     - MemCompression                                               - 62          =
= [968]      - explorer.exe                                                 - 61          =

Exemplos de objetos de depurador

Este exemplo mostra os cinco principais processos que executam a maioria dos threads:

0: kd> dx -r2 Debugger.Sessions.First().Processes.Select(p => new { Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.ThreadCount),5
Debugger.Sessions.First().Processes.Select(p => new { Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.ThreadCount),5 

: 
    [0x4]            : 
        Name             : <Unknown Image>
        ThreadCount      : 0x73
    [0x708]          : 
        Name             : explorer.exe
        ThreadCount      : 0x2d
    [0x37c]          : 
        Name             : svchost.exe
        ThreadCount      : 0x2c
    [0x6b0]          : 
        Name             : MsMpEng.exe
        ThreadCount      : 0x22
    [0x57c]          : 
        Name             : svchost.exe
        ThreadCount      : 0x15
    [...]       

Este exemplo mostra os dispositivos na árvore de dispositivos plug-and-play agrupadas pelo nome do driver do objeto do dispositivo físico. Nem toda a saída é mostrada.

kd> dx -r2 Debugger.Sessions.First().Devices.DeviceTree.Flatten(n => n.Children).GroupBy(n => n.PhysicalDeviceObject->Driver->DriverName.ToDisplayString())
Debugger.Sessions.First().Devices.DeviceTree.Flatten(n => n.Children).GroupBy(n => n.PhysicalDeviceObject->Driver->DriverName.ToDisplayString()) 

: 
    ["\"\\Driver\\PnpManager\""] : 
        [0x0]            : HTREE\ROOT\0
        [0x1]            : ROOT\volmgr\0000 (volmgr)
        [0x2]            : ROOT\BasicDisplay\0000 (BasicDisplay)
        [0x3]            : ROOT\CompositeBus\0000 (CompositeBus)
        [0x4]            : ROOT\vdrvroot\0000 (vdrvroot)
         ...  

Preenchimento automático da guia comando Dx

A conclusão automática da chave TAB contextual está ciente dos métodos de consulta LINQ e funcionará para parâmetros de lambdas.

Por exemplo, digite (ou copie e cole) o texto a seguir no depurador. Em seguida, pressione a tecla TAB várias vezes para percorrer possíveis conclusões.

dx -r2 Debugger.Sessions.First().Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.

Pressione a tecla TAB até ". Nome" é exibido. Adicione um parêntese de fechamento ")" e pressione Enter para executar o comando.

kd> dx -r2 Debugger.Sessions.First().Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.Name)
Debugger.Sessions.First().Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.Name) : 
    [0x274]          : 
        Name             : winlogon.exe
        ThreadCount      : 0x4
    [0x204]          : 
        Name             : wininit.exe
        ThreadCount      : 0x2
    [0x6c4]          : 
        Name             : taskhostex.exe
        ThreadCount      : 0x8
         ...  

Este exemplo mostra a conclusão com um método de comparador de chave. A substituição mostrará métodos de cadeia de caracteres, já que a chave é uma cadeia de caracteres.

dx -r2 Debugger.Sessions.First().Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.Name, (a, b) => a.

Pressione a tecla TAB até ". Length" é exibido. Adicione um parêntese de fechamento ")" e pressione Enter para executar o comando.

kd> dx -r2 Debugger.Sessions.First().Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.Name, (a, b) => a.Length)
Debugger.Sessions.First().Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.Name, (a, b) => a.Length) : 
    [0x544]          : 
        Name             : spoolsv.exe
        ThreadCount      : 0xc
    [0x4d4]          : 
        Name             : svchost.exe
        ThreadCount      : 0xa
    [0x438]          : 
        Name             : svchost.exe

Variáveis definidas pelo usuário

Uma variável definida pelo usuário pode ser definida prefixando o nome da variável com @$. Uma variável definida pelo usuário pode ser atribuída a qualquer coisa que dx possa utilizar, por exemplo, lambdas, os resultados de consultas LINQ etc.

Você pode criar e definir o valor de uma variável de usuário como esta.

kd> dx @$String1="Test String"

Você pode exibir as variáveis de usuário definidas usando Debugger.State.UserVariables ou @$vars.

kd> dx Debugger.State.UserVariables
Debugger.State.UserVariables : 
    mySessionVar     : 
    String1          : Test String

Você pode remover uma variável usando . Remover.

kd> dx @$vars.Remove("String1")

Este exemplo mostra como definir uma variável de usuário para fazer referência a Debugger.Sesssions.

kd> dx @$mySessionVar = Debugger.Sessions

A variável definida pelo usuário pode ser usada conforme mostrado abaixo.

kd> dx -r2 @$mySessionVar 
@$mySessionVar   : 
    [0x0]            : Remote KD: KdSrv:Server=@{<Local>},Trans=@{COM:Port=\\.\com3,Baud=115200,Timeout=4000}
        Processes        : 
        Devices     

Variáveis definidas pelo sistema

As variáveis definidas pelo sistema a seguir podem ser usadas em qualquer consulta LINQ dx.

  • @$cursession - A sessão atual

  • @$curprocess - O processo atual

  • @$curthread - O thread atual

Este exemplo mostra o uso das variáveis definidas pelo sistema.

kd> dx @$curprocess.Threads.Count()
@$curprocess.Threads.Count() : 0x4
kd> dx -r1 @$curprocess.Threads
@$curprocess.Threads : 
    [0x4adc]         : 
    [0x1ee8]         : 
    [0x51c8]         : 
    [0x62d8]         : 
     ...

Variáveis definidas pelo usuário – Tipos anônimos

Essa criação de objetos dinâmicos é feita usando a sintaxe de tipo anônimo C# (novo { ... }). Para obter mais informações, consulte Tipos anônimos (Guia de Programação em C#). Este exemplo cria um tipo anônimo com um valor inteiro e de cadeia de caracteres.

kd> dx -r1 new { MyInt = 42, MyString = "Hello World" }
new { MyInt = 42, MyString = "Hello World" } : 
    MyInt            : 42
    MyString         : Hello World

Objetos de função (expressões Lambda)

Muitos dos métodos usados para consultar dados baseiam-se no conceito de executar repetidamente uma função fornecida pelo usuário entre objetos em uma coleção. Para dar suporte à capacidade de consultar e manipular dados no depurador, o comando dx dá suporte a expressões lambda usando a sintaxe C# equivalente. Uma expressão lambda é definida pelo uso do operador => da seguinte maneira:

(argumentos) => (resultado)

Para ver como o LINQ é usado com dx, tente este exemplo simples para adicionar 5 e 7.

kd> dx ((x, y) => (x + y))(5, 7) 

O comando dx ecoa de volta a expressão lambda e exibe o resultado de 12.

((x, y) => (x + y))(5, 7)  : 12

Esta expressão lambda de exemplo combina as cadeias de caracteres "Hello" e "World".

kd> dx ((x, y) => (x + y))("Hello", "World")
((x, y) => (x + y))("Hello", "World") : HelloWorld

Sintaxe LINQ com suporte – Métodos de consulta

Qualquer objeto que dx define como iterável (seja uma matriz nativa, um tipo que tenha NatVis escrito descrevendo-o como um contêiner ou um objeto de extensão de depurador) tem uma série de métodos LINQ (ou equivalente linq) projetados nele. Esses métodos de consulta são descritos abaixo. As assinaturas dos argumentos para os métodos de consulta são listadas após todos os métodos de consulta.

Métodos de filtragem

. Where ( PredicateMethod ): retorna uma nova coleção de objetos que contém todos os objetos na coleção de entrada para a qual o método de predicado retornou true.

Métodos de projeção

. Flatten ( [KeyProjectorMethod] ): usa um contêiner de entrada de contêineres (uma árvore) e o nivela em um único contêiner que tem todos os elementos na árvore. Se o método de projetor de chave opcional for fornecido, a árvore será considerada um contêiner de chaves que são os próprios contêineres e essas chaves serão determinadas por uma chamada para o método de projeção.

. Select ( KeyProjectorMethod ): retorna uma nova coleção de objetos que contém o resultado da chamada do método do projetor em cada objeto na coleção de entrada.

Métodos de agrupamento

. GroupBy ( KeyProjectorMethod, [KeyComparatorMethod] ): retorna uma nova coleção de coleções agrupando todos os objetos na coleção de entrada com a mesma chave determinada chamando o método de projetor de chave. Um método comparador opcional pode ser fornecido.

Join (InnerCollection, outer key selector method, Inner key selector method, Result selector method, [ComparatorMethod]): une duas sequências com base em funções seletoras de chave e extrai pares de valores. Um método comparador opcional também pode ser especificado.

Intersecção (InnerCollection, [ComparatorMethod]): retorna a interseção do conjunto, o que significa elementos que aparecem em cada uma das duas coleções. Um método comparador opcional também pode ser especificado.

Union (InnerCollection, [ComparatorMethod]): retorna a união definida, o que significa elementos exclusivos que aparecem em uma das duas coleções. Um método comparador opcional também pode ser especificado.

Métodos de conjunto de dados

Contains (Object, [ComparatorMethod]): determina se uma sequência contém um elemento especificado. Um método comparador opcional pode ser fornecido que será chamado sempre que o elemento for comparado com uma entrada na sequência.

Distinct ([ComparatorMethod]): remove valores duplicados de uma coleção. Um método comparador opcional pode ser fornecido para ser chamado sempre que objetos na coleção precisarem ser comparados.

Exceto (InnerCollection, [ComparatorMethod]): retorna a diferença de conjunto, o que significa os elementos de uma coleção que não aparecem em uma segunda coleção. Um método comparador opcional pode ser especificado.

Concat (InnerCollection): concatena duas sequências para formar uma sequência.

Métodos de ordenação

. OrderBy ( KeyProjectorMethod, [KeyComparatorMethod] ): classifica a coleção em ordem crescente de acordo com uma chave, conforme fornecido chamando o método de projeção de chave em cada objeto na coleção de entrada. Um método comparador opcional pode ser fornecido.

. OrderByDescending ( KeyProjectorMethod, [KeyComparatorMethod] ): classifica a coleção em ordem decrescente de acordo com uma chave, conforme fornecido chamando o método de projeção de chave em cada objeto na coleção de entrada. Um método comparador opcional pode ser fornecido.

Métodos de agregação

Count (): um método que retorna o número de elementos na coleção.

Soma ([ProjectionMethod]): calcula a soma dos valores em uma coleção. Opcionalmente, pode especificar um método de projetor para transformar os elementos antes da ocorrência da soma.

Ignorar métodos

Ignorar (Contagem): ignora os elementos até uma posição especificada em uma sequência.

SkipWhile (PredicateMethod): ignora elementos com base em uma função de predicado até que um elemento não atenda à condição.

Métodos Take

Take (Count): leva elementos até uma posição especificada em uma sequência.

TakeWhile (PredicateMethod): usa elementos com base em uma função de predicado até que um elemento não atenda à condição.

Métodos de comparação

SequenceEqual (InnerCollection, [ComparatorMethod]): determina se duas sequências são iguais comparando elementos de maneira emparelhada. Um comparador opcional pode ser especificado.

Métodos de tratamento de erros

AllNonError (PredicateMethod): retorna se todos os elementos que não são de erro de uma coleção atendem a uma determinada condição.

FirstNonError ([PredicateMethod]): retorna o primeiro elemento de uma coleção que não é um erro.

LastNonError ([PredicateMethod]): retorna o último elemento de uma coleção que não é um erro.

Outros métodos

. All ( PredicateMethod ): retorna se o resultado de chamar o método de predicado especificado em cada elemento na coleção de entrada é true.

. Any ( PredicateMethod ): retorna se o resultado de chamar o método de predicado especificado em qualquer elemento na coleção de entrada é true.

. First ( [PredicateMethod] ): retorna o primeiro elemento da coleção. Se o predicado opcional for passado, retornará o primeiro elemento na coleção para o qual uma chamada para o predicado retornará true.

. Last ( [PredicateMethod] ): retorna o último elemento na coleção. Se o predicado opcional for passado, retornará o último elemento na coleção para o qual uma chamada para o predicado retornará true.

Min([KeyProjectorMethod]): retorna o elemento mínimo da coleção. Um método de projetor opcional pode ser especificado para projetar cada método antes de ser comparado com outros.

Max([KeyProjectorMethod]): retorna o elemento máximo da coleção. Um método de projetor opcional pode ser especificado para projetar cada método antes de ser comparado com outros.

Single([PredicateMethod]): retorna o único elemento da lista (ou um erro se a coleção contiver mais de um elemento). Se um predicado for especificado, retornará o único elemento que satisfaz esse predicado (se mais de um elemento o atender, a função retornará um erro).

Assinaturas dos argumentos

KeyProjectorMethod : ( obj => chave arbitrária ) Usa um objeto da coleção e retorna uma chave desse objeto.
KeyComparatorMethod: ( (a, b) => valor inteiro ) Pega duas chaves e as compara retornando:

-1 se ( a < b )

0 se ( a == b)

1 se ( a > b )

PredicateMethod: ( obj => valor booliano ) Usa um objeto da coleção e retorna true ou false com base em se esse objeto atende a determinados critérios.

Sintaxe LINQ com suporte – Manipulação de cadeia de caracteres

Todos os objetos de cadeia de caracteres têm os seguintes métodos projetados neles, para que estejam disponíveis para uso:

Consultar métodos relevantes & propriedades

. Contains ( OtherString ): retorna um valor booliano que indica se a cadeia de caracteres de entrada contém OtherString.

. EndsWith ( OtherString ): retorna um valor booliano que indica se a cadeia de caracteres de entrada termina com OtherString.

Comprimento: uma propriedade que retorna o comprimento da cadeia de caracteres.

. StartsWith ( OtherString ): retorna um valor booliano que indica se a cadeia de caracteres de entrada começa com OtherString.

. Substring ( StartPos, [Length] ): retorna uma subcadeia de caracteres dentro da cadeia de caracteres de entrada começando na posição inicial determinada. Se o comprimento opcional for fornecido, a subcadeia de caracteres retornada será do comprimento especificado; caso contrário, ele irá para o final da cadeia de caracteres.

Métodos diversos

. IndexOf ( OtherString ): retorna o índice da primeira ocorrência de OtherString dentro da cadeia de caracteres de entrada.

. LastIndexOf ( OtherString ): retorna o índice da última ocorrência de OtherString dentro da cadeia de caracteres de entrada.

Métodos de formatação

. PadLeft ( TotalWidth ): adiciona espaços conforme necessário ao lado esquerdo da cadeia de caracteres para trazer o comprimento total da cadeia de caracteres para a largura especificada.

. PadRight ( TotalWidth ): adiciona espaços conforme necessário ao lado direito da cadeia de caracteres para trazer o comprimento total da cadeia de caracteres para a largura especificada.

. Remove ( StartPos, [Length] ): remove caracteres da cadeia de caracteres de entrada começando como a posição inicial especificada. Se o parâmetro de comprimento opcional for fornecido, esse número de caracteres será removido; caso contrário, todos os caracteres até o final da cadeia de caracteres serão removidos.

. Replace ( SearchString, ReplaceString ): substitui todas as ocorrências de SearchString dentro da cadeia de caracteres de entrada pelo ReplaceString especificado.

Projeções de objeto de cadeia de caracteres

Além dos métodos que são projetados diretamente em objetos de cadeia de caracteres, qualquer objeto que tenha uma conversão de cadeia de caracteres tem o seguinte método projetado nele, tornando-o método disponível para uso:

. ToDisplayString ( ): retorna uma conversão de cadeia de caracteres do objeto . Essa é a conversão de cadeia de caracteres que seria mostrada em uma invocação dx para o objeto . Você pode fornecer um especificador de formatação para formatar a saída de ToDisplayString. Para obter mais informações, consulte Especificadores de formato para C++ no depurador do Visual Studio

Os exemplos a seguir ilustram o uso de especificadores de formato.

kd> dx (10).ToDisplayString("d")
(10).ToDisplayString("d") : 10

kd> dx (10).ToDisplayString("x")
(10).ToDisplayString("x") : 0xa

kd> dx (10).ToDisplayString("o")
(10).ToDisplayString("o") : 012

kd> dx (10).ToDisplayString("b") 
(10).ToDisplayString("b")  : 0y1010

kd> dx ("some wchar string here").ToDisplayString("su") 
("some wchar string here").ToDisplayString("su")  : "some wchar string here"

kd> dx ("some wchar string here").ToDisplayString("sub") 
("some wchar string here").ToDisplayString("sub")  : some wchar string here

Exemplo de depuração Plug and Play

Esta seção ilustra como os objetos internos do depurador usados com consultas LINQ podem ser usados para depurar objetos plug and play.

Exibir todos os dispositivos

Use Flatten na árvore de dispositivos para exibir todos os dispositivos.

 1: kd> dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children)
@$cursession.Devices.DeviceTree.Flatten(n => n.Children)                
    [0x0]            : HTREE\ROOT\0
    [0x1]            : ROOT\volmgr\0000 (volmgr)
    [0x2]            : ROOT\BasicDisplay\0000 (BasicDisplay)
    [0x3]            : ROOT\CompositeBus\0000 (CompositeBus)
    [0x4]            : ROOT\vdrvroot\0000 (vdrvroot)
    [0x5]            : ROOT\spaceport\0000 (spaceport)
    [0x6]            : ROOT\KDNIC\0000 (kdnic)
    [0x7]            : ROOT\UMBUS\0000 (umbus)
    [0x8]            : ROOT\ACPI_HAL\0000
...

Exibição de grade

Assim como acontece com outros comandos dx, você pode selecionar e segurar (ou clicar com o botão direito do mouse) um comando depois que ele foi executado e selecionar "Exibir como grade" ou adicionar "-g" ao comando para obter uma exibição de grade dos resultados.

# 0: kd> dx -g @$cursession.Devices.DeviceTree.Flatten(n => n.Children)
=====================================================================================================================================================================================================================================================================================================================
# =                                                              = (+) DeviceNodeObject = InstancePath                                                 = ServiceName               = (+) PhysicalDeviceObject                                    = State                          = (+) Resources = (+) Children       =
=====================================================================================================================================================================================================================================================================================================================
= [0x0] : HTREE\ROOT\0                                         - {...}                - HTREE\ROOT\0                                                 -                           - 0xffffb6075614be40 : Device for "\Driver\PnpManager"        - DeviceNodeStarted (776)        - {...}        - [object Object]    =
= [0x1] : ROOT\volmgr\0000 (volmgr)                            - {...}                - ROOT\volmgr\0000                                             - volmgr                    - 0xffffb607561fbe40 : Device for "\Driver\PnpManager"        - DeviceNodeStarted (776)        - {...}        - [object Object]    =
= [0x2] : ROOT\BasicDisplay\0000 (BasicDisplay)                - {...}                - ROOT\BasicDisplay\0000                                       - BasicDisplay              - 0xffffb607560739b0 : Device for "\Driver\PnpManager"        - DeviceNodeStarted (776)        - {...}        - [object Object]    =
= [0x3] : ROOT\CompositeBus\0000 (CompositeBus)                - {...}                - ROOT\CompositeBus\0000                                       - CompositeBus              - 0xffffb607561f9060 : Device for "\Driver\PnpManager"        - DeviceNodeStarted (776)        - {...}        - [object Object]    =
...

Exibir dispositivos por estado

Use Where para especificar um estado de dispositivo específico.

dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.State <operator> <state number>)

Por exemplo, para exibir dispositivos no estado DeviceNodeStarted, use este comando.

1: kd>  dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.State == 776)
@$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.State == 776)                
    [0x0]            : HTREE\ROOT\0
    [0x1]            : ROOT\volmgr\0000 (volmgr)
    [0x2]            : ROOT\BasicDisplay\0000 (BasicDisplay)
    [0x3]            : ROOT\CompositeBus\0000 (CompositeBus)
    [0x4]            : ROOT\vdrvroot\0000 (vdrvroot)
...

Exibir dispositivos não iniciados

Use este comando para exibir dispositivos que não estão no estado DeviceNodeStarted.

1: kd>  dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.State != 776)
@$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.State != 776)                
    [0x0]            : ACPI\PNP0C01\1
    [0x1]            : ACPI\PNP0000\4&215d0f95&0
    [0x2]            : ACPI\PNP0200\4&215d0f95&0
    [0x3]            : ACPI\PNP0100\4&215d0f95&0
    [0x4]            : ACPI\PNP0800\4&215d0f95&0
    [0x5]            : ACPI\PNP0C04\4&215d0f95&0
    [0x6]            : ACPI\PNP0700\4&215d0f95&0 (fdc)
    [0x7]            : ACPI\PNP0C02\1
    [0x8]            : ACPI\PNP0C02\2

Exibir dispositivos por código de problema

Use o objeto DeviceNodeObject.Problem para exibir dispositivos que têm códigos de problema específicos.

dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem <operator> <problemCode>)

Por exemplo, para exibir dispositivos que têm um código de problema diferente de zero, use este comando. Isso fornece informações semelhantes a "!devnode 0 21".

1: kd> dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem != 0)
@$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem != 0)                
    [0x0]            : HTREE\ROOT\0
    [0x1]            : ACPI\PNP0700\4&215d0f95&0 (fdc)

Exibir todos os dispositivos sem problemas

Use este comando para exibir todos os dispositivos sem problemas

1: kd> dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem == 0)
@$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem == 0)                
    [0x0]            : ROOT\volmgr\0000 (volmgr)
    [0x1]            : ROOT\BasicDisplay\0000 (BasicDisplay)
    [0x2]            : ROOT\CompositeBus\0000 (CompositeBus)
    [0x3]            : ROOT\vdrvroot\0000 (vdrvroot)
...

Exibir todos os dispositivos com um problema específico

Use este comando para exibir dispositivos com um estado de problema de 0x16.

1: kd> dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem == 0x16)
@$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem == 0x16)                
    [0x0]            : HTREE\ROOT\0
    [0x1]            : ACPI\PNP0700\4&215d0f95&0 (fdc)

Exibir dispositivos por driver de função

Use este comando para exibir dispositivos por driver de função.

dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.ServiceName <operator> <service name>)

Para exibir dispositivos usando um determinado driver de função, como atapi, use este comando.

1: kd> dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.ServiceName == "atapi")
@$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.ServiceName == "atapi")                
    [0x0]            : PCIIDE\IDEChannel\4&10bf2f88&0&0 (atapi)
    [0x1]            : PCIIDE\IDEChannel\4&10bf2f88&0&1 (atapi)

Exibindo uma lista de drivers de inicialização

Para exibir a lista do que o winload carregou como drivers de inicialização, você precisa estar em um contexto em que tenha acesso ao LoaderBlock e cedo o LoaderBlock ainda esteja por perto. Por exemplo, durante nt! IopInitializeBootDrivers. Um ponto de interrupção pode ser definido para parar nesse contexto.

1: kd> g
Breakpoint 0 hit
nt!IopInitializeBootDrivers:
8225c634 8bff            mov     edi,edi

Use o ?? para exibir a estrutura do driver de inicialização.

1: kd> ?? LoaderBlock->BootDriverListHead
struct _LIST_ENTRY
 [ 0x808c9960 - 0x808c8728 ]
   +0x000 Flink            : 0x808c9960 _LIST_ENTRY [ 0x808c93e8 - 0x808a2e18 ]
   +0x004 Blink            : 0x808c8728 _LIST_ENTRY [ 0x808a2e18 - 0x808c8de0 ]

Use o objeto de depurador Debugger.Utility.Collections.FromListEntry para exibir os dados, usando o endereço inicial da estrutura nt!_LIST_ENTRY.

1: kd> dx Debugger.Utility.Collections.FromListEntry(*(nt!_LIST_ENTRY *)0x808c9960, "nt!_BOOT_DRIVER_LIST_ENTRY", "Link")
Debugger.Utility.Collections.FromListEntry(*(nt!_LIST_ENTRY *)0x808c9960, "nt!_BOOT_DRIVER_LIST_ENTRY", "Link")                
    [0x0]            [Type: _BOOT_DRIVER_LIST_ENTRY]
    [0x1]            [Type: _BOOT_DRIVER_LIST_ENTRY]
    [0x2]            [Type: _BOOT_DRIVER_LIST_ENTRY]
    [0x3]            [Type: _BOOT_DRIVER_LIST_ENTRY]
    [0x4]            [Type: _BOOT_DRIVER_LIST_ENTRY]
    [0x5]            [Type: _BOOT_DRIVER_LIST_ENTRY]
...

Use a opção -g para criar uma exibição de grade dos dados.

dx -r1 -g Debugger.Utility.Collections.FromListEntry(*(nt!_LIST_ENTRY *)0x808c9960, "nt!_BOOT_DRIVER_LIST_ENTRY", "Link")

Exibir dispositivos por funcionalidade

Exiba dispositivos por funcionalidade usando o objeto DeviceNodeObject.CapabilityFlags.

dx -r1 @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => (n.DeviceNodeObject.CapabilityFlags & <flag>) != 0)

Esta tabela resume o uso do comando dx com sinalizadores comuns de funcionalidade do dispositivo.

Removível

dbgcmd 0: kd> dx -r1 @$cursession. Devices.DeviceTree.Flatten(n => n.Children). Where(n => (n.DeviceNodeObject.CapabilityFlags & 0x10) != 0) @$cursession. Devices.DeviceTree.Flatten(n => n.Children). Where(n => (n.DeviceNodeObject.CapabilityFlags & 0x10) != 0)
[0x0] : SWD\PRINTENUM{2F8DBBB6-F246-4D84-BB1D-AA8761353885} [0x1] : SWD\PRINTENUM{F210BC77-55A1-4FCA-AA80-013E2B408378} [0x2] : SWD\PRINTENUM{07940A8E-11F4-46C3-B714-7FF9B87738F8} [0x3] : DISPLAY\Default_Monitor\6&1a097cd8&0&UID5527112 (monitor)

UniqueID

dbgcmd 0: kd> dx -r1 @$cursession. Devices.DeviceTree.Flatten(n => n.Children). Where(n => (n.DeviceNodeObject.CapabilityFlags & 0x40) != 0) @$cursession. Devices.DeviceTree.Flatten(n => n.Children). Where(n => (n.DeviceNodeObject.CapabilityFlags & 0x40) != 0)
[0x0] : HTREE\ROOT\0 [0x1] : ROOT\volmgr\0000 (volmgr) [0x2] : ROOT\spaceport\0000 (spaceport) ...

SilentInstall

dbgcmd 0: kd> dx -r1 @$cursession. Devices.DeviceTree.Flatten(n => n.Children). Where(n => (n.DeviceNodeObject.CapabilityFlags & 0x80) != 0) @$cursession. Devices.DeviceTree.Flatten(n => n.Children). Where(n => (n.DeviceNodeObject.CapabilityFlags & 0x80) != 0)
[0x0] : HTREE\ROOT\0 [0x1] : ROOT\volmgr\0000 (volmgr) [0x2] : ROOT\spaceport\0000 (spaceport) ...

RawDeviceOk

dbgcmd 0: kd> dx -r1 @$cursession. Devices.DeviceTree.Flatten(n => n.Children). Where(n => (n.DeviceNodeObject.CapabilityFlags & 0x100) != 0) @$cursession. Devices.DeviceTree.Flatten(n => n.Children). Where(n => (n.DeviceNodeObject.CapabilityFlags & 0x100) != 0)
[0x0] : HTREE\ROOT\0 [0x1] : SWD\MMDEVAPI\MicrosoftGSWavetableSynth [0x2] : SWD\IP_TUNNEL_VBUS\IP_TUNNEL_DEVICE_ROOT ...

SurpriseRemovalOK

dbgcmd 0: kd> dx -r1 @$cursession. Devices.DeviceTree.Flatten(n => n.Children). Where(n => (n.DeviceNodeObject.CapabilityFlags & 0x200) != 0) @$cursession. Devices.DeviceTree.Flatten(n => n.Children). Where(n => (n.DeviceNodeObject.CapabilityFlags & 0x200) != 0)
[0x0] : SWD\MMDEVAPI\MicrosoftGSWavetableSynth [0x1] : SWD\IP_TUNNEL_VBUS\IP_TUNNEL_DEVICE_ROOT [0x2] : SWD\PRINTENUM\PrintQueues ...

Para obter mais informações sobre o CapabilityFlags, consulte DEVICE_CAPABILITIES.

Confira também

dx (exibir expressão de modelo de objeto do depurador)

Objetos nativos do depurador no NatVis

Objetos nativos do depurador em extensões JavaScript