Использование LINQ с объектами отладчика

Синтаксис LINQ можно использовать с объектами отладчика для поиска данных и управления ими. Использование синтаксиса LINQ с командой dx обеспечивает более согласованное взаимодействие по сравнению с командами отладчика. Выходные данные и параметры согласованы независимо от того, какой объект отладчика вы просматриваете. Запросы LINQ позволяют задавать такие вопросы, как "Какие 5 основных процессов выполняют больше всего потоков?".

Объекты отладчика проецируются в пространство имен с корнем "Отладчик". Процессы, модули, потоки, стеки, кадры стека и локальные переменные доступны для использования в запросе LINQ.

LINQ концептуально похож на язык SQL (SQL), который используется для запросов к базам данных. Для поиска, фильтрации и анализа отладочных данных можно использовать несколько методов LINQ. Используется синтаксис метода LINQ C#. Дополнительные сведения о синтаксисе LINQ и LINQ C# см. в разделе начало работы с LINQ в C#

LINQ, используемый в поддержке отладчика, использует синтаксис метода LINQ, а не синтаксис запроса. Дополнительные сведения о различиях в LINQ (запрос, интегрированный с языком) см. в этой статье.

С объектами отладчика можно использовать следующие команды LINQ. Все. Любой. Рассчитывать. Первый. Выровнять. Groupby. Последний. Orderby. OrderByDescending, . Выберите и . Где. Эти методы соответствуют (насколько это возможно) форме метода LINQ C#.

Собственные объекты отладчика

Собственные объекты отладчика представляют различные конструкции и поведения среды отладчика. Ниже приведены примеры объектов отладчика.

  • Сеанс
  • Потоки / Потоки
  • Процессы / Процесс
  • Кадры стека / кадр стека
  • Локальные переменные
  • Модули / Модуль
  • Служебная программа
  • Состояние
  • Параметры

Вы также можете работать с объектами отладчика с помощью NatVis. Дополнительные сведения см. в разделе Собственные объекты отладчика в NatVis. Сведения об использовании объектов отладчика с JavaScript см. в разделе Собственные объекты отладчика в расширениях JavaScript. Сведения о работе с C++ и объектами драйвера см. в разделе Обзор модели данных отладчика C++.

Команда Dx

В приведенных здесь примерах используется команда dx. Дополнительные сведения о работе с командой dx см. в разделе dx (отображение выражения объектной модели отладчика).

Разработка запроса LINQ

Одним из способов разработки запроса объекта отладчика LINQ является использование ссылок DML, отображаемых для изучения модели данных, чтобы сначала найти объект отладчика, который будет использоваться в запросе.

В этом примере мы хотим отобразить список процессов в сеансе отладки ядра и количество потоков для каждого из этих процессов.

Чтобы начать исследование, можно использовать команду dx для отображения объекта отладчика верхнего уровня.

0: kd> dx Debugger
Debugger
    Sessions
    Settings
    State
    Utility

Выбрав разделы верхнего уровня, мы определяем, что сеансы выглядят наиболее интересными, поэтому мы выбираем ссылку DML, чтобы показать, что она содержит процессы.

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

Затем мы выберем дополнительные сведения, чтобы просмотреть конкретный процесс, и видим, что потоки , связанные с этим процессом, доступны. Если выбрать потоки для одного из процессов, мы увидим, что все связанные с этим процессом потоки доступны.

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] 

Теперь мы знаем, что данные, необходимые для отображения количества потоков, связанных с процессом, доступны в объектной модели отладчика.

Чтобы сделать запрос LINQ немного короче, можно использовать системные переменные, описанные далее в этом разделе, для отображения процессов, связанных с текущим сеансом.

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

Затем добавьте оператор select. Для начала можно указать поле Имя.

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

Для нашего сценария также требуется количество потоков. Так как существует два поля, создайте анонимный тип с помощью нового, аналогично синтаксису анонимного типа C#, описанному ниже в разделе Определяемые пользователем переменные.

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

