将 LINQ 与调试器对象配合使用

LINQ 语法可与调试器对象一起使用,以搜索和操作数据。 与使用调试器命令相比,将 LINQ 语法与 dx 命令配合使用可实现更一致的体验。 无论查看哪个调试器对象,输出和选项都是一致的。 LINQ 查询允许你提出诸如“运行线程最多的前 5 个进程是什么?”等问题。

调试器对象投影到根位于“Debugger”的命名空间中。 进程、模块、线程、堆栈、堆栈帧和局部变量都可用于 LINQ 查询。

LINQ 在概念上类似于用于查询数据库的 结构化查询语言 (SQL) 。 可以使用许多 LINQ 方法来搜索、筛选和分析调试数据。 使用 LINQ C# 方法语法。 有关 LINQ 和 LINQ C# 语法的详细信息,请参阅在 C# 中使用 LINQ 入门

调试器支持中使用的 LINQ 使用 LINQ 的“方法语法”,而不是“查询语法”。 可以在 LINQ (语言集成查询) 中找到有关差异的更多详细信息。

LINQ 命令(如下所示)可与调试器对象一起使用。 所有。任何。计数。第一。扁平 化。GroupBy, 。最后。OrderBy, 。OrderByDescending, 。选择 和 。其中。 这些方法在 C# LINQ 方法窗体) 尽可能紧密地遵循 (。

本机调试器对象

本机调试器对象表示调试器环境的各种构造和行为。 示例调试器对象包括以下内容。

  • 会话
  • 线程/线程
  • Processes / Process
  • 堆栈帧/堆栈帧
  • 局部变量
  • 模块/模块
  • 实用工具
  • 状态
  • 设置

还可以使用 NatVis 处理调试器对象。 有关详细信息,请参阅 NatVis 中的本机调试器对象。 有关将调试器对象与 JavaScript 配合使用的信息,请参阅 JavaScript 扩展中的本机调试器对象。 有关使用 C++ 和驱动程序对象的信息,请参阅 调试器数据模型 C++ 概述

Dx 命令

此处显示的示例使用 dx 命令,有关使用 dx 命令的详细信息,请参阅 dx (Display Debugger 对象模型表达式)

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

对于我们的方案,我们还需要线程数。 由于有两个字段,因此请使用 new 创建匿名类型,类似于 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
    [...]       

此示例演示即插即用设备树中的设备,这些设备按物理设备对象的驱动程序的名称分组。 并非所有输出都显示。

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 查询方法,并且适用于 lambda 的参数。

例如,键入 (或将以下文本复制并粘贴到调试器) 。 然后多次点击 TAB 键以循环完成可能的完成。

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

按 TAB 键,直到“。将显示“名称”。 添加右括号“) ”,然后按 Enter 执行命令。

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 键,直到“。显示“长度”。 添加右括号“) ”,然后按 Enter 执行命令。

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 可以利用的任何内容,例如 lambda、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

