USB ETW トレースでのアクティビティ ID GUID の使用
このトピックでは、アクティビティ ID GUID に関する情報、イベント トレース プロバイダーに GUID を追加する方法、Netmon で GUID を表示する方法について説明します。
USB ドライバー スタックのドライバー (2.0 と 3.0 の両方) は ETW イベント トレース プロバイダーです。 Windows 7 では、USB ドライバー スタックからイベント トレースをキャプチャしながら、他のドライバーやアプリケーションなどの他のプロバイダーからトレースをキャプチャできます。 その後、結合されたログを読み取ることができます (プロバイダーのイベント トレース用に Netmon パーサーを作成していることが前提)。
Windows 8 以降では、アクティビティ ID GUID を使用して、プロバイダー間で (アプリケーション、クライアント ドライバー、および USB ドライバー スタックからの) イベントを関連付けることができます。 複数のプロバイダーからのイベントに同じアクティビティ ID GUID が割り当てられている場合は、Netmon でそれらのイベントを関連付けることができます。 これらの GUID に基づいて、Netmon は、上位層で計測されたアクティビティから生じた一連の USB イベントを表示できます。
Netmon で他のプロバイダーからのイベント トレースを組み合わせて表示する場合、アプリケーションからのイベントを右クリックし、[Find Conversations] (やり取りを検索) -> [NetEvent] を選択すると、関連するドライバー イベントが表示されます。
この画像では、アプリケーション、UMDF ドライバー、および Ucx01000.sys (USB ドライバー スタックのドライバーの 1 つ) からの関連イベントを示しています。これらのイベントには同じアクティビティ ID GUID が割り当てられています。
- アプリケーションにアクティビティ ID GUID を追加する方法
- UMDF ドライバーでアクティビティ ID GUID を設定する方法
- カーネル モード ドライバーにアクティビティ ID GUID を追加する方法
アプリケーションにアクティビティ ID GUID を追加する方法
アプリケーションには、EventActivityIdControl を呼び出すことでアクティビティ ID GUID を含めることができます。 詳細については、「イベント トレーシング機能」を参照してください。
このコード例では、アプリケーションがアクティビティ ID GUID を設定し、UMDF ドライバーである ETW プロバイダーに送信する方法を示しています。
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 で呼び出してアクティビティ ID を作成し、次に EVENT_ACTIVITY_CTRL_SET_ID で呼び出してそのアクティビティ ID を現在のスレッドに設定しています。 アプリケーションは、ドライバー定義の IOCTL (次のセクションで説明) を送信することで、ユーザー モード ドライバーなどの ETW イベント プロバイダーにアクティビティ GUID を指定します。
イベント プロバイダーは計測マニフェスト ファイル (.MAN ファイル) を公開する必要があります。 メッセージ コンパイラ (Mc.exe) を実行すると、イベント プロバイダー、イベント属性、チャンネル、およびイベントの定義を含むヘッダー ファイルが生成されます。 この例では、アプリケーションは、生成されたヘッダー ファイルで定義されている EventWriteReadFail を呼び出し、障害が発生した場合にトレース イベント メッセージを書き込みます。
UMDF ドライバーでアクティビティ ID GUID を設定する方法
ユーザー モード ドライバーは、EventActivityIdControl を呼び出してアクティビティ ID GUID を作成および設定します。その呼び出しは、前のセクションで説明したように、アプリケーションがそれらを呼び出す方法と似ています。 これらの呼び出しにより、アクティビティ ID GUID が現在のスレッドに追加され、スレッドがイベントをログに記録するたびにそのアクティビティ ID GUID が使用されます。 詳細については、「アクティビティ識別子の使用」を参照してください。
このコード例では、アプリケーションによって IOCTL を通じて作成および指定されたアクティビティ ID GUID を UMDF ドライバーが設定する方法を示しています。
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;
}
アプリケーションによって作成されたアクティビティ ID GUID がユーザー モード ドライバー フレームワーク (UMDF) クライアント ドライバーにどのように関連付けられるかを見てみましょう。 ドライバーはアプリケーションから IOCTL 要求を受信すると、GUID をプライベート メンバーにコピーします。 ある時点で、アプリケーションは ReadFile を呼び出して読み取り操作を実行します。 フレームワークは要求を作成し、ドライバーのハンドラー ForwardFormattedRequest を呼び出します。 ハンドラー内で、ドライバーは EventActivityIdControl を呼び出して以前に保存したアクティビティ ID GUID をスレッドに設定し、EventWriteReadFail を呼び出してイベント メッセージをトレースします。
注 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 を呼び出してから EtwWrite を呼び出すことで、新しい GUID を IRP に関連付けることができます。
アクティビティ ID GUID は IRP と共に 1 層下位のドライバーに渡されます。 下位のドライバーはそのトレース メッセージをスレッドに追加できます。