在 USB ETW 跟踪中使用活动 ID GUID

本主题提供有关活动 ID GUID、如何在事件跟踪提供程序中添加这些 GUID,以及在 Netmon 中查看它们的信息。

USB 驱动程序跟踪(2.0 和 3.0)中的驱动程序是 ETW 事件跟踪提供程序。在 Windows 7 中,在从 USB 驱动程序堆栈中捕获事件跟踪时,你可以从其他提供程序捕获跟踪,例如其他驱动程序和应用程序。然后可以阅读合并日志(假设你已经为提供程序的事件跟踪创建了 Netmon 分析程序)。

从 Windows 8 开始,你可以通过使用活动 ID GUID 跨提供程序关联事件(来自应用程序、客户端驱动程序和 USB 驱动程序堆栈)。 当来自多个提供程序的事件具有相同的活动 ID GUID 时,你可以在 Netmon 中将它们关联在一起。基于这些 GUID,Netmon 可以显示一组从上层检测活动中得出的 USB 事件。

在 Netmon 中查看来自其他提供程序的合并事件跟踪时,从应用程序中右键单击一个事件并选择“查找对话 ”->“NetEvent”可看到关联的驱动程序事件。

此图显示了来自应用程序、UMDF 驱动程序和 Ucx01000.sys(USB 驱动程序堆栈中的驱动程序之一)的相关事件。这些事件具有相同的活动 ID GUID。

JJ151578.netmon_activity(zh-cn,VS.85).png

如何在应用程序中添加活动 ID GUID

应用程序可通过调用 EventActivityIdControl 包括活动 ID GUID。有关详细信息,请参阅事件跟踪功能

此示例代码显示了应用程序如何设置活动 ID GUID 并将其发送到 ETW 提供程序,即一个 UMDF 驱动程序。


EventActivityIdControl(EVENT_ACTIVITY_CTRL_CREATE_ID, &activityIdStruct.ActivityId); 
EventActivityIdControl(EVENT_ACTIVITY_CTRL_SET_ID,    &activityIdStruct.ActivityId); 
                
if (!DeviceIoControl(hRead,
                     IOCTL_OSRUSBFX2_SET_ACTIVITY_ID,
                     &activityIdStruct,         // Ptr to InBuffer
                     sizeof(activityIdStruct),  // Length of InBuffer
                     NULL,                      // Ptr to OutBuffer
                     0,                         // Length of OutBuffer
                     NULL,                      // BytesReturned
                     0))                        // Ptr to Overlapped structure
{         

          wprintf(L"Failed to set activity ID - error %d\n", GetLastError());
}

...

success = ReadFile(hRead, pinBuf, G_ReadLen, (PULONG) &nBytesRead, NULL);

if(success == 0) 
{
          wprintf(L"ReadFile failed - error %d\n", GetLastError());

          EventWriteReadFail(0, GetLastError());

          ...


}


在上一示例中,对于当前线程,应用程序通过调用 EventActivityIdControl 来创建一个活动 ID (EVENT_ACTIVITY_CTRL_CREATE_ID),然后对其进行设置 (EVENT_ACTIVITY_CTRL_SET_ID)。该应用程序通过发送驱动程序定义的 IOCTL(在下一节中介绍)将该活动 GUID 指定给 ETW 事件提供程序,如用户模式驱动程序。

事件提供程序必须发布检测清单文件 (.MAN file)。通过运行 message compiler (Mc.exe),将生成一个头文件,其中包含事件提供程序、事件特性、通道和事件的定义。 在本例中,如果出现故障,应用程序将调用在生成的头文件中定义的 EventWriteReadFail,以写入跟踪事件消息。

如何在 UMDF 驱动程序中设置活动 ID GUID

用户模式驱动程序通过调用 EventActivityIdControl 创建并设置活动 ID GUID,并且调用方式与应用程序调用它们的方式类似(已在上一节中介绍)。这些调用将活动 ID GUID 添加到当前线程,每当线程记录事件时将使用该活动 ID GUID。有关详细信息,请参阅使用活动描述符

此示例代码显示 UMDF 驱动程序如何设置应用程序通过 IOCTL 创建和指定的活动 ID GUID。



VOID
STDMETHODCALLTYPE
CMyControlQueue::OnDeviceIoControl(
    _In_ IWDFIoQueue *FxQueue,
    _In_ IWDFIoRequest *FxRequest,
    _In_ ULONG ControlCode,
    _In_ SIZE_T InputBufferSizeInBytes,
    _In_ SIZE_T OutputBufferSizeInBytes
    )
