You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows用户态应用实时收集WPP追踪日志的代码问题排查

Windows用户态应用实时收集WPP追踪日志的代码问题排查

首先明确一点:WPP追踪完全可以通过ETW API实时收集,你的思路是对的,问题应该出在代码的几个关键参数配置或者权限上,咱们一步步梳理:

一、核心问题排查点

1. 错误的日志会话模式

你在pSessionProperties->LogFileMode里设置了EVENT_TRACE_SYSTEM_LOGGER_MODE,这个标志是专门给Windows系统内置的内核会话用的,自定义用户会话绝对不能用它!去掉这个标志,只保留EVENT_TRACE_REAL_TIME_MODE即可,否则会话创建后会出现异常,无法正确捕获自定义提供者的事件。

2. Provider GUID的匹配问题

你在EnableTraceEx2里传入的&ProviderGuid必须完全和驱动里WPP初始化时用的DRIVER_PROVIDER_GUID一致,包括大小写、连字符位置,一点都不能错。可以把驱动里定义的GUID直接复制到用户态代码里,避免手动输入出错。

3. 关键字参数设置

你的EnableTraceEx2里关键字参数传了0,但如果驱动里的WPP日志宏指定了关键字(比如用WPP_LOGGER(WPP_KEYWORD_MYFEATURE)这类带关键字的宏输出日志),0表示不匹配任何关键字,自然收不到事件。可以先改成0xFFFFFFFF(匹配所有关键字)来测试,确认能收到事件后再根据需求调整。

4. 权限不足

ETW会话创建和内核提供者启用需要管理员权限,一定要确保你的用户态程序是以管理员身份运行的,否则StartTrace或EnableTraceEx2会静默失败(返回错误码比如ERROR_ACCESS_DENIED),但你代码里没对这些API的返回值做详细检查,建议在每个API调用后添加错误判断,比如:

status = StartTrace((PTRACEHANDLE)&SessionHandle, SessionName, pSessionProperties);
if (status != ERROR_SUCCESS) {
    wprintf(L"StartTrace failed: %lu\n", status);
    goto cleanup;
}

5. TdhLoadManifest的误用

WPP日志解析依赖的是TMF文件(Trace Message Format),而不是直接加载PDB。TdhLoadManifest是用来加载ETW清单的,对WPP来说并不适用。正确的做法是:

  • 确保驱动编译时生成了TMF文件(通常在输出目录里,后缀是.tmf)
  • 调用TdhSetManifestSearchPath指定TMF文件所在的路径,这样后续解析事件时Tdh才能找到格式信息
  • 如果想用PDB解析,需要调用TdhLoadTraceManifestFromPdb来从PDB中提取追踪信息,但这个步骤是解析事件用的,不影响事件捕获——你现在连事件都收不到,先解决捕获问题,再处理解析。

二、修正后的关键代码片段

