Windows 上的 DTrace

DTrace (DTrace.exe) 是一个命令行工具,用于显示系统信息和事件。 DTrace 是移植到 Windows 的一种开放源代码跟踪平台。 DTrace 最初是为 Solaris 操作系统而开发的。 它提供用户/内核函数的动态检测、使用 D 语言来编写脚本以及推理跟踪功能。 此外,DTrace 具有特定于 Windows OS 的扩展,例如 ETW 检测、ETW 事件生成、系统调用探测和实时转储捕获功能。

注意

版本 18980 和 Windows Server 内部版本 18975 之后的 Windows Insider 内部版本支持 DTrace。

Windows GitHub 站点上的 DTrace 位于以下位置:

https://github.com/microsoft/DTrace-on-Windows

打开 DTrace 信息

有关 DTrace 的详细信息,请参阅剑桥大学的 OpenDTrace 规范版本 1.0

主要 GitHub 站点位于 https://github.com/opendtrace/

https://github.com/opendtrace/toolkit 提供了一组实用脚本。

提供大量 DTrace 书籍,例如:

DTrace: Dynamic Tracing in Oracle Solaris, Mac OS X and FreeBSD,作者:Brendan Gregg 和 Jim Mauro

Solaris Performance and Tools: DTrace and MDB Techniques for Solaris 10 and OpenSolaris,作者:Richard McDougall、Jim Mauro 和 Brendan Gregg

提供有关 Windows DTrace 的反馈

使用反馈中心请求新功能,或是通过 Windows DTrace 报告任意问题或 bug。

  1. 启动反馈中心。 转到搜索,输入反馈一词,然后选择反馈中心
  2. 选择建议功能报告问题
  3. 提供问题或建议的详细、具体说明。

DTrace Windows 扩展

Windows 上提供的某些提供程序及其检测内容如下。

  • syscall – NTOS 系统调用

  • fbt(函数边界跟踪)- 内核函数条目和返回

  • pid(进程 ID)- 用户模式进程跟踪。 与内核模式 FBT 一样,但还允许检测任意函数偏移量。

  • etw(适用于 Windows 的事件跟踪)- 允许为 ETW 定义探测,而此提供程序有助于利用 DTrace 中的现有操作系统检测。

SYSCALL

SYSCALL 可为每个系统调用提供一对探测:其中一个为入口探测,它可在进入系统调用之前触发;另一个则为返回探测,它可在系统调用已完成但将控制转移回用户级别之前来触发。 对于所有 SYSCALL 探测,函数名称均会设为已检测系统调用的名称,而模块名称则是函数所在的模块。 可通过从命令提示符键入命令 dtrace.exe -l -P syscall 来查找由 SYSCALL 提供程序提供的系统调用的名称。 请注意,探测名称为小写的 syscall。 dtrace -ln syscall::: 命令还会列出 syscall 提供程序提供的所有探测及其参数。

C:\> dtrace -ln syscall:::
  ID   PROVIDER            MODULE                          FUNCTION NAME
    6    syscall                                 NtWaitHighEventPair entry
    7    syscall                                 NtWaitHighEventPair return
    8    syscall                       NtRegisterThreadTerminatePort entry
    9    syscall                       NtRegisterThreadTerminatePort return
...

请注意,这些示例并未显示所有屏幕输出。 “...”被用于表示截断的输出。

若要滚动查看整个输出,请按如下所示并通过管道输出到 more 命令:

dtrace -ln syscall:::|more

添加 v 选项可显示有关可用 syscall 探测的详细信息。

C:\> dtrace -lvn syscall:::
...

  942    syscall                                    NtSaveMergedKeys entry

        Probe Description Attributes
                Identifier Names: Private
                Data Semantics:   Private
                Dependency Class: ISA

        Argument Attributes
                Identifier Names: Private
                Data Semantics:   Private
                Dependency Class: ISA

        Argument Types
                args[0]: HANDLE
                args[1]: HANDLE
                args[2]: HANDLE
...

ETW

DTrace 包括对现有已列入清单/已记录到跟踪日志的 ETW 探测的支持。 可在事件触发时同步检测、筛选和分析 ETW 事件。 此外,DTrace 还可用于合并各种事件/系统状态,从而提供合并的输出流来帮助调试复杂的错误情况。