Lambda 表达式) (函数对象

用于查询数据的许多方法都基于跨集合中的对象重复运行用户提供的函数的概念。 为了支持在调试器中查询和操作数据的功能,dx 命令支持使用等效 C# 语法的 lambda 表达式。 lambda 表达式由使用 => 运算符定义,如下所示:

(参数) => (结果)

若要了解如何将 LINQ 与 dx 配合使用,请尝试此简单示例将 5 和 7 相加。

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

dx 命令回显 lambda 表达式并显示 12 的结果。

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

此示例 lambda 表达式组合了字符串“Hello”和“World”。

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

支持的 LINQ 语法 - 查询方法

dx 定义为可迭代的任何对象 (是本机数组、NatVis 编写的类型(将它描述为容器)或调试器扩展对象) 具有一系列 LINQ (或 LINQ 等效) 方法投影到该对象上。 下面介绍了这些查询方法。 查询方法的参数签名在所有查询方法之后列出。

筛选方法

。其中, ( PredicateMethod ) :返回一个新的 对象集合,其中包含谓词方法为其返回 true 的输入集合中的每个对象。

投影方法

。平展 ( [KeyProjectorMethod] ) :将容器的输入容器 (树) ,并将其平展到树中具有每个元素的单个容器中。 如果提供了可选的密钥投影仪方法,则树被视为键的容器,这些密钥本身是容器,这些键由对投影方法的调用确定。

。选择“ ( KeyProjectorMethod ) :返回对象的新集合,其中包含对输入集合中的每个对象调用投影仪方法的结果。

分组方法

。GroupBy ( KeyProjectorMethod,[KeyComparatorMethod] ) :通过将输入集合中的所有对象分组,返回新的集合集合,这些对象具有通过调用键投影仪方法确定的相同键。 可以提供可选的比较器方法。

联接 (InnerCollection、外部键选择器方法、内部键选择器方法、结果选择器方法、[ComparatorMethod]) :基于键选择器函数联接两个序列并提取值对。 还可以指定可选的比较器方法。

intersect (InnerCollection, [ComparatorMethod]) :返回集的交集,这意味着出现在两个集合中的每个元素。 还可以指定可选的比较器方法。

Union (InnerCollection, [ComparatorMethod]) :返回集联合,这意味着出现在两个集合中的任何一个中的唯一元素。 还可以指定可选的比较器方法。

数据集方法

包含 (Object, [ComparatorMethod]) :确定序列是否包含指定的元素。 可以提供一个可选的比较器方法,每次将元素与序列中的条目进行比较时都会调用该方法。

Distinct ([ComparatorMethod]) :从集合中删除重复值。 每次必须比较集合中的对象时,可以提供一个可选的比较器方法来调用。

除 (InnerCollection 外,[ComparatorMethod]) :返回集差,这意味着一个集合中未出现在另一个集合中的元素。 可以指定可选的比较器方法。

Concat (InnerCollection) :连接两个序列以形成一个序列。

排序方法

。OrderBy ( KeyProjectorMethod,[KeyComparatorMethod] ) :通过调用输入集合中每个对象的键投影方法,根据键按升序对集合进行排序。 可以提供可选的比较器方法。

。OrderByDescending ( KeyProjectorMethod, [KeyComparatorMethod] ) :根据通过调用输入集合中每个对象的键投影方法提供的键,按降序对集合进行排序。 可以提供可选的比较器方法。

聚合方法

Count () :返回集合中元素数的方法。

Sum ([ProjectionMethod]) :计算集合中值的总和。 可以选择指定投影仪方法,以在求和发生之前转换元素。

Skip 方法

跳过 (计数) :跳过序列中指定位置的元素。

SkipWhile (PredicateMethod) :跳过基于谓词函数的元素,直到元素不满足条件。

采用方法

采取 (计数) :将元素置于序列中的指定位置。

TakeWhile (PredicateMethod) :根据谓词函数获取元素,直到元素不满足条件。

比较方法

SequenceEqual (InnerCollection, [ComparatorMethod]) :通过以成对方式比较元素来确定两个序列是否相等。 可以指定可选的比较器。

错误处理方法

AllNonError (PredicateMethod) :返回集合的所有非错误元素是否满足给定条件。

FirstNonError ([PredicateMethod]) :返回集合中不是错误的第一个元素。

LastNonError ([PredicateMethod]) :返回集合中不是错误的最后一个元素。

其他方法

。所有 ( PredicateMethod ) :返回对输入集合中每个元素调用指定谓词方法的结果是否为 true。

。任何 ( PredicateMethod ) :返回对输入集合中任何元素调用指定谓词方法的结果是否为 true。

。First ( [PredicateMethod] ) :返回集合中的第一个元素。 如果传递了可选谓词,则返回集合中对谓词的调用返回 true 的第一个元素。

。上一 ( [PredicateMethod] ) :返回集合中的最后一个元素。 如果传递了可选的谓词,则返回集合中对谓词的调用返回 true 的最后一个元素。

Min ([KeyProjectorMethod]) :返回集合的最小元素。 可以指定一个可选的投影仪方法,以投影每个方法,然后再将其与其他方法进行比较。

Max ([KeyProjectorMethod]) :返回集合的最大元素。 可以指定一个可选的投影仪方法,以投影每个方法,然后再将其与其他方法进行比较。

Single ([PredicateMethod]) :如果集合包含多个元素) ,则返回列表中的唯一元素 (或错误。 如果指定了谓词,则返回满足谓词 (如果多个元素满足谓词,则函数返回错误) 。

参数的签名

KeyProjectorMethod: ( obj => 任意键 ) 获取集合的 一个 对象,并从该对象返回一个键。
KeyComparatorMethod: ( (a,b) => 整数值 ) 获取两个键并比较它们返回的返回情况:

如果 ( < b ) ,则为 -1

如果 ( a == b) ,则为 0

如果 ( b ) > ,则为 1

PredicateMethod: ( obj => 布尔值 ) 获取集合的 对象,并根据该对象是否满足特定条件返回 true 或 false。

支持的 LINQ 语法 - 字符串操作

所有字符串对象都具有以下投影方法,以便它们可供使用:

查询相关方法 & 属性

。包含 ( OtherString ) :返回一个布尔值,该值指示输入字符串是否包含 OtherString。

。EndsWith ( OtherString ) :返回一个布尔值,该值指示输入字符串是否以 OtherString 结尾。

Length:返回字符串长度的属性。

。StartsWith ( OtherString ) :返回一个布尔值,该值指示输入字符串是否以 OtherString 开头。

。子字符串 ( StartPos,[Length] ) :返回输入字符串中从给定起始位置开始的子字符串。 如果提供了可选长度,则返回的子字符串将采用指定的长度;否则 , 它将转到字符串的末尾。

其他方法

。IndexOf ( OtherString ) :返回输入字符串中 OtherString 的第一个匹配项的索引。

。LastIndexOf ( OtherString ) :返回输入字符串中 OtherString 最后一个匹配项的索引。

格式设置方法

。PadLeft ( TotalWidth ) :根据需要向字符串左侧添加空格,以使字符串的总长度达到指定的宽度。

。PadRight ( TotalWidth ) :根据需要向字符串右侧添加空格,以使字符串的总长度达到指定的宽度。

。删除 ( StartPos,[Length] ) :从输入字符串中删除字符,从指定起始位置开始。 如果提供了可选的 length 参数,则将删除该数量的字符;否则 , 将删除字符串末尾的所有字符。

。替换 ( SearchString、ReplaceString ) :将输入字符串中出现的 SearchString 替换为指定的 ReplaceString。

字符串对象投影

除了直接投影到字符串对象的方法外,任何本身具有字符串转换的对象都有以下投影方法,使其方法可供使用:

。ToDisplayString ( ) :返回 对象的字符串转换。 这是字符串转换,将在对象的 dx 调用中显示。 可以提供格式说明符来设置 ToDisplayString 输出的格式。 有关详细信息,请参阅 Visual Studio 调试器中 C++ 的格式说明符

以下示例演示了格式说明符的使用。

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

调试即插即用示例

本部分说明如何使用与 LINQ 查询一起使用的内置调试器对象来调试即插即用对象。

查看所有设备

使用设备树上的 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]    =
...