С помощью этой команды dx больше не выводит имя, поэтому добавьте -r2 (рекурсив два уровня) для отображения имени и потоков.

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       

На этом этапе отображается имя процесса и список потоков. Чтобы отобразить ThreadCount, используйте . Метод 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
...

Чтобы узнать, какие процессы имеют большое количество потоков, упорядочение списка по количеству потоков с помощью 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

Для отрисовки в форматируемой сетке измените значение "-r2" на "-g". Уровень рекурсии указывать не нужно, так как параметр сетки отображает столбцы соответствующим образом. Наконец, добавьте описатель формата ",d" для вывода десятичных значений.

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          =

Примеры объектов отладчика

В этом примере показаны 5 основных процессов, выполняющих наибольшее число потоков:

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
    [...]       

В этом примере показаны устройства в дереве устройств plug and play, сгруппированные по имени драйвера физического объекта устройства. Отображаются не все выходные данные.

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

Автозавершение на вкладке "Команда Dx"

Контекстное автозавершение клавиши TAB учитывает методы запроса LINQ и работает для параметров лямбда-выражений.

Например, введите (или скопируйте и вставьте) следующий текст в отладчик. Затем нажмите клавишу TAB несколько раз, чтобы выполнить циклический переход по возможным завершениям.

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

Нажимайте клавишу TAB, пока не будет ". Появится имя". Добавьте закрывающая скобку ")" и нажмите клавишу ВВОД, чтобы выполнить команду.

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
         ...  

В этом примере показано завершение с помощью метода сравнения ключей. Подстановка будет отображать строковые методы, так как ключ является строкой.

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

Нажимайте клавишу TAB, пока не будет ". Появится длина". Добавьте закрывающая скобку ")" и нажмите клавишу ВВОД, чтобы выполнить команду.

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

Определяемые пользователем переменные

Определяемую пользователем переменную можно определить, указав для имени переменной префикс @$. Определяемую пользователем переменную можно назначить чему угодно, что dx может использовать, например, лямбда-выражения, результаты запросов LINQ и т. д.

Вы можете создать и задать значение пользовательской переменной следующим образом.

kd> dx @$String1="Test String"

Определенные пользовательские переменные можно отобразить с помощью Debugger.State.UserVariables или @$vars.

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

Переменную можно удалить с помощью . Удалить.

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

В этом примере показано, как определить пользовательскую переменную для ссылки на Debugger.Sesssions.

kd> dx @$mySessionVar = Debugger.Sessions

Затем можно использовать определяемую пользователем переменную, как показано ниже.

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

Системные переменные

Следующие системные переменные можно использовать в любом запросе LINQ dx.

  • @$cursession — текущий сеанс

  • @$curprocess — текущий процесс

  • @$curthread — текущий поток

В этом примере показано использование системных переменных.

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

Определяемые пользователем переменные — анонимные типы

Создание динамических объектов выполняется с помощью синтаксиса анонимного типа C# (новый { ... }). Дополнительные сведения см. в разделе Анонимные типы (руководство по программированию на C#). В этом примере создается анонимный тип с целым числом и строковым значением.

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

Объекты функций (лямбда-выражения)

Многие методы, используемые для запроса данных, основаны на концепции многократного выполнения предоставленной пользователем функции между объектами в коллекции. Для поддержки возможности запроса данных и управления ими в отладчике команда dx поддерживает лямбда-выражения с использованием эквивалентного синтаксиса C#. Лямбда-выражение определяется использованием оператора => следующим образом:

(аргументы) => (результат)

Чтобы узнать, как LINQ используется с dx, попробуйте этот простой пример, чтобы сложить 5 и 7.

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

Команда dx возвращает лямбда-выражение и отображает результат 12.

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

В этом примере лямбда-выражения объединяются строки Hello и World.

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

Поддерживаемый синтаксис LINQ — методы запроса

