ETW - 事件提供者(Event Provider)

2024-07-15 21:55:27 浏览数 (2)

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复制
<?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生成事件的步骤如下:

  1. 引入Provider的头文件
  2. 注意事件清单messageFileName字段定义的目录,比如:messageFileName="%temp%ETWProviders.dll",需要将Provider对应的dll拷贝到%temp%目录下
  3. 引入Provider对应Dll导入库
  4. 利用宏生成事件即可

源码展示如下:

代码语言: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打开就可以看到我们定义的事件展示

0 人点赞