/*++

Routine Description:


    DeviceIoControl dispatch routine

Aruments:
    
    FxQueue - Framework Queue instance
    FxRequest - Framework Request  instance
    ControlCode - IO Control Code
    InputBufferSizeInBytes - Lenth of input buffer
    OutputBufferSizeInBytes - Lenth of output buffer

    Always succeeds DeviceIoIoctl
Return Value:

    VOID

--*/
{
    ...

    switch (ControlCode)
    {

        ....

        case IOCTL_OSRUSBFX2_SET_ACTIVITY_ID:
        {
            if (InputBufferSizeInBytes < sizeof(UMDF_ACTIVITY_ID))
            {
                hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
            }
            else
            {
                FxRequest->GetInputMemory(&memory );
            }

            if (SUCCEEDED(hr)) 
            {
                buffer = memory->GetDataBuffer(&bigBufferCb);
                memory->Release();

                m_Device->SetActivityId(&((PUMDF_ACTIVITY_ID)buffer)->ActivityId);
                hr = S_OK;
            }

            break;
        }
    } 
}

VOID
 SetActivityId(
        LPCGUID ActivityId
        )
    {
        CopyMemory(&m_ActivityId, ActivityId, sizeof(m_ActivityId));
    }



void
CMyReadWriteQueue::ForwardFormattedRequest(
    _In_ IWDFIoRequest*                         pRequest,
    _In_ IWDFIoTarget*                          pIoTarget
    )
{
...
    pRequest->SetCompletionCallback(
        pCompletionCallback,
        NULL
        );

...
    hrSend = pRequest->Send(pIoTarget,
                            0,  //flags
                            0); //timeout

...
    if (FAILED(hrSend))
    {
        contextHr = pRequest->RetrieveContext((void**)&pRequestContext);

        if (SUCCEEDED(contextHr)) {

            EventActivityIdControl(EVENT_ACTIVITY_CTRL_SET_ID, &pRequestContext->ActivityId);

            if (pRequestContext->RequestType == RequestTypeRead)
            {
                EventWriteReadFail(m_Device, hrSend);
            }

            delete pRequestContext;
        }

        pRequest->CompleteWithInformation(hrSend, 0);
    }

    return;
}




让我们看一下应用程序创建的活动 ID GUID 如何与用户模式驱动程序框架 (UMDF) 客户端驱动程序关联。当驱动程序从应用程序收到 IOCTL 请求时,它将在私有成员中复制 GUID。有时,应用程序调用 ReadFile 执行读操作。该框架创建一个请求并调用驱动程序的处理程序 ForwardFormattedRequest。在该处理程序中,驱动程序通过调用 EventActivityIdControl 和 EventWriteReadFail 在线程上设置以前存储的活动 ID GUID,以跟踪事件消息。

注意  UMDF 驱动程序还必须包括通过检测清单文件生成的头文件。头文件定义写入跟踪消息的宏,如 EventWriteReadFail。

如何在内核模式驱动程序中添加活动 ID GUID

在内核模式中,驱动程序可以跟踪位于源自用户模式的线程上或驱动程序创建的线程上的消息。在这两种情况下,驱动程序都需要线程的活动 ID GUID。

要跟踪消息,驱动程序必须作为事件提供程序获取注册句柄(参见 EtwRegister),然后通过指定 GUID 和事件消息调用 EtwWrite。有关详细信息,请参阅向内核模式驱动程序中添加事件跟踪

如果内核模式驱动程序处理由应用程序或用户模式驱动程序创建的请求,则内核模式驱动程序不创建和设置活动 ID GUID。相反,I/O 管理器将处理大多数活动 ID 传播。当用户模式线程启动一个请求时,I/O 管理器将为该请求创建一个 IRP,并自动将当前线程的活动 ID GUID 复制到新的 IRP 中。如果内核模式驱动程序希望跟踪该线程上的事件,它必须通过调用 IoGetActivityIdIrp 获取 GUID,然后调用 EtwWrite

如果内核模式驱动程序使用活动 ID GUID 创建一个 IRP,则该驱动程序可以通过 EVENT_ACTIVITY_CTRL_CREATE_SET_ID 调用 EtwActivityIdControl 以生成新的 GUID。驱动程序然后可以通过调用 IoSetActivityIdIrp 将新的 GUID 与 IRP 关联在一起,随后调用 EtwWrite

活动 ID GUID 随 IRP 一起传递到接下来较低的驱动程序。较低的驱动程序可以将其跟踪消息添加到该线程。

相关主题

Windows 的 USB 事件跟踪

 

 

显示:
© 2015 Microsoft