按状态查看设备

使用 Where 指定特定设备状态。

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) 。其中 (n => (n.DeviceNodeObject.CapabilityFlags & 0x10) != 0) @$cursession。Devices.DeviceTree.Flatten (n => n.Children) 。其中 (n => (n.DeviceNodeObject.CapabilityFlags & 0x10) != 0)
[0x0] : SWD\PRINTENUM{2F8DBBB6-F246-4D84-BB1D-AA8761353885} [0x1] : SWD\PRINTENUM{F210BC77-55A1-4FCA-AA80-013E2B408378} [0x2 F210BC77 :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) 。其中 (n => (n.DeviceNodeObject.CapabilityFlags & 0x40) != 0) @$cursession。Devices.DeviceTree.Flatten (n => n.Children) 。其中 (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) 。其中 (n => (n.DeviceNodeObject.CapabilityFlags & 0x80) != 0) @$cursession。Devices.DeviceTree.Flatten (n => n.Children) 。其中 (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) 。其中 (n => (n.DeviceNodeObject.CapabilityFlags & 0x100) != 0) @$cursession。Devices.DeviceTree.Flatten (n => n.Children) 。其中 (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) 。其中 (n => (n.DeviceNodeObject.CapabilityFlags & 0x200) != 0) @$cursession。Devices.DeviceTree.Flatten (n => n.Children) 。其中 (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 扩展中的本机调试器对象