Любой объект, который dx определяет как итерируемый (будь то собственный массив, тип с NatVis, описывающий его как контейнер, или объект расширения отладчика), имеет ряд методов LINQ (или эквивалентных LINQ), проецируемых на него. Эти методы запроса описаны ниже. Сигнатуры аргументов методов запроса перечислены после всех методов запроса.

Методы фильтрации

. Where ( PredicateMethod ): возвращает новую коллекцию объектов , содержащих каждый объект входной коллекции, для которой метод предиката вернул значение true.

Методы проекции

. Flatten ( [KeyProjectorMethod] ): принимает входной контейнер контейнеров (дерево) и преобразует его в плоскую структуру в один контейнер с каждым элементом в дереве. Если указан необязательный метод проектора ключа, дерево считается контейнером ключей, которые сами по себе являются контейнерами, и эти ключи определяются вызовом метода проекции.

. Select ( KeyProjectorMethod ): возвращает новую коллекцию объектов , содержащую результат вызова метода проектора для каждого объекта входной коллекции.

Методы группирования

. GroupBy ( KeyProjectorMethod, [KeyComparatorMethod] ): возвращает новую коллекцию коллекций путем группировки всех объектов входной коллекции с тем же ключом, что и при вызове метода проектора ключа. Можно предоставить необязательный метод сравнения.

Соединение (InnerCollection, метод выбора внешнего ключа, метод селектора внутреннего ключа, метод выбора результата, [ComparatorMethod]): объединяет две последовательности на основе функций селектора ключей и извлекает пары значений. Также можно указать необязательный метод сравнения.

Intersect (InnerCollection, [ComparatorMethod]): возвращает пересечение набора, то есть элементы, которые отображаются в каждой из двух коллекций. Также можно указать необязательный метод сравнения.

Union (InnerCollection, [ComparatorMethod]) — возвращает объединение набора, которое означает уникальные элементы, которые отображаются в любой из двух коллекций. Также можно указать необязательный метод сравнения.

Методы набора данных

Contains (Object, [ComparatorMethod]): определяет, содержит ли последовательность указанный элемент. Можно предоставить необязательный метод сравнения, который будет вызываться при каждом сравнении элемента с записью в последовательности.

Distinct ([ComparatorMethod]): удаляет повторяющиеся значения из коллекции. Можно предоставить необязательный метод сравнения для вызова при каждом сравнении объектов в коллекции.

Except (InnerCollection, [ComparatorMethod]): возвращает разность наборов, то есть элементы одной коллекции, которые не отображаются во второй коллекции. Можно указать необязательный метод сравнения.

Concat (InnerCollection): объединяет две последовательности для формирования одной последовательности.

Методы упорядочивания

. OrderBy ( KeyProjectorMethod, [KeyComparatorMethod] ): сортирует коллекцию в порядке возрастания в соответствии с ключом, указанным путем вызова метода проекции ключа для каждого объекта входной коллекции. Можно предоставить необязательный метод сравнения.

. OrderByDescending ( KeyProjectorMethod, [KeyComparatorMethod] ): сортирует коллекцию в порядке убывания в соответствии с ключом, указанным путем вызова метода проекции ключа для каждого объекта во входной коллекции. Можно предоставить необязательный метод сравнения.

Агрегирование методов

Count (): метод, возвращающий количество элементов в коллекции.

Sum ([ProjectionMethod]): вычисляет сумму значений в коллекции. При необходимости можно указать метод проектора для преобразования элементов перед суммированием.

Пропустить методы

Skip (Count): пропускает элементы до указанной позиции в последовательности.

SkipTime (PredicateMethod): пропускает элементы, основанные на функции предиката, пока элемент не удовлетворяет условию.

Методы Take

Take (Count): принимает элементы до указанной позиции в последовательности.

TakeTime (PredicateMethod): принимает элементы на основе функции предиката до тех пор, пока элемент не удовлетворяет условию.