dtrace -ln etw::: 命令会列出 syscall 提供程序提供的所有探测及其参数。

  C:\> dtrace -ln etw:::
  ID   PROVIDER            MODULE                          FUNCTION NAME
  944        etw 048dc470-37c1-52a8-565a-54cb27be37ec           0xff_0xffffffffffffffff generic_event
  945        etw aab97afe-deaf-5882-1e3b-d7210f059dc1           0xff_0xffffffffffffffff generic_event
  946        etw b0f40491-9ea6-5fd5-ccb1-0ec63be8b674           0xff_0xffffffffffffffff generic_event
  947        etw 4ee869fa-9954-4b90-9a62-308c74f99d32           0xff_0xffffffffffffffff generic_event
  ...

有关详细信息,请参阅 DTrace ETW

函数边界跟踪 (FBT)

函数边界跟踪 (FBT) 提供程序可提供与 Windows 内核中大多数函数的入口和返回相关联的探测。 此函数为程序文本的基本单元。 与其他 DTrace 提供程序类似,如果未显式启用 FBT,则无探测效果。 启用后,FBT 只会在被探测函数中引入探测效果。 FBT 已在 x86 和 x64 平台上实现。

对于每个指令集,均有少量函数不会调用其他函数且由编译器(所谓的叶函数)进行高度优化,同时 FBT 无法检测这些函数。 DTrace 中不存在针对这些函数的探测。

dtrace -ln fbt:nt:: 命令会列出可用于 nt 模块的所有探测及其参数。 使用调试器 lm(列出已加载模块)命令可列出所有可用模块。

C:\>dtrace -ln "fbt:nt::"
   ID   PROVIDER            MODULE                          FUNCTION NAME
 3336        fbt                nt                PiDqActionDataFree entry
 3337        fbt                nt                PiDqActionDataFree return
 3338        fbt                nt PiDqActionDataGetRequestedProperties entry
 3339        fbt                nt PiDqActionDataGetRequestedProperties return
 3340        fbt                nt _CmGetMatchingFilteredDeviceInterfaceList entry
...

注意

由于 nt 中提供了数千个调用,因此在运行用于记录数据的 DTrace 命令时,不建议将函数名称留空。 避免潜在性能影响的建议方法是:至少指定函数名称的一部分,例如 fbt:nt:*Timer*:entry

PID

使用 DTrace PID 提供程序可跟踪用户模式进程(如 Web 浏览器或数据库)的内部执行情况。 此外,还可在进程启动时附加 DTrace,以便调试进程启动问题。 作为 PID 定义的一部分,可指定在进程中定义的函数以及函数内的特定偏移量(或是,使用通配符 (*) 的所有偏移量)。 PID 提供程序要求在脚本执行时启动或运行二进制文件。

此示例命令显示了有关与 notepad.exe 关联的 PID 中的特定调用的信息。 使用调试器 lm(列出已加载模块)命令可列出所有可用模块。

C:\Windows\system32>dtrace -ln "pid$target:ntdll:RtlAllocateHeap:entry" -c notepad.exe
   ID   PROVIDER            MODULE                          FUNCTION NAME
 5102    pid6100             ntdll                   RtlAllocateHeap entry

注意

跟踪 C++ 所编写的函数时,函数名称可能会过长或经过修饰,从而无法指定为具有完整形式的探测。 常见的解决方案是使用能唯一匹配目标函数的表达式。 例如,使用“String??Copy“以作为探测名称的”probefunc“部分从而匹配”String::Copy()“,或是使用”*GetPinnableReference“以匹配”String::GetPinnableReference()”。

DTrace Windows 体系结构

用户可通过 DTrace 命令与 DTrace 交互,而该命令会充当 DTrace 引擎的前端。 D 脚本会在用户空间中编译为中间格式 (DIF) 并发送到 DTrace 内核组件以供执行,而它有时也被称为 DIF 虚拟机。 它会在 dtrace.sys 驱动程序中运行。

Traceext.sys(跟踪扩展)是一种 Windows 内核扩展驱动程序,它允许 Windows 公开 DTrace 依赖其来提供跟踪的功能。 Windows 内核在堆栈查核行程或内存访问期间可提供标注,而跟踪扩展随后会实现这些标注。

该图显示了 dtrace.exe 连接到 libtrace 的 DTrace Windows 体系结构,该体系结构与 DTrace.sys 通信并调用 Traceext.sys。

在 Windows 下安装 DTrace

  1. 检查是否正在运行受支持的 Windows 版本。 版本 18980 和 Windows Server 内部版本 18975 之后的 Windows 20H1 Insider 内部版本支持 DTrace 的当前下载。 在较旧版本的 Windows 上安装此版本的 DTrace 可能会导致系统不稳定,因此不推荐。 (适用于 19H1 的 DTrace 存档版本已不再可用,且不再受支持。)

  2. 从 Microsoft 下载中心下载 MSI 安装文件(在 Windows 上下载 DTrace)。

  3. 选择“完成安装”。

    重要

    在使用 bcdedit 更改启动信息之前,可能需要在测试电脑上暂时挂起 Windows 安全功能,例如 Patchguard、BitLocker 和安全启动。 在已禁用这些安全功能的情况下,在测试完成后重新启用这些安全功能,并适当地管理测试电脑。

  4. 更新 PATH 环境变量以包括 C:\Program Files\DTrace

