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 性能和工具:理查德·麦克杜格尔、吉姆·莫罗和布兰登·格雷格的 DTrace 和 MDB 技术,适用于 Solaris 10 和 OpenSolaris
提供有关 Windows DTrace 的反馈
使用反馈中心请求新功能,或使用 Windows DTrace 报告任何问题或 bug。
- 若要在 Windows 中启动反馈中心,请转到搜索,输入字词反馈,然后选择“反馈中心”。
- 选择建议功能或报告问题。
- 提供问题或建议的详细、具体说明。
DTrace Windows 扩展
以下是 Windows 上提供的一些 Dtrace 提供程序及其检测内容。
syscall – NTOS 系统调用。
fbt (函数边界跟踪) - 内核函数条目和返回。
pid(进程 ID)- 用户模式进程跟踪。 与内核模式 FBT 一样,还允许检测任意函数偏移量。
etw (Windows 事件跟踪) - 允许为 ETW 定义探测。 此提供程序有助于利用 DTrace 中的现有操作系统检测。
SYSCALL – NTOS 系统调用
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 内核在堆栈查核行程或内存访问期间可提供标注,而跟踪扩展随后会实现这些标注。
在 Windows 下安装 DTrace
检查是否正在运行受支持的 Windows 版本。 版本 18980 和 Windows Server 内部版本 18975 之后的 Windows 20H1 Insider 内部版本支持 DTrace 的当前下载。 在较旧版本的 Windows 上安装此版本的 DTrace 可能会导致系统不稳定,因此不推荐。 (适用于 19H1 的 DTrace 存档版本已不再可用,且不再受支持。)
从 Microsoft 下载中心下载 MSI 安装文件(在 Windows 上下载 DTrace)。
选择“完成安装”。
重要
在使用 bcdedit 更改启动信息之前,可能需要在测试电脑上暂时挂起 Windows 安全功能,例如 Patchguard、BitLocker 和安全启动。 在已禁用这些安全功能的情况下,在测试完成后重新启用这些安全功能,并适当地管理测试电脑。
更新 PATH 环境变量以包括 C:\Program Files\DTrace
set PATH=%PATH%;"C:\Program Files\DTrace"
- 使用 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