Методы сравнения

SequenceEqual (InnerCollection, [ComparatorMethod]): определяет, равны ли две последовательности, путем сравнения элементов в парном порядке. Можно указать необязательный компаратор.

Методы обработки ошибок

AllNonError (PredicateMethod): возвращает значение, удовлетворяющее заданному условию всех элементов коллекции, не являющихся ошибками.

FirstNonError ([PredicateMethod]): возвращает первый элемент коллекции, который не является ошибкой.

LastNonError ([PredicateMethod]): возвращает последний элемент коллекции, который не является ошибкой.

Другие методы

. All ( PredicateMethod): возвращает значение, определяющее, является ли результатом вызова указанного метода предиката для каждого элемента входной коллекции.

. Any ( PredicateMethod ): возвращает значение , является ли результат вызова указанного метода предиката для любого элемента входной коллекции истинным.

. First ( [PredicateMethod] ): возвращает первый элемент в коллекции. Если передается необязательный предикат, возвращает первый элемент в коллекции, для которого вызов предиката возвращает значение true.

. Last ( [PredicateMethod] ): возвращает последний элемент в коллекции. Если передается необязательный предикат, возвращает последний элемент в коллекции, для которого вызов предиката возвращает значение true.

Min([KeyProjectorMethod]): возвращает минимальный элемент коллекции. Необязательный метод проектора можно указать для проецирования каждого метода перед его сравнением с другими.

Max([KeyProjectorMethod]) — возвращает максимальный элемент коллекции. Необязательный метод проектора можно указать для проецирования каждого метода перед его сравнением с другими.

Single([PredicateMethod]): возвращает единственный элемент из списка (или ошибку, если коллекция содержит несколько элементов). Если указан предикат, возвращает один элемент, удовлетворяющий его (если ему удовлетворяет несколько элементов, функция возвращает ошибку).

Подписи аргументов

KeyProjectorMethod : ( obj => произвольный ключ ) Принимает объект коллекции и возвращает ключ из этого объекта.
KeyComparatorMethod: ( (a, b) => целочисленное значение ) Принимает два ключа и сравнивает их, возвращая:

-1, если ( a < b )

0 if ( a == b)

1, если ( a > b )

PredicateMethod: ( obj => логическое значение ) Принимает объект коллекции и возвращает значение true или false в зависимости от того, соответствует ли этот объект определенным критериям.

Поддерживаемый синтаксис LINQ — обработка строк

Все строковые объекты имеют следующие методы, проецируемые в них, чтобы они были доступны для использования:

Запрос соответствующих методов & свойств

. Contains ( OtherString ): возвращает логическое значение, указывающее, содержит ли входная строка OtherString.

. EndsWith ( OtherString): возвращает логическое значение, указывающее, заканчивается ли входная строка на OtherString.

Length: свойство, возвращающее длину строки.

. StartsWith ( OtherString): возвращает логическое значение, указывающее, начинается ли входная строка с OtherString.

. Substring ( StartPos, [Length] ): возвращает подстроку во входной строке, начиная с заданной начальной позиции. Если указана необязательная длина, возвращаемая подстрока будет иметь указанную длину; в противном случае — он перейдет в конец строки.

Прочие методы

. IndexOf ( OtherString): возвращает индекс первого вхождения OtherString во входной строке.

. LastIndexOf ( OtherString): возвращает индекс последнего вхождения OtherString во входной строке.

Методы форматирования

. PadLeft ( TotalWidth): при необходимости добавляет пробелы в левую часть строки, чтобы привести общую длину строки к указанной ширине.

. PadRight ( TotalWidth): при необходимости добавляет пробелы в правую часть строки, чтобы привести общую длину строки к указанной ширине.

. Remove ( StartPos, [Length] ): удаляет символы из входной строки, начиная с указанной начальной позиции. Если указан необязательный параметр length, это количество символов будет удалено; в противном случае — все символы в конце строки будут удалены.