set PATH=%PATH%;"C:\Program Files\DTrace"
  1. 使用 bcdedit 命令在此计算机上启用 DTrace。
bcdedit /set dtrace ON

更新到新的 Windows Insider 内部版本时,需再次设置 dtrace bcdedit 选项。

注意

如果使用的是 BitLocker,则请在更改启动值时将其禁用。 否则,系统可能会提示输入 BitLocker 恢复密钥。 从此情况中恢复的其中一种方法是启动到恢复控制台并还原 bcdedit 值 bcdedit /set {default} dtrace on。 如果 OS 更新已删除该值而你重新将其添加到其中,则应使用 bcdedit 来删除值 bcdedit /deletevalue {default} dtrace,从而恢复 OS。 然后,禁用 BitLocker 并运行 bcdedit /set dtrace ON 来重新启用 dtrace。

通过将“HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceGuard\EnableVirtualizationBasedSecurity”设为 1 来启用 VSM 和安全内核,从而在计算机上配置 VSM(虚拟安全模式),以便启用内核函数边界跟踪 (FBT)。

为此,请使用 REG Add 命令,如下所示:

REG ADD HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceGuard\ /v EnableVirtualizationBasedSecurity /t REG_DWORD /d 1

某些 DTrace 命令会使用 Windows 符号。 若要使用 Windows 符号,请创建符号目录并设置符号路径:

mkdir c:\symbols
set _NT_SYMBOL_PATH=srv*C:\symbols*https://msdl.microsoft.com/download/symbols

有关符号路径的详细信息,请参阅 Windows 调试器的符号路径

在虚拟机中使用 DTrace

若在 VM 上运行 DTrace,则请使用以下 PowerShell 命令在支持 VM 的计算机上启用嵌套虚拟化。 为在其中运行 DTrace 的 VM 提供 <VMName>。 以管理员身份打开 PowerShell 窗口。

Set-VMProcessor -VMName <VMName> -ExposeVirtualizationExtensions $true

重新启动支持 VM 的电脑。

验证 DTrace 安装

使用 -l 选项可列出活动探测。 如果 DTrace 处于活动状态,则应为 etw 和系统事件列出大量探测。

以管理员身份打开 Windows 命令提示符,以便输入 DTrace 命令。

C:\> dtrace -l

...

  179    syscall                                 NtLockVirtualMemory return
  180    syscall                               NtDeviceIoControlFile entry
  181    syscall                               NtDeviceIoControlFile return
  182    syscall                                 NtCreateUserProcess entry
  183    syscall                                 NtCreateUserProcess return
  184    syscall                                      NtQuerySection entry
  185    syscall                                      NtQuerySection return

...

 3161        etw 222962ab-6180-4b88-a825-346b75f2a24a           0xff_0xffffffffffffffff generic_event
 3162        etw 3ac66736-cc59-4cff-8115-8df50e39816b           0xff_0xffffffffffffffff generic_event
 3163        etw 42695762-ea50-497a-9068-5cbbb35e0b95           0xff_0xffffffffffffffff generic_event
 3164        etw 3beef58a-6e0f-445d-b2a4-37ab737bd47e           0xff_0xffffffffffffffff generic_event

...

如果仅列出了这三个探测,则当前所加载的 DTrace.sys 驱动程序出现问题。

C:\>  dtrace -l
   ID   PROVIDER            MODULE                          FUNCTION NAME
    1     dtrace                                                     BEGIN
    2     dtrace                                                     END
    3     dtrace                                                     ERROR

DTrace 入门 - 单行命令

首先,从管理员命令提示符运行这些命令。

此命令会按程序显示 syscall 摘要并持续 5 秒钟。 tick-5sec 参数会指定时间段。 exit(0); 可让该命令在完成后退出,并返回到命令提示符。 使用 [pid,execname] = count(); 可指定输出。它会显示进程 ID (PID)、可执行文件名称和某一计数并持续 5 秒钟。

