使用英语阅读

通过


调试器数据模型函数别名

函数别名是一个唯一的短名称,调试器用户可以访问调试器扩展中定义的功能(无论是用 C++ 编写还是使用 JavaScript 等脚本环境编写)。 此短名称与数据模型函数对象(实现 IModelMethod 的对象)相关联。 该函数采用任意数量的参数并返回单个值。 调用函数别名以及使用该值执行的作取决于调用函数别名的方式及其在其中调用的主机调试器。

本主题假定读者熟悉调试器对象模型和 JavaScript。 有关将调试器对象与 JavaScript 配合使用的信息,请参阅 javaScript 扩展 中的本机调试器对象。

此处所示的一些示例使用 dx 命令,有关使用 dx 命令的详细信息,请参阅 dx(显示调试器对象模型表达式)。 此外,使用 LINQ,使用 LINQ 和调试器对象

使用函数别名作为扩展命令

可以在 WinDbg 中创建的所有函数别名调用,就像它们是调试扩展 “bang”命令。 如果函数不采用任何参数,只需调用 !aliasName 将导致调用该函数并显示结果值。 例如(使用 JavaScript 扩展性创建)

例如,此函数提供两个常量值,pie

function __constants()
{
    return { math_constants : { pi :  3.1415926535 , e :  2.7182818284}, description : "Archimedes' pi and Euler's number e" };
}

function initializeScript()
{
    return [new host.functionAlias(__constants, "constants")];
}

此处显示了调用函数别名的结果。

0:000> !constants
@$constants()                 : [object Object]
    math_constants   : [object Object]
    description      : Archimedes' pi and Euler's number e

将自动生成指向复杂对象的 DML 链接。 单击上面所示的math_constants将导致以下输出。

0:000> dx -r1 @$constants().math_constants
@$constants().math_constants                 : [object Object]
    pi               : 3.141593
    e                : 2.718282

如果函数具有参数,则可以在函数别名命令本身之后提供它们。 请注意,函数别名命令后的任何作都被视为表达式,并按此类计算。 文本字符串不会直接传递给函数。 对于单个参数表达式,它可以在函数别名命令本身之后。 对于多个参数,应将其括号括起来,就像它是函数调用一样,如下例所示。

function __oneArgument(x)
{
    return -x;
}

function __twoArguments(x, y)
{
    return x + y;
}

function initializeScript()
{
    return [new host.functionAlias(__oneArgument, "neg"),
            new host.functionAlias(__twoArguments, "add")];
}

可以调用这两个函数,如下所示。

0:000> !neg 42
@$neg(42)        : -42

0:000> !add (5, 7)
@$add(5, 7)      : 0xc

函数别名与 dx 表达式计算器一起使用

除了调试扩展 用于调用别名函数的“bang”命令语法,当以 @$ 为前缀时,与函数别名关联的所有名称都直接在 dx 表达式计算器中可用,如下所示。

0:000> dx @$neg(42)
@$neg(42)        : -42

0:000> dx @$add(99, 77)
@$add(99, 77)    : 0xb0

函数别名设计注意事项

函数别名不应是唯一一种在绝大多数数据模型扩展中公开功能的方式。 数据模型扩展插件(无论是在 C++ 或 JavaScript 中),应该几乎总是包含它公开的数据,它与类型或其他调试器概念相关联。 与进程关联的内容应属于 Debugger.Models.Process 或该对象的子命名空间。 函数别名是获取(或转换)可能需要明显更长查询的数据的便捷方法。

作为内核模式示例,以下查询采用 PnP 设备树并将其平展为简单的设备列表:

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

此 JavaScript 代码演示如何将其实现为函数别名。

function __flatDevices()
{
    return host.currentSession.Devices.DeviceTree.Flatten(n => n.Children);
}

function initializeScript()
{
    return [new host.functionAlias(__flatDevices, "devices")];
}

然后,可以将函数别名作为调试扩展命令调用。

0: kd> !devices
@$devices()                
    [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)

    ...

使用函数别名的优点之一是,可以使用 dx 语法进一步优化它。 在此示例中,添加了 where 子句以查找包含“Harddisk”的设备节点。

0: kd> dx @$devices().Where(n => n.InstancePath.Contains("Harddisk"))
@$devices().Where(n => n.InstancePath.Contains("Harddisk"))                
    [0x0]            : STORAGE\VolumeSnapshot\HarddiskVolumeSnapshot1
    [0x1]            : STORAGE\VolumeSnapshot\HarddiskVolumeSnapshot2
    [0x2]            : STORAGE\VolumeSnapshot\HarddiskVolumeSnapshot3
    [0x3]            : STORAGE\VolumeSnapshot\HarddiskVolumeSnapshot4
    [0x4]            : STORAGE\VolumeSnapshot\HarddiskVolumeSnapshot5
    [0x5]            : STORAGE\VolumeSnapshot\HarddiskVolumeSnapshot6

LINQ 命令(如以下内容)可用于功能别名 - 。都。任何。计数。第一。扁平 化。GroupBy, .最后。OrderBy, .OrderByDescending, .选择和 。哪里。 这些方法遵循 C# LINQ 方法形式(尽可能接近)。 有关详细信息,请参阅 将 LINQ 与调试器对象

网格显示

与其他 dx 命令一样,可以在执行命令后右键单击命令,然后单击“显示为网格”或向命令添加“-g”以获取结果的网格视图。 然后,可以单击任意列进行排序,例如,在 InstancePath 上。

0: kd> dx -g @$devices().OrderBy(obj => obj.@"InstancePath")

调试器对象函数别名网格输出的屏幕截图,其中包含已排序的行。

进程线程示例

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

此示例 JavaScript 演示如何显示当前会话进程的线程计数:

function __Processes()
{
    return host.currentSession.Processes.Select(p => ({Name: p.Name, ThreadCount: p.Threads.Count()}));
}

function initializeScript()
{
    return [new host.functionAlias(__Processes, "Processes")];
}

这显示了带有 ! 的示例输出处理函数别名。

0: kd> !Processes
@$Processes()                
    [0x0]            : [object Object]
    [0x4]            : [object Object]
    [0x1b4]          : [object Object]
    [0x248]          : [object Object]
    [0x2c4]          : [object Object]
    [0x340]          : [object Object]
    [0x350]          : [object Object]
    [0x3d4]          : [object Object]
    [0x3e8]          : [object Object]
    [0x4c]           : [object Object]
    [0x214]          : [object Object]
    [0x41c]          : [object Object]
    [0x494]          : [object Object]

...    

在此示例中,将显示具有最大线程计数的前 5 个进程。

0: kd> dx -r1 @$Processes().OrderByDescending(p =>p.ThreadCount),5
@$Processes().OrderByDescending(p =>p.ThreadCount),5                
    [0x4]            : [object Object]
    [0x180]          : [object Object]
    [0x978]          : [object Object]
    [0xda4]          : [object Object]
    [0x3e8]          : [object Object]
    [...]   

另请参阅

dx (显示调试器对象模型表达式)

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

在 NatVis 本机调试器对象

JavaScript 扩展中的本机调试器对象