. Replace ( SearchString, ReplaceString ): заменяет каждое вхождение SearchString во входной строке указанным ReplaceString.

Проекции строковых объектов

В дополнение к методам, проецируемым непосредственно на строковые объекты, любой объект, который сам имеет преобразование строк, имеет следующий метод, проецируемый на него, что делает его доступным для использования:

. ToDisplayString ( ): возвращает строковое преобразование объекта . Это преобразование строк, которое будет отображаться в вызове dx для объекта . Можно указать описатель форматирования для форматирования выходных данных ToDisplayString. Дополнительные сведения см. в разделе Спецификаторы формата для C++ в отладчике Visual Studio.

В следующих примерах показано использование описателей формата.

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

Пример Plug and Play отладки

В этом разделе показано, как встроенные объекты отладчика, используемые с запросами LINQ, можно использовать для отладки объектов plug and play.

Просмотр данных обо всех устройствах

Используйте параметр Flatten в дереве устройств, чтобы просмотреть все устройства.

 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
...

Отображение сетки

Как и в случае с другими командами dx, вы можете выбрать и удерживать (или щелкнуть правой кнопкой мыши) команду после ее выполнения и выбрать "Отобразить как сетку" или добавить в команду "-g", чтобы получить представление результатов в сетке.

# 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]    =
...

Просмотр устройств по состоянию

Используйте параметр Где , чтобы указать определенное состояние устройства.

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

Например, чтобы просмотреть устройства в состоянии 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]            : HTREE\ROOT\0
    [0x1]            : ROOT\volmgr\0000 (volmgr)
    [0x2]            : ROOT\BasicDisplay\0000 (BasicDisplay)
    [0x3]            : ROOT\CompositeBus\0000 (CompositeBus)
    [0x4]            : ROOT\vdrvroot\0000 (vdrvroot)
...

Просмотр не запущенных устройств

Используйте эту команду для просмотра устройств, не в состоянии 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

Просмотр устройств по коду проблемы

Используйте объект DeviceNodeObject.Problem для просмотра устройств с определенными кодами проблем.

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

Например, чтобы просмотреть устройства с ненулевым кодом проблемы, используйте эту команду. Эта информация аналогична "!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)

Просмотр всех устройств без проблем

Используйте эту команду для просмотра всех устройств без проблем

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

Просмотр всех устройств с определенной проблемой

Используйте эту команду для просмотра устройств с проблемным состоянием 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)

Просмотр устройств по драйверу функции

Используйте эту команду для просмотра устройств по драйверу функции.

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

Чтобы просмотреть устройства, использующие определенный драйвер функции, например atapi, используйте эту команду.

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)

Просмотр списка драйверов запуска загрузки

Чтобы просмотреть список winload, загруженных в качестве драйверов запуска загрузки, необходимо находиться в контексте, где у вас есть доступ к LoaderBlock, а loaderBlock на ранних этапах все еще доступен. Например, во время nt! IopInitializeBootDrivers. В этом контексте точку останова можно задать для остановки.

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

С помощью оператора "??" команда для отображения структуры драйвера загрузки.

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

Используйте объект отладчика Debugger.Utility.Collections.FromListEntry для просмотра данных, используя начальный адрес структуры 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]
...

Используйте параметр -g для создания представления данных в сетке.

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

Просмотр устройств по возможностям

Просматривайте устройства по возможности с помощью объекта DeviceNodeObject.CapabilityFlags.

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

В этой таблице приведены общие сведения об использовании команды dx с общими флагами возможностей устройств.

Съемный

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 (монитор)

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 (космодром) ...

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 (космодром) ...

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 ...

Дополнительные сведения о CapabilityFlags см. в разделе DEVICE_CAPABILITIES.

См. также

dx (отображение выражения объектной модели отладчика)

Собственные объекты отладчика в NatVis

Собственные объекты отладчика в расширениях JavaScript