C:\> dtrace -Fn "tick-5sec {exit(0);} syscall:::entry{ @num[pid,execname] = count();} "  
dtrace: description 'tick-5sec ' matched 471 probes
CPU FUNCTION
  0 | :tick-5sec

     1792  svchost.exe                                                       4
     4684  explorer.exe                                                      4
     4916  dllhost.exe                                                       4
     6192  svchost.exe                                                       4
     6644  SecurityHealth                                                    4
       92  TrustedInstall                                                    5
      504  csrss.exe                                                         5
      696  svchost.exe                                                       6
...

以下命令会汇总计时器设置/取消调用,并持续 3 秒钟:

C:\> dtrace -Fn "tick-3sec {exit(0);} syscall::Nt*Timer*:entry { @[probefunc, execname, pid] = count();}"
dtrace: description 'tick-3sec ' matched 14 probes
CPU FUNCTION
  0 | :tick-3sec

  NtCreateTimer                                       WmiPrvSE.exe                                            948                1
  NtCreateTimer                                       svchost.exe                                             564                1
  NtCreateTimer                                       svchost.exe                                            1276                1
  NtSetTimer2                                         svchost.exe                                            1076                1
  NtSetTimer2                                         svchost.exe                                            7080                1
  NtSetTimerEx                                        WmiPrvSE.exe                                            948                1
...  

使用符号的单行命令

这些命令会利用 Windows 符号,并要求按安装部分的所述方法来设置符号路径。 按先前在安装中提及的方法创建一个目录,并使用以下命令设置符号路径。

C:\> mkdir c:\symbols
C:\> set _NT_SYMBOL_PATH=srv*C:\symbols*https://msdl.microsoft.com/download/symbols

此示例命令会显示顶级 NT 函数。

C:\> dtrace -n "fbt:nt:*Timer*:entry { @k[probefunc] = count(); } tick-5s { trunc(@k, 10);printa(@k); exit(0); }"
dtrace: description 'fbt:nt:*Timer*:entry ' matched 340 probes
CPU     ID                    FUNCTION:NAME
  0  22362                         :tick-5s
  KeCancelTimer                                                   712
  KeSetTimer2                                                     714
  HalpTimerClearProblem                                           908
  ExpSetTimerObject                                               935
  NtSetTimerEx                                                    935
  KeSetTimer                                                     1139
  KeSetCoalescableTimer                                          3159
  KeResumeClockTimerFromIdle                                    11767
  xHalTimerOnlyClockInterruptPending                            22819
  xHalTimerQueryAndResetRtcErrors                               22819

此命令会转储 SystemProcess 内核结构。

C:\> dtrace -n "BEGIN {print(*(struct nt`_EPROCESS *) nt`PsInitialSystemProcess);exit(0);}"

...

   uint64_t ParentSecurityDomain = 0
    void *CoverageSamplerContext = 0
    void *MmHotPatchContext = 0
    union _PS_PROCESS_CONCURRENCY_COUNT ExpectedConcurrencyCount = {
         Fraction :20 = 0
         Count :12 = 0
        uint32_t AllFields = 0
    }
    struct _KAFFINITY_EX IdealProcessorSets = {
        uint16_t Count = 0x1
        uint16_t Size = 0x20
        uint32_t Reserved = 0
        uint64_t [32] Bitmap = [ 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
    }
}

此命令会显示过去 10 秒的顶级内核堆栈。

C:\> dtrace -qn "profile-997hz { @[stack()] = count(); } tick-10sec { trunc(@,5); printa(@); exit(0);}"

              nt`KiDispatchInterruptContinue
              nt`KiDpcInterrupt+0x318
              nt`KiSwapThread+0x1054
              nt`KiCommitThreadWait+0x153
              nt`KeRemoveQueueEx+0x263
              nt`IoRemoveIoCompletion+0x54
              nt`NtWaitForWorkViaWorkerFactory+0x284
              nt`KiSystemServiceCopyEnd+0x35
               14

              nt`KiDispatchInterruptContinue
              nt`KiDpcInterrupt+0x318
...

此命令会显示启动期间 notepad.exe 所调用的顶级模块。 -c 选项会运行指定的命令 (notepad.exe),并在完成后退出。

C:\> dtrace -qn "pid$target:::entry { @k[probemod] = count();} tick-10s{printa(@k); exit(0);}" -c notepad.exe

  gdi32full                                                         5
  msvcp_win                                                         6
  combase                                                           7
  notepad                                                           9
  ADVAPI32                                                         10
  GDI32                                                            11
  SHELL32                                                          11
  USER32                                                           21
  win32u                                                          345
  KERNELBASE                                                     3727
  msvcrt                                                         7749
  KERNEL32                                                       9883
  RPCRT4                                                        11710
  ntdll                                                        383445

另请参阅

DTrace Windows 编程

DTrace ETW

DTrace 实时转储

DTrace Windows 代码示例