在 USB ETW 追蹤中使用活動識別碼 GUID
本主題提供活動標識碼 GUID 的相關信息、如何在事件追蹤提供者中新增這些 GUID,並在 Netmon 中檢視這些 GUID。
USB 驅動程式堆疊中的驅動程式 (2.0 和 3.0) 都是 ETW 事件追蹤提供者。 在 Windows 7 中,從 USB 驅動程式堆疊擷取事件追蹤時,您可以從其他提供者擷取追蹤,例如其他驅動程式和應用程式。 然後,您可以讀取合併的記錄 (,假設您已為提供者的事件追蹤建立 Netmon 剖析器) 。
從 Windows 8 開始,您可以使用活動識別碼 GUID,將來自應用程式、用戶端驅動程式和 USB 驅動程式) 堆疊 (提供者之間的事件產生關聯。 當事件具有相同的活動標識碼 GUID 時,可以在 Netmon 中關聯來自多個提供者的事件。 根據這些 GUID,Netmon 可以向您顯示由上層檢測活動所產生的一組 USB 事件。
檢視 Netmon 中其他提供者的合併事件追蹤時,以滑鼠右鍵按兩下應用程式的事件,然後選擇 [尋找交談 -> NetEvent ] 以查看相關聯的驅動程式事件。
下圖顯示來自應用程式、UMDF 驅動程式,以及 Ucx01000.sys (USB 驅動程式堆疊中其中一個驅動程式) 的相關事件。這些事件具有相同的活動標識碼 GUID。
如何在應用程式中新增活動標識碼 GUID
應用程式可以藉由呼叫 EventActivityIdControl 來包含活動標識碼 GUID。 如需詳細資訊,請參閱 事件追蹤函式。
此範例程式代碼示範應用程式如何設定活動標識碼 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) 。 應用程式會藉由傳送驅動程式定義的 IOCTL (下一) 節中所述,將活動 GUID 指定給 ETW 事件提供者,例如使用者模式驅動程式。
事件提供者必須發佈檢測指令清單檔案 (。MAN 檔案) 。 藉由執行 訊息編譯程式 (Mc.exe) ,會產生頭檔,其中包含事件提供者、事件屬性、通道和事件的定義。 在此範例中,應用程式會呼叫在產生的頭檔中定義的 EventWriteReadFail,以在發生失敗時寫入追蹤事件訊息。
如何在 UMDF 驅動程式中設定活動識別碼 GUID
使用者模式驅動程式會藉由呼叫 EventActivityIdControl 來建立及設定活動識別碼 GUID,而且呼叫類似於應用程式呼叫它們的方式,如上一節所述。 這些呼叫會將活動標識碼 GUID 新增至目前的線程,而且每當線程記錄事件時,就會使用該活動標識元 GUID。 如需詳細資訊,請參閱 使用活動標識符。
此範例程式代碼示範 UMDF 驅動程式如何透過 IOCTL 設定應用程式所建立和指定的活動識別碼 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
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 如何與 使用者模式驅動程式 Framework 建立關聯, (UMDF) 用戶端驅動程式。 當驅動程式從應用程式收到IOCTL要求時,它會複製私人成員中的 GUID。 此時,應用程式會呼叫 ReadFile 來執行讀取作業。 架構會建立要求,並叫用驅動程序的處理程式 ForwardFormattedRequest。 在處理程式中,驅動程式會呼叫 EventActivityIdControl 和 EventWriteReadFail 來追蹤事件訊息,以在線程上設定先前儲存的活動識別符 GUID。
注意 UMDF 驅動程式也必須包含透過檢測指令清單檔案產生的頭檔。 標頭檔會定義宏,例如寫入追蹤訊息的 EventWriteReadFail。
如何在內核模式驅動程式中新增活動標識碼 GUID
在核心模式中,驅動程式可以在源自使用者模式的線程或驅動程式建立的線程上追蹤訊息。 在這兩種情況下,驅動程式都需要線程的活動標識碼 GUID。
若要追蹤訊息,驅動程式必須取得註冊句柄作為事件提供者, (請參閱 EtwRegister) ,然後藉由指定 GUID 和事件訊息呼叫 EtwWrite 。 如需詳細資訊,請參閱 將事件追蹤新增至 Kernel-Mode 驅動程式。
如果您的內核模式驅動程式會處理應用程式或使用者模式驅動程式所建立的要求,則內核模式驅動程式不會建立和設定活動標識元 GUID。 相反地,I/O 管理員會處理大部分的活動標識碼傳播。 當使用者模式線程起始要求時,I/O 管理員會為要求建立 IRP,並自動將目前線程的活動標識元 GUID 複製到新的 IRP。 如果內核模式驅動程式想要追蹤該線程上的事件,它必須藉由呼叫 IoGetActivityIdIrp來取得 GUID,然後呼叫 EtwWrite。
如果您的內核模式驅動程式建立具有活動標識碼 GUID 的 IRP,驅動程式可以使用 EVENT_ACTIVITY_CTRL_CREATE_SET_ID 呼叫 EtwActivityIdControl 來產生新的 GUID。 然後,驅動程式可以藉由呼叫 IoSetActivityIdIrp ,然後呼叫 EtwWrite,將新的 GUID 與 IRP 產生關聯。
活動標識元 GUID 會連同 IRP 一起傳遞至下一個較低的驅動程式。 較低的驅動程式可以將追蹤訊息新增至線程。