ETW - 事件消费者
介绍
Event Tracing for Windows (ETW) - Windows drivers | Microsoft Learn
官方示例:Eventdrv - Code Samples | Microsoft Learn
Windows ETW(Event Tracing for Windows 简称ETW)是 Windows 操作系统中的一种高性能、可扩展的事件跟踪框架。它允许开发人员在应用程序、设备驱动程序和内核组件中插入事件,以便在运行时收集有关系统行为的详细信息。这些事件可以用于诊断性能问题、调试应用程序、监视系统活动等。
事件提供者(Event Provider)
事件提供者(Event Provider): 事件提供者是生成事件并将其发送到 ETW 的可执行模块,例如应用程序、设备驱动程序或内核组件。事件提供者在系统中注册,并指定事件的类型和结构。
为了实现事件提供者,开发者需要完成以下任务:
- 定义事件:事件提供者需要定义要生成的事件,包括事件类型、级别、关键字和有效负载(即事件数据)等信息。
- 注册事件提供者:事件提供者需要在系统中注册,以便 ETW 能够识别它。注册过程通常包括提供一个 GUID(全局唯一标识符)和事件元数据的文件(通常是一个 XML 文件)。
- 生成事件:事件提供者需要在适当的代码位置生成事件。生成事件时,需要指定事件的级别、关键字和有效负载等信息。
- 注销事件提供者:在不再需要生成事件时,事件提供者需要从系统中注销。 针对Provider,如果我们想自定义事件以及后续分析的话,这是首先需要创建的。
针对Provider,如果我们想自定义事件以及后续分析的话,这是首先需要创建的。
工具准备
需要安装Windows SDK:Windows相关资源汇总
比如mc,安装10.0.22621.0版本的SDK成功后可以在C:Program Files (x86)Windows Kits10bin10.0.22621.0x86mc.exe
找到
打开下载的微软VS命令行,或者配置mc.exe这类工具到系统环境变量,以便可以使用mc.exe等工具
wevtutil
官网说明:wevtutil | Microsoft Learn
wevtutil.exe是一个 Windows 命令行实用程序,用于管理事件日志和事件跟踪会话。它允许您查看、导出、清除和归档事件日志,以及查询和配置事件提供者和事件跟踪会话。
wevtutil.exe是 Windows Event Log 服务的一部分,它在 Windows Vista 及更高版本的操作系统中可用。
- 功能: - 管理事件日志:wevtutil.exe 可以用于查看、导出、清除和归档事件日志。您可以使用它来查找特定类型的事件,或者在出现问题时导出事件日志以供进一步分析。 - 管理事件提供者:wevtutil.exe 可以用于查询和配置事件提供者。您可以使用它来查看已注册的事件提供者,或者更改事件提供者的配置,以便收集不同类型的事件。 - 管理事件跟踪会话:wevtutil.exe 可以用于查询和配置事件跟踪会话。您可以使用它来查看活动的事件跟踪会话,或者启用和禁用事件跟踪。
- 常用命令参数: - wevtutil el:列出所有事件日志。 - wevtutil qe <LogName>:查询指定事件日志中的事件(例如,wevtutil qe System)。 - wevtutil epl <LogName> <FileName>:将指定事件日志导出到文件(例如,wevtutil epl System system_log.evtx)。 - wevtutil cl <LogName>:清除指定事件日志中的事件(例如,wevtutil cl System)。 - wevtutil ep <ProviderName>:查询指定事件提供者的配置(例如,wevtutil ep Microsoft-Windows-Kernel-Power)。 - wevtutil sl <ProviderName> <Options>:设置指定事件提供者的配置(例如,wevtutil sl Microsoft-Windows-Kernel-Power /e:true)。
示例: 假设我们有一个名为 MyProvider.mc
的 XML 消息定义文件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<instrumentationManifest
xmlns="http://schemas.microsoft.com/win/2004/08/events"
xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<instrumentation>
<events>
<provider name="MyProvider"
guid="{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
resourceFileName="MyProvider.dll"
messageFileName="MyProvider.dll">
<events>
<event value="1" version="1" level="win:Informational"
channel="win:Application" task="0" opcode="0">
<template>
<data name="Message" inType="win:UnicodeString" />
</template>
</event>
</events>
</provider>
</events>
</instrumentation>
<localization>
<resources culture="en-US">
<stringTable>
<string id="1" value="MyProvider: %1" />
</stringTable>
</resources>
</localization>
</instrumentationManifest>
运行以下命令,使用 mc.exe生成事件资源、头文件、清单文件和消息文件:mc.exe MyProvider.mc
,这将生成以下文件:
- MyProvider.h:包含事件 ID、级别、关键字、通道、任务和操作码的常量定义的头文件。
- MyProvider.rc:包含事件资源的资源文件。
- MyProvider.xml:包含事件提供者和事件的元数据的清单文件。
- MyProvider.dll:包含事件的文本描述的消息文件。
mc.exe 是一个用于生成事件资源的实用程序,它可以帮助开发者创建和维护事件提供者。通过使用 mc.exe,开发者可以更轻松地记录和处理事件,以便进行性能分析、调试和系统监控。
wpaexporter.exe
wpaexporter.exe(Windows Performance Analyzer Exporter)是一个 Windows 命令行实用程序,用于将 Windows Performance Recorder(WPR)生成的事件跟踪日志(ETL)文件转换为可读的报告。它是 Windows Performance Toolkit(WPT)的一部分,主要用于性能分析和调试。
以下是关于 wpaexporter.exe 的详细介绍:
功能:
- 转换事件跟踪日志:wpaexporter.exe 可以将 WPR 生成的 ETL 文件转换为 CSV、XML 或 TSV 格式的报告,以便进行进一步分析。
- 自定义报告:wpaexporter.exe 支持使用预定义的分析配置文件(WPA Profile)来自定义报告的内容和格式。分析配置文件定义了报告中要包含的表格、图形和摘要信息。
- 批量处理:wpaexporter.exe 可以在命令行中批量处理多个 ETL 文件,从而提高分析效率。
使用方法: 要使用 wpaexporter.exe,首先需要一个由 WPR 生成的 ETL 文件。然后,运行 wpaexporter.exe,将 ETL 文件作为输入,指定输出文件的格式和名称。您还可以选择一个分析配置文件来自定义报告的内容和格式。
示例: 假设我们有一个名为 trace.etl 的事件跟踪日志文件,我们希望将其转换为 CSV 格式的报告。运行以下命令:
代码语言:shell复制wpaexporter.exe -i trace.etl -o report.csv -profile CPU
在这个示例中,-i 参数指定输入的 ETL 文件,-o 参数指定输出的 CSV 文件,-profile 参数指定预定义的分析配置文件(在这里,我们使用了名为 "CPU" 的配置文件)。
生成过程
准备事件清单,xml内容,按照指定格式填下,格式说明如下或者官方Demo:Windows-driver-samples/general/tracing/evntdrv/Eventdrv/evntdrv.xml at main · microsoft/Windows-driver-samples (github.com)
provider字段说明 :
- guid:此Guid用于后续Consumer和Controller使用
- name:Provider名称,使用命令行:
wevtutil ep > providers.txt
可以导出所有注册的Provider - symbol:可选 - messageFileName:参数指定了包含事件消息的文件的路径。当事件被触发时,事件跟踪会使用该文件中的消息来记录事件的详细信息
- resourceFileName:参数指定了包含本地化资源的文件的路径。这些资源文件包含了事件消息的本地化字符串,以便根据不同的语言环境显示适当的文本。
通过指定正确的 messageFileName 和 resourceFileName,事件跟踪可以根据提供程序的设置,将事件消息和参数信息转换为易于理解的文本,并在日志中进行记录。这使得事件的分析和诊断更加方便和可读性更强。 运行命令行:mc.exe etwproviders.man
生成以下文件:
- etwprovidersTEMP.BIN
- etwproviders.h
- etwproviders.rc
生成后就可以使用这些文件编写属于自己的Provider,应用层的话编写dll即可 先上Demo源码:(头文件独立出来,方便后面Controllor使用)
源码
头文件
头文件用于导出一系列接口,供应用程序生产事件,以及后续Controllor和Consumer进行使用。
代码语言:C 复制// etwprof.h
PLATFORM_INTERFACE void __cdecl ETWMarkPrintf(_Printf_format_string_ _In_z_ PCSTR pMessage, ...);
PLATFORM_INTERFACE void __cdecl ETWRenderFrameMark();
实现
分为三个步骤:注册、生成、注销
代码语言:C 复制// Typedefs for use with GetProcAddress
typedef ULONG (__stdcall *tEventRegister)( _In_ LPCGUID ProviderId, _In_opt_ PENABLECALLBACK EnableCallback, _In_opt_ PVOID CallbackContext, _Out_ PREGHANDLE RegHandle);
typedef ULONG (__stdcall *tEventWrite)( _In_ REGHANDLE RegHandle, _In_ PCEVENT_DESCRIPTOR EventDescriptor, _In_ ULONG UserDataCount, _In_reads_opt_(UserDataCount) PEVENT_DATA_DESCRIPTOR UserData);
typedef ULONG (__stdcall *tEventUnregister)( _In_ REGHANDLE RegHandle );
ULONG EVNTAPI EventRegister( _In_ LPCGUID ProviderId, _In_opt_ PENABLECALLBACK EnableCallback, _In_opt_ PVOID CallbackContext, _Out_ PREGHANDLE RegHandle )
{
if ( g_ETWRegister.m_pEventRegister )
return g_ETWRegister.m_pEventRegister( ProviderId, EnableCallback, CallbackContext, RegHandle );
*RegHandle = 0;
return ERROR_INVALID_FUNCTION;
}
ULONG EVNTAPI EventWrite( _In_ REGHANDLE RegHandle, _In_ PCEVENT_DESCRIPTOR EventDescriptor, _In_ ULONG UserDataCount, _In_reads_opt_(UserDataCount) PEVENT_DATA_DESCRIPTOR UserData )
{
if ( g_ETWRegister.m_pEventWrite )
return g_ETWRegister.m_pEventWrite( RegHandle, EventDescriptor, UserDataCount, UserData );
return ERROR_INVALID_FUNCTION;
}
ULONG EVNTAPI EventUnregister( _In_ REGHANDLE RegHandle )
{
if ( g_ETWRegister.m_pEventUnregister )
return g_ETWRegister.m_pEventUnregister( RegHandle );
return ERROR_INVALID_FUNCTION;
}
注册
代码语言:C 复制class CETWRegister
{
public:
CETWRegister()
{
QueryPerformanceFrequency(&m_frequency);
// 查找 Advapi32.dll,这应该总是成功的。
HMODULE pAdvapiDLL = LoadLibraryW(L"Advapi32.dll");
if (pAdvapiDLL) {
// 尝试查找 ETW 函数。这在 Windows XP 上会失败。
m_pEventRegister = (tEventRegister)GetProcAddress(pAdvapiDLL, "EventRegister");
m_pEventWrite = (tEventWrite)GetProcAddress(pAdvapiDLL, "EventWrite");
m_pEventUnregister = (tEventUnregister)GetProcAddress(pAdvapiDLL, "EventUnregister");
// 注册我们的 ETW 提供程序。如果注册失败,则事件日志调用将失败。
// 在 Windows XP 上,这些调用将不起作用。
// 在 Vista 及更高版本中,如果这些提供程序已由 xperf 或 logman 启用,
// 则 *Context 全局变量将被修改如下:
// MatchAnyKeyword: 0xffffffffffffffff
// IsEnabled: 1
// Level: 255
// 换句话说,完全启用。
EventRegisterMulti_FrameRate();
EventRegisterMulti_Main();
EventRegisterMulti_Worker();
EventRegisterMulti_Input();
// 发出主线程的线程 ID。这也表示主提供程序已初始化。
EventWriteThread_ID(GetCurrentThreadId(), "Main thread");
}
}
~CETWRegister()
{
// Unregister our providers.
EventUnregisterMulti_Input();
EventUnregisterMulti_Worker();
EventUnregisterMulti_Main();
EventUnregisterMulti_FrameRate();
}
tEventRegister m_pEventRegister;
tEventWrite m_pEventWrite;
tEventUnregister m_pEventUnregister;
// QPC frequency
LARGE_INTEGER m_frequency;
} g_ETWRegister;
事件生成
具体使用Provider生成事件的步骤如下:
- 引入Provider的头文件
- 注意事件清单messageFileName字段定义的目录,比如:messageFileName="%temp%ETWProviders.dll",需要将Provider对应的dll拷贝到%temp%目录下
- 引入Provider对应Dll导入库
- 利用宏生成事件即可
源码展示如下:
代码语言:C 复制#include "stdafx.h"
#include <Windows.h>
#include "ETWProvidersetwprof.h"
#include <future>
#include <string>
__declspec(noinline) void IdleDelay()
{
Sleep(10);
}
__declspec(noinline) void BusyDelay()
{
DWORD startTick = GetTickCount();
for (;;)
{
DWORD elapsed = GetTickCount() - startTick;
if (elapsed > 10)
break;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
printf("Emitting custom ETW events that can be recorded with etwrecord.batn");
CETWScope timer("main");
for (int i = 0; i < 40; i) {
ETWMarkPrintf("This is loop %d", i);
ETWRenderFrameMark();
// Simulating code that does something.
IdleDelay();
BusyDelay();
}
return 0;
}
效果展示
录制etl,然后用wpa打开就可以看到我们定义的事件展示