void StartETWSession()
{
    ENABLE_TRACE_PARAMETERS traceParameters;
    ZeroMemory(&traceParameters, sizeof(traceParameters));
    traceParameters.Version = ENABLE_TRACE_PARAMETERS_VERSION_2;
    traceParameters.EnableFilterDesc = NULL;
    traceParameters.FilterDescCount = 0;

    bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(SessionName);
    pSessionProperties = (EVENT_TRACE_PROPERTIES*)malloc(bufferSize);
    ZeroMemory(pSessionProperties, bufferSize);
    pSessionProperties->Wnode.BufferSize = bufferSize;
    pSessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
    pSessionProperties->Wnode.ClientContext = 1;
    // 自定义会话GUID,随便生成一个即可,不用系统的
    pSessionProperties->Wnode.Guid = MY_CUSTOM_SESSION_GUID; 
    pSessionProperties->EnableFlags = EVENT_TRACE_FLAG_CSWITCH; // 这个是系统事件开关,和WPP无关,可保留或去掉
    // 去掉SYSTEM_LOGGER_MODE,只保留实时模式
    pSessionProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE; 
    pSessionProperties->MaximumFileSize = 100;
    pSessionProperties->FlushTimer = 1;
    pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
    StringCbCopy((LPWSTR)((char*)pSessionProperties + pSessionProperties->LoggerNameOffset), sizeof(SessionName), SessionName);

    ULONG status = StartTrace((PTRACEHANDLE)&SessionHandle, SessionName, pSessionProperties);
    if (status != ERROR_SUCCESS) {
        wprintf(L"StartTrace failed: %lu\n", status);
        goto cleanup;
    }

    // 设置TMF文件路径(假设TMF在当前目录)
    WCHAR tmfPath[MAX_PATH] = L".\\";
    status = TdhSetManifestSearchPath(tmfPath);
    if (status != ERROR_SUCCESS) {
        wprintf(L"TdhSetManifestSearchPath failed: %lu\n", status);
        // 这里可以继续执行,只是后续解析可能失败
    }

    // 启用驱动的WPP提供者,用正确的DRIVER_PROVIDER_GUID,匹配所有关键字
    status = EnableTraceEx2(
        SessionHandle,
        &DRIVER_PROVIDER_GUID, // 确保这个GUID和驱动里的完全一致
        EVENT_CONTROL_CODE_ENABLE_PROVIDER,
        TRACE_LEVEL_VERBOSE,
        0xFFFFFFFF, // 匹配所有关键字
        0,
        0,
        NULL
    );
    if (status != ERROR_SUCCESS) {
        wprintf(L"EnableTraceEx2 failed: %lu\n", status);
        goto cleanup;
    }

    EVENT_TRACE_LOGFILE logFile;
    ZeroMemory(&logFile, sizeof(EVENT_TRACE_LOGFILE));
    logFile.LoggerName = SessionName;
    logFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP;
    logFile.EventRecordCallback = EventRecordCallback;
    logFile.Context = &context;

    hTrace = OpenTrace(&logFile);
    if (INVALID_PROCESSTRACE_HANDLE == hTrace)
    {
        wprintf(L"OpenTrace() failed with status: %lu\n", GetLastError());
        goto cleanup;
    }

    status = ProcessTrace(&hTrace, 1, NULL, NULL);
    if (status != ERROR_SUCCESS) {
        wprintf(L"ProcessTrace failed: %lu\n", status);
    }

cleanup:
    // 别忘了关闭会话和释放资源
    if (SessionHandle != INVALID_HANDLE_VALUE) {
        ControlTrace(SessionHandle, SessionName, pSessionProperties, EVENT_TRACE_CONTROL_STOP);
    }
    if (pSessionProperties) {
        free(pSessionProperties);
    }
    if (hTrace != INVALID_PROCESSTRACE_HANDLE) {
        CloseTrace(hTrace);
    }
}

VOID WINAPI EventRecordCallback(EVENT_RECORD* pEventRecord) {
    // 先打印所有收到的事件的ProviderID,确认是否有驱动的GUID
    GUID providerId = pEventRecord->EventHeader.ProviderId;
    wprintf(L"Received event from provider: {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
        providerId.Data1, providerId.Data2, providerId.Data3,
        providerId.Data4[0], providerId.Data4[1], providerId.Data4[2], providerId.Data4[3],
        providerId.Data4[4], providerId.Data4[5], providerId.Data4[6], providerId.Data4[7]);

    if (IsEqualGUID(providerId, DRIVER_PROVIDER_GUID))
    {
        // 现在应该能走到这里了
        wprintf(L"Got WPP event from driver!\n");
        // 后续可以用TdhFormatEvent等函数解析事件内容
    }
}

三、额外验证步骤

  1. 先用TraceView确认驱动的WPP日志能正常输出,排除驱动本身的问题
  2. 检查驱动里是否调用了WPP_INIT_TRACING初始化追踪,并且在卸载时调用WPP_CLEANUP
  3. 如果驱动是内核模式,确认用户态程序的位数和驱动一致(比如都是x64),避免跨位数的兼容性问题

备注:内容来源于stack exchange,提问作者TheKwiatek666

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 10:38:06