使用适用于 Windows 的 DTrace 处理现有 ETW 事件并添加新的 ETW 事件。
Windows 事件跟踪(ETW)是一种内核级跟踪功能,可用于将内核或应用程序定义的事件记录到日志文件。 可以实时处理事件或来自日志文件的事件,并利用这些事件来调试应用程序或确定应用程序中性能问题出现的地方。 有关 ETW 的一般信息,请参阅 “关于事件跟踪”。
注释
Windows 和 Windows Server 的内部版本在 18980 版本之后以及 18975 版本之后支持 DTrace。
有关在 Windows 上使用 DTrace 的常规信息,请参阅 DTrace。
ETW Windows DTrace 供应商
可以使用 DTrace 捕获和报告记录的跟踪事件以及基于清单的 ETW 事件。 若要探测特定的关键字/级别/eventID,如果不使用通配符,ETW 探测将更可靠地工作。 请根据以下规则详细定义您的探测器:
Probename = etw
Modname = 提供程序 guid,格式为 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,使用所有小写字符。
Funcname = 格式为0x00_0x0000000000000000的Level_Keyword。 若要匹配所有内容,应将其设置为0xff_0xffffffffffffffff。
Probename = 整数事件 ID 或“generic_event”,以匹配所有事件 ID。
基于 Probename 的筛选仅适用于已记录的事件。 对跟踪记录的事件使用通配符 * 。
ETW 有效负载通过 arg0 参数进行访问。 这由 nt`_EVENT_HEADER 组成,后面跟着事件的具体日期。
确定可用的 ETW 提供程序
使用 logman 命令显示活动的 ETW 提供程序及其提供程序 GUID。
C:\>logman query providers
...
Microsoft-Windows-Kernel-Memory {D1D93EF7-E1F2-4F45-9943-03D245FE6C00}
Microsoft-Windows-Kernel-Network {7DD42A49-5329-4832-8DFD-43D979153A88}
Microsoft-Windows-Kernel-PnP {9C205A39-1250-487D-ABD7-E831C6290539}
Microsoft-Windows-Kernel-Power {331C3B3A-2005-44C2-AC5E-77220C37D6B4}
Microsoft-Windows-Kernel-Prefetch {5322D61A-9EFA-4BC3-A3F9-14BE95C144F8}
Microsoft-Windows-Kernel-Process {22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}
...
显示现有的 ETW 提供程序信息
DTrace 能够输出 ETW 事件。 在已有 ETW 管道用于报告、收集和分析的场景中,这非常有用。
使用此示例 DTrace 命令报告 Microsoft-Windows-Kernel-Memory 提供程序事件。
C:\>dtrace -n "etw:d1d93ef7-e1f2-4f45-9943-03d245fe6c00:0xff_0xffffffffffffffff:12"
dtrace: description 'etw:d1d93ef7-e1f2-4f45-9943-03d245fe6c00:0xff_0xffffffffffffffff:12' matched 1 probe
CPU ID FUNCTION:NAME
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
0 3271 0xff_0xffffffffffffffff:12
添加新的 ETW 事件
可以通过调用etw_trace宏来创建 Etw 跟踪事件。 仅当指定的跟踪提供程序有活动侦听器时,才会记录事件,否则将跳过这些事件。
etw_trace宏支持基本数据类型,例如 int8、uint8、int16、uint16、int32、uint32、int64、uint64、hexint32、hexint64 和 string。 有关更多详细信息,请参阅下面的 支持的 ETW 数据类型 表。
示例ETW_TRACE宏:
当 syscall 例程返回0xc0000001 - STATUS_UNSUCCESSFUL时,此脚本将生成自定义 ETW 事件。
可以更改 this->status 的值,以使用不同的 NTSTATUS 值 来记录不同的系统调用 (syscall) 返回值。
syscall:::return
{
this->status = (uint32_t) arg0;
if (this->status == 0xc0000001UL)
{
etw_trace
(
"Tools.DTrace.Platform", /* Provider Name */
"AAD330CC-4BB9-588A-B252-08276853AF02", /* Provider GUID */
"My custom event from DTrace", /* Event Name */
1, /* Event Level (0 - 5) */
0x0000000000000020, /* Flag */
"etw_int32", /* Field_1 Name */
"PID",/* Field_1 Type */
(int32_t)pid, /* Field_1 Value */
"etw_string", /* Field_2 Name */
"Execname", /* Field_2 type */
execname, /* Field_2 Value */
"etw_string", /* Field_3 Name */
"Probefunc", /* Field_3 type */
probefunc /* Field_3 Value */
);
}
}
C:\> dtrace -s addnewetwevent.d
dtrace: script 'addnewetwevent.d' matched 1881 probes
CPU ID FUNCTION:NAME
0 93 NtAlpcSendWaitReceivePort:return
0 93 NtAlpcSendWaitReceivePort:return
0 93 NtAlpcSendWaitReceivePort:return
ETW NUMA MEM STATS 示例代码
此示例脚本使用 Microsoft-Windows-Kernel-Memory ETW 提供程序来导出 NUMA 节点内存。 可以通过乘以 4 将页面大小转换为 KB 大小。 有关 NUMA 的一般信息,请参阅 NUMA 支持。
此代码也位于 https://github.com/microsoft/DTrace-on-Windows/blob/windows/samples/windows/etw/numamemstats.d
typedef struct KernelMemInfoEvent
{
struct nt`_EVENT_HEADER _EH;
uint32_t PartitionId;
uint32_t Count;
uint32_t NodeNumber;
}kmi;
typedef struct MemoryNodeInfo
{
uint64_t TotalPageCount;
uint64_t SmallFreePageCount;
uint64_t SmallZeroPageCount;
uint64_t MediumFreePageCount;
uint64_t MediumZeroPageCount;
uint64_t LargeFreePageCount;
uint64_t LargeZeroPageCount;
uint64_t HugeFreePageCount;
uint64_t HugeZeroPageCount;
}m_nodeinfo;
int printcounter;
BEGIN
{
printcounter = 0;
}
/* MemNodeInfo */
etw:d1d93ef7-e1f2-4f45-9943-03d245fe6c00:0xff_0xffffffffffffffff:12
{
if (printcounter%10 == 0)
{
printf ("\n \n");
printf("Partition ID: %d \n",((kmi *)arg0)->PartitionId);
printf("Count: %d \n", ((kmi *)arg0)->Count);
printf("Node number: %d\n", ((kmi *)arg0)->NodeNumber);
counters = (m_nodeinfo*)(arg0 + sizeof(struct nt`_EVENT_HEADER) + 12);
print(*counters);
/* Dump rest of the NUMA node info */
if (((kmi *)arg0)->Count > 1)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(1)) + (sizeof(uint32_t)*(1)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(1)) + (sizeof(uint32_t)*(1)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 2)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(2)) + (sizeof(uint32_t)*(2)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(2)) + (sizeof(uint32_t)*(2)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 3)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(3)) + (sizeof(uint32_t)*(3)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(3)) + (sizeof(uint32_t)*(3)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 4)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(4)) + (sizeof(uint32_t)*(4)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(4)) + (sizeof(uint32_t)*(4)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 5)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(5)) + (sizeof(uint32_t)*(5)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(5)) + (sizeof(uint32_t)*(5)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 6)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(6)) + (sizeof(uint32_t)*(6)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(6)) + (sizeof(uint32_t)*(6)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 7)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(7)) + (sizeof(uint32_t)*(7)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(7)) + (sizeof(uint32_t)*(7)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 8)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(8)) + (sizeof(uint32_t)*(8)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(8)) + (sizeof(uint32_t)*(8)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 9)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(9)) + (sizeof(uint32_t)*(9)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(9)) + (sizeof(uint32_t)*(9)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 10)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(10)) + (sizeof(uint32_t)*(10)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(10)) + (sizeof(uint32_t)*(10)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 11)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(11)) + (sizeof(uint32_t)*(11)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(11)) + (sizeof(uint32_t)*(11)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 12)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(12)) + (sizeof(uint32_t)*(12)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(12)) + (sizeof(uint32_t)*(12)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 13)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(13)) + (sizeof(uint32_t)*(13)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(13)) + (sizeof(uint32_t)*(13)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 14)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(14)) + (sizeof(uint32_t)*(14)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(14)) + (sizeof(uint32_t)*(14)) + sizeof(uint32_t));
print(*counters);
}
if (((kmi *)arg0)->Count > 15)
{
nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(15)) + (sizeof(uint32_t)*(15)) );
printf ("Node Number: %d \n", *nodenumber);
counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(15)) + (sizeof(uint32_t)*(15)) + sizeof(uint32_t));
print(*counters);
}
}
exit(1);
printcounter++;
}
将文件另存为 etwnumamemstats.d
以管理员身份打开命令提示符,并使用 -s 选项运行脚本。
在客户端的 Windows 电脑上运行时,将显示一个单一的 NUMA 节点。
C:\> dtrace -s etwnumamemstats.d
trace: script 'etwnumamemstats.d' matched 36 probes
CPU ID FUNCTION:NAME
0 42735 0xff_0xffffffffffffffff:12
Partition ID: 0
Count: 1
Node number: 1
m_nodeinfo {
uint64_t TotalPageCount = 0xab98d
uint64_t SmallFreePageCount = 0
uint64_t SmallZeroPageCount = 0x1bec
uint64_t MediumFreePageCount = 0
uint64_t MediumZeroPageCount = 0x5a
uint64_t LargeFreePageCount = 0
uint64_t LargeZeroPageCount = 0
uint64_t HugeFreePageCount = 0
uint64_t HugeZeroPageCount = 0
}
0 42735 0xff_0xffffffffffffffff:12
支持的 ETW 数据类型
| ETW 类型 | D 语言数据类型 | 备注 |
|---|---|---|
| etw_struct | 整数 | 此类型的有效负载值表示新结构的成员数。 |
| etw_string | 字符串 | 无 |
| etw_mbcsstring | 字符串 | 无 |
| etw_int8 | 整数 | 类型大小应匹配,建议在 D 脚本中将其转换为“int8_t”类型。 |
| etw_uint8 | 整数 | 应匹配类型大小,建议在 D 脚本中转换为“uint8_t” |
| etw_int16 | 整数 | 应确保类型大小匹配,并建议在 D 脚本中转换为“int16_t” |
| etw_uint16 | 整数 | 类型大小应匹配,建议在 D 脚本中将其转换为“uint16_t” |
| etw_int32 | 整数 | 无 |
| etw_uint32 | 整数 | 无 |
| etw_int64 | 整数 | 类型必须显式为“int64_t”,因为 D 默认为“int32_t” |
| etw_uint64 | 整数 | 类型必须显式为“int64_t”,因为 D 默认为“int32_t” |
| etw_float | 标量 | D 脚本中不允许使用浮点常量,但在加载的符号上允许使用该常量。 |
| etw_double | 标量 | D 脚本中不允许使用浮点常量,但在加载的符号上则允许使用。 |
| etw_bool32 | 整数 | 无 |
| etw_hexint32 | 整数 | 无 |
| etw_hexint64 | 整数 | 类型必须显式为“int64_t”,因为 D 默认为“int32_t” |
| etw_countedmbcsstring | 整数 | 无 |
| etw_intptr | 整数 | 数据类型大小根据体系结构更改(“int32_t”与“int64_t”) |
| etw_uintptr | 整数 | 数据类型大小根据体系结构更改(“int32_t”与“int64_t”) |
| etw_pointer | 整数 | 数据类型大小根据体系结构更改(“int32_t”与“int64_t”) |
| etw_char16 | 整数 | 类型大小应保持一致,建议在 D 脚本中将类型转换为“int16_t” |
| etw_char8 | 整数 | 应匹配类型大小,建议在 D 脚本中转换为“int8_t” |
| etw_bool8 | 整数 | 类型大小应匹配,并且建议在 D 脚本中转换为“int8_t” |
| etw_hexint8 | 整数 | 应匹配类型大小,建议在 D 脚本中转换为“int8_t” |
| etw_hexint16 | 整数 | 应匹配类型大小,建议在 D 脚本中转换为“int16_t” |
| etw_pid | 整数 | 无 |
| etw_tid | 整数 | 无 |
| etw_mbcsxml | 整数 | 无 |
| etw_mbcsjson | 整数 | 无 |
| etw_countedmbcsxml | 整数 | 无 |
| etw_countedmbcsjson | 整数 | 无 |
| etw_win32error | 整数 | 无 |
| etw_ntstatus | 整数 | 无 |
| etw_hresult | 整数 | 无 |