Использование GUID идентификаторов активности в трассировках USB ETW

В этом разделе содержатся сведения об идентификаторах GUID действия, об их добавлении в поставщики трассировки событий и просмотре в Netmon.

Драйверы в стеке USB-драйверов (версий 2.0 и 3.0) являются поставщиками трассировки событий ETW. В Windows 7 при записи трассировок событий из стека USB-драйверов можно записывать трассировки от других поставщиков, таких как другие драйверы и приложения. Затем вы сможете прочитать объединенный журнал (при условии, что вы создали Netmon-средство для анализа трассировок событий вашего поставщика).

Начиная с Windows 8, можно связать события между поставщиками (от приложений, клиентского драйвера и стека драйверов USB) с помощью идентификаторов действий . События из нескольких поставщиков могут быть связаны в Netmon, если у них один и тот же GUID идентификатора действия. На основе этих графических идентификаторов Netmon может показать набор событий USB, которые привели к инструментированному действию на верхнем слое.

Просматривая объединенные трассировки событий от других поставщиков в Netmon, щелкните правой кнопкой мыши событие из приложения и выберите Поиск бесед —> NetEvent, чтобы просмотреть связанные события драйвера.

На этом рисунке показаны связанные события из приложения, драйвера UMDF и Ucx01000.sys (один из драйверов в стеке USB-драйверов). Эти события имеют один и тот же ИДЕНТИФИКАТОР действия GUID.

сетевой монитор Майкрософт.

Как добавить GUID идентификатор активности в приложение

Приложение может включать идентификаторы действий с помощью вызова EventActivityIdControl. Дополнительные сведения см. в разделе Функции трассировки событий.

В этом примере кода показано, как приложение может задать идентификатор действия 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 для создания идентификатора действия (EVENT_ACTIVITY_CTRL_CREATE_ID), а затем, чтобы задать его (EVENT_ACTIVITY_CTRL_SET_ID) для текущего потока. Приложение указывает GUID активности поставщику событий ETW, например драйверу в пользовательском режиме, отправляя IOCTL, определяемый драйвером (описано в следующем разделе).

Поставщик событий должен опубликовать файл манифеста инструментирования (.MAN-файл). При запуске компилятора сообщений (Mc.exe)создается файл заголовка, содержащий определения для поставщика событий, атрибутов событий, каналов и самих событий. В примере приложение вызывает EventWriteReadFail, определенный в созданном файле заголовка, для записи сообщений о событиях трассировки в случае сбоя.

Настройка GUID идентификатора активности в драйвере UMDF

Драйвер пользовательского режима создает и задает идентификаторы действий, вызывая EventActivityIdControl, и эти вызовы аналогичны вызовам, которые делает приложение, как описано в предыдущем разделе. Эти вызовы добавляют идентификатор действия GUID в текущий поток и идентификатор действия используется всякий раз, когда поток регистрирует событие. Дополнительные сведения см. в разделе Использование идентификаторов действий.

В этом примере кода показано, как драйвер UMDF задает идентификатор действия, созданный и заданный приложением с помощью IOCTL.

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

Arguments:

    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;
}

Давайте посмотрим, как GUID идентификатора действия, созданный приложением, связывается с клиентским драйвером (UMDF) в рамкахUser-Mode Driver Framework. Когда драйвер получает запрос IOCTL от приложения, он копирует GUID в приватный элемент. В какой-то момент приложение вызывает ReadFile для выполнения операции чтения. Платформа создает запрос и вызывает обработчик драйвера ForwardFormattedRequest. В обработчике драйвер задает ранее сохраненный идентификатор активности в потоке, вызывая EventActivityIdControl и EventWriteReadFail для отслеживания сообщений о событиях.

Примечание Драйвер UMDF также должен включать файл заголовка, созданный с помощью файла манифеста инструментирования. Файл заголовка определяет макросы, такие как EventWriteReadFail, которые записывают сообщения трассировки.

Как добавить GUID идентификатора активности в драйвере в режиме ядра

В режиме ядра драйвер может отслеживать сообщения в потоке, исходном в пользовательском режиме или потоке, который создает драйвер. В обоих случаях драйверу требуется идентификатор активности GUID потока.

Чтобы отслеживать сообщения, драйвер должен получить дескриптор регистрации в качестве поставщика событий (см. EtwRegister), а затем вызвать EtwWrite, указав GUID и сообщение о событии. Дополнительные сведения см. в разделе Добавление трассировки событий к драйверам Kernel-Mode.

Если драйвер режима ядра обрабатывает запрос, созданный приложением или драйвером пользовательского режима, драйвер режима ядра не создает и не задает ИДЕНТИФИКАТОР действия. Вместо этого диспетчер ввода-вывода обрабатывает большую часть распространения идентификатора действия. Когда поток пользовательского режима инициирует запрос, диспетчер операций ввода-вывода создает IRP для этого запроса и автоматически копирует GUID идентификатора активности текущего потока в новый IRP. Если драйвер в режиме ядра хочет трассировать события в этом потоке, он должен получить GUID, вызвав IoGetActivityIdIrp, а затем вызвать EtwWrite.

Если драйвер в режиме ядра создает IRP, содержащий GUID активности, он может вызвать EtwActivityIdControl с EVENT_ACTIVITY_CTRL_CREATE_SET_ID для создания нового GUID. Затем драйвер может связать новый GUID с IRP, вызвав IoSetActivityIdIrp, а затем вызвать EtwWrite.

GUID идентификатора действия передается вместе с IRP следующим по уровню драйверам. Драйверы нижнего уровня могут добавлять сообщения трассировки в поток.