ケース スタディ: ETW と Netmon を使用した不明な USB デバイスのトラブルシューティング
このトピックでは、USB ETW と Netmon を使用して、USB デバイスが Windows で認識されない場合のトラブルシューティングを行う方法の例を示します。
この例では、接続したデバイスが、デバイス マネージャーやユーザー インターフェイス (UI) の他の部分で不明なデバイスとして表示された場合を想定します。 ハードウェア ID は USB\UNKNOWN です。 さらに診断するために、デバイスを取り外し、ETW トレースを開始し、デバイスをもう一度接続します。 デバイスが不明なデバイスとして表示された後、トレースを停止します。
不明なデバイスの問題について
不明な USB デバイスの問題をデバッグするには、ユーザーがデバイスをシステムに接続したときに、USB ドライバー スタックでデバイスを列挙するためにどのような処理が行われるかを理解することが有用です。 USB 列挙の詳細については、USB スタックでデバイスを列挙する方法に関するブログ記事を参照してください。
通常、USB ドライバー スタックがデバイスの列挙に失敗した場合でも、ハブ ドライバーはデバイスの到着を Windows に報告し、このUSB デバイスはデバイス マネージャーで不明なデバイスであると示されます。 デバイスのデバイス ID は USB\VID_0000&PID_0000、ハードウェア ID と互換 ID は USB\UNKNOWN です。 以下の場合は、USB ハブ ドライバーが USB デバイスを不明なデバイスとして列挙します。
- 列挙中にポートのリセット要求がタイムアウトした。
- USB デバイスのアドレスの設定要求が失敗した。
- USB デバイスのデバイス記述子に対する要求が失敗した。
- USB デバイス記述子の形式に誤りがあり、検証に失敗した。
- 構成記述子の要求が失敗した。
- USB 構成記述子の形式に誤りがあり、検証に失敗した。
Windows 7 では、列挙に失敗した不明なデバイスには、デバイス マネージャーでエラー コード 43 が示されます。
デバイス マネージャーでデバイスにがエラー コード 28 が示されている場合は、そのデバイスが正常に列挙されたものの不明なデバイスであることを示します。 このエラー コードは、列挙中にデバイスが製品 ID 文字列を提供しておらず、ドライバーのインストール先デバイスに一致する INF を Windows が見つけられなかったことを示します。
イベント トレース分析の開始
これはデバイスの障害であるため、Netmon を USB パーサーと共に使用してログ ファイルを分析することをお勧めします。
イベント トレース ログを表示するには
Netmon を実行し、[ファイル] -> [開く] -> [キャプチャ] の順にクリックし、ファイルを選択します。
[フレームの概要] ペインの最初のイベントを選択します。このイベントの説明欄には SystemTrace と表示されています。 この画像には、最初のイベントを選択したときの画面を示しています。
Netmon で表示される列をカスタマイズするには、列名を右クリックし、[列の選択] を選択 します。
SystemTrace 型と示されている 1 つ目のイベントには、ログに関する一般的な情報が含まれています。 [フレームの詳細] ペインで情報ツリーを展開すると、失われたイベントの数やトレース開始時刻などの情報を表示できます。
USB デバイスの概要イベント
イベント 2 は、ログ内で最初の USB イベントです。 このイベントとその後のいくつかのイベントは、トレースを開始したときにシステムに接続されていた USB ホスト コントローラー、ハブ、およびデバイスを説明しています。 このイベント グループは、デバイスの概要イベント、または単に概要イベントと呼ぶことができます。 1 つ目のイベントと同様、概要イベントにドライバー アクティビティは示されていません。 概要イベントは、ログ セッションの開始時にデバイスの状態を記録します。 他のイベントは、バス上の動き、クライアント ドライバーやシステムとの対話、内部状態の変化などを表しています。
USB ハブと USB ポート ドライバーの両方で、ログに概要イベントが記録されます。 イベントを記録したドライバーは、[プロトコル名] 列で識別されます。 たとえば、USB ポート ドライバーによって記録されたイベントのプロトコル名は USBPort_MicrosoftWindowsUSBPORT です。 USB イベント トレースには通常、一連のポート概要イベントと、それに続く一連のハブ概要イベントが含まれます。 USB ポートおよび USB ハブの概要イベントの多くには、説明に "Information" または "Attributes" という単語が含まれています。
概要イベントの終了を特定するにはどうすればよいのでしょうか? ログ開始時のタイムスタンプ パターンで、USB ハブ イベント間に長い間隔がある場合、その間隔がデバイス概要の終了であることが考えられます。 そうでない場合、USB ハブ イベントの後の最初の USB ポート イベントは、最初の非概要イベントである可能性があります。 下の図 3 は、このサンプル トレースの最初の非概要イベントを示しています。
この例では、トレースを開始したときに対象のデバイスがシステムに接続されていなかったため、ここではデバイスの概要イベントをスキップできます。
イベントの説明とデータ ペイロード
サンプル ログでは、デバイス概要イベントの後の最初のイベントは、USB Hub Wait Wake IRP Completed イベントです。 ここではデバイスを接続し、それに応じてホスト コントローラーまたはハブがウェイクアップしています。 ウェイクアップしているコンポーネントを特定するには、イベントのデータを確認します。 データは [フレームの詳細] ペインにあり、次のような形式のツリー構造で表示されます。
Frame information
ETW event header information
ETW event descriptor (Constant information about the event ID such
as error level)
Event payload (Data logged at the time of the event)
Name of a USB-specific structure
Structure members and their values (Types: numbers, strings,
or arrays)
...
USB Hub Wait Wake IRP Completed イベントのペイロード データを展開すると、fid_USBHUB_Hub という名前の ETW 構造体が表示されます。 構造体名は、以下の部分で構成されています。
用語 | 説明 |
---|---|
fid_ | USB ETW 構造体の一般的なプレフィックス。 |
USBHUB_ | USB ハブ ドライバーがイベントを記録したことを示します。 |
文字列の残りの部分 | 構造体のデータが表すオブジェクトの名前。 このイベントの場合は、ハブ オブジェクトです。 |
USB ハブ ドライバーは、fid_USBHUB_Hub 構造体を使用して USB ハブを記述します。 データ ペイロードにこのハブ構造を持つイベントはハブを参照し、構造の内容を使用して特定のハブを識別できます。 図 4 は、[フレームの詳細] ペインを示しています。fid_USBHUB_Hub 構造が展開され、フィールドが表示されています。
ハブの構造は、USB ETW イベントに一般的に表示される他の 2 つの構造体 (fid_USBHUB_Device および fid_USBPORT_Device) とよく似ています。 以下は、3 つの構造すべてに共通した重要なフィールドです。
フィールド | 説明 |
---|---|
fid_idVendor | デバイスの USB ベンダー ID (VID) |
fid_idProduct | デバイスの USB 製品 ID (PID) |
fid_PortPath | USB デバイスの接続に使用される、1 から始まるハブ ポート番号のリスト。 リスト内のポート番号の数は、PortPathDepth フィールドに含まれます。 ルート ハブ デバイスの場合、このリストはすべてゼロです。 ルート ハブ ポートに直接接続されている USB デバイスの場合、PortPath[0] の値は、デバイスが接続されているポートのルート ハブ ポート番号です。 |
1 つ以上の追加の USB ハブを介して接続されている USB デバイスの場合、ハブ ポート番号のリストはルート ハブ ポートから始まり、追加のハブ (ルート ハブからの距離の順) に続きます。 ゼロはすべて無視できます。 次に例を示します。
値の例 | 説明 |
---|---|
[0, 0, 0, 0, 0, 0] | このイベントは、ルート ハブ (USB ホスト コントローラーによって直接制御される PC 上のポート) を指します。 |
[3, 0, 0, 0, 0, 0] | このイベントは、ルート ハブのポート番号 3 に接続されているハブまたはデバイスを指します。 |
[3, 1, 0, 0, 0, 0] | ハブは、ルート ハブのポート 3 に接続されています。 このイベントは、この外部ハブのポート 1 に接続されているハブまたはデバイスを指します。 |
対象となるデバイスのポート パスを監視する必要があります。 デバイスが列挙される場合、VID と PID は不明であり、0 として記録されます。 VID と PID は、リセットやサスペンドなど、一部の低レベルのデバイス要求中には表示されません。 これらの要求は、デバイスが接続されているハブに送信されます。
このサンプル ログでは、Wait Wake 完了イベントには 6 つのゼロを含むポート パスがあります。 このイベントは、ルート ハブ上の Wait Wake アクションを示します。 これは、実行した操作を考えれば当然のことです。デバイスをルート ハブ ポートに接続したため、ルート ハブが起動しています。
USB Netmon フィルター
時間があれば、ログ内の各イベントを時系列に調べることができます。 経験があったとしても、イベントの説明一覧に目を通して重要なイベントを迅速に特定することは容易ではありません。 不明なデバイスの原因をより迅速に見つけるには、Netmon フィルター機能を使用できます。
USB エラー フィルター
Netmon で USB エラー フィルターをアクティブ化するには、[フィルター] -> [表示フィルター] -> [フィルターの読み込み] -> [標準フィルター] -> [USB ] -> [USB ハブ エラー] の順にクリックし、[表示フィルター] ペインで [適用] をクリックします。
USB エラー フィルターは、次の表に示された基準を満たすイベントのみに限定して、イベントのリストを絞り込みます。
フィルター テキスト | 説明 |
---|---|
(USBPort_MicrosoftWindowsUSBUSBPORT AND NetEvent.Header.Descriptor.Opcode == 34) | オペコード 34 の USB ポート イベントはポート エラーです。 |
(USBHub_MicrosoftWindowsUSBUSBHUB AND NetEvent.Header.Descriptor.Opcode == 11) | オペコード 11 の USB ハブ イベントはハブ エラーです。 |
(NetEvent.Header.Descriptor.Level == 0x2) | 通常、レベル 0x2 のイベントはエラーです。 |
(USBHub_MicrosoftWindowsUSBUSBHUB AND NetEvent.Header.Descriptor.Id == 210) | ID 210 の USB ハブ イベントは、"USB Hub Exception Logged" イベントです。 詳細については、「エラー イベントと状態コードについて」を参照してください。 |
この画像は、サンプル トレース ログに USB エラー フィルターを適用した後、[フレームの概要] ペインに表示される小規模なイベント セットを示しています。
一連のエラーの概要を確認するために、各エラー イベントを簡単に確認できます。 注意を要する重要なフィールドには、fid_NtStatus、fid_UsbdStatus、fid_DebugText などがあります。 詳細については、「エラー イベントと状態コードについて」を参照してください。 フィルターをオフにするには、[表示フィルター] ペインの [削除] ボタンをクリックします。
カスタムの Netmon フィルター
Netmon では、カスタム フィルターを作成できます。 最も簡単な方法は、次のいずれかの方法で画面上のデータからフィルターを作成することです。
- [フレームの詳細] ウィンドウでフィールドを右クリックし、[選択した値を表示フィルターに追加] を選択します。
- [フレームの概要] ペインでフィールドを右クリックし、[[フィールド名] を表示フィルターに追加] を選択します。
演算子 (OR、AND、== など) とフィルター値を変更して、適切なフィルター式を作成できます。
エラー イベントと状態コードについて
ここに示した不明なデバイスの例では、ほとんどの USB ハブ例外には fid_DebugText データとして CreateDeviceFailure が示されています。 例外の重大度は明らかではありませんが、このデバッグ テキストには原因に関するヒントが示されています。つまり、新しいデバイスに関連する操作が失敗したということです。 ここでは、近辺の Create Device Failed イベントが冗長であると仮定します。 最後の 2 つの例外は、CreateDeviceFailure_Popup と GenErr_UserIoctlFailed です。 popup 例外はユーザーに公開されたエラーのように見えますが、これらのエラーはすべて、不明なデバイスの問題に関連している可能性があります。
USB エラー イベントやその他のイベントのデータには、問題に関する貴重な情報を提供する状態値が含まれています。 状態の値に関する情報は、次の表のリソースを使用して確認できます。
状態のタイプ | リソース |
---|---|
fid_NtStatus | NTSTATUS の値に関する記事を参照してください。 |
USB 要求ブロック (URB) の状態フィールドまたは fid_UsbdStatus | Windows Driver Kit (WDK) の inc\api\usb.h で値を USBD_STATUS として検索してください。 また、USBD_STATUS を使用することもできます。 このトピックには、USBD_STATUS 値のシンボリック名と意味が掲載されています。 |
問題のあるイベントから逆方向に読み取る
エラー イベントの前にログに記録されたイベントは、エラーの原因に関する重要な手掛かりとなる可能性があります。 不明なデバイスの根本原因を特定するには、エラーの前にログ記録されたイベントを調べる必要があります。 この例では、最後から 2 番目の例外である CreateDeviceFailure_Popup イベントから遡って調べ始めます。 USB エラー フィルターが有効な状態でこのイベントを選択し、[表示フィルター] ペインで [削除] をクリックします。 USB エラー フィルターは引き続き [表示フィルター] ペインに表示され、後で再適用できます。 ただし、この時点ではフィルターが無効になり、次の図に示すように、[フレームの概要] ペインにすべてのイベントが表示されます。
CreateDeviceFailure_Popup イベントの直前にログ記録された 2 つのイベントは、USB 制御転送の Dispatch と Complete です。 どちらのイベントでも fid_USBPORT_Device ポート パス フィールドは 0 であり、転送のターゲットがルート ハブであることを示しています。 完了イベントである fid_USBPORT_URB_CONTROL_TRANSFER 構造体の状態は 0 (USBD_STATUS_SUCCESS) で、転送が成功したことを示します。 引き続き、先に発生したイベントを調査していきましょう。
その前の 2 つのイベントは、前に調べた 4 番目 (最後) の Create Device Failed イベントと 4 番目 (最後) の CreateDeviceFailure 例外です。
その前のイベントは Endpoint Close です。 このイベントは、エンドポイントが使用できなくなったことを意味します。 イベント データには、デバイスとそのデバイス上のエンドポイントの両方が示されています。 デバイス のポート パスは [1, 0, 0, 0, 0, 0] です。 トレースを実行したシステムに含まれているのは、ホスト コントローラー (ルート ハブ) と接続していたデバイスのみであるため、このポート パスでハブは示されていません。 クローズド エンドポイントは、接続した単一のデバイス上に存在する必要があり、これでデバイスのパスが 1 であることがわかります。 先に発生した問題が原因で、ドライバーがデバイスのエンドポイントにアクセスできなくなった可能性があります。 引き続き、先に発生したイベントを調査していきましょう。
その前のイベントは、USB 制御転送の完了です。 イベント データには、転送のターゲットがデバイスであることが示されています (ポート パスが 1)。 fid_USBPORT_Endpoint_Descriptor 構造体で、エンドポイントのアドレスが 0 であることが示されており、これが USB 定義の既定の制御エンドポイントです。 URB の状態は 0xC0000004 です。 状態が 0 ではないため、転送は成功しなかった可能性があります。 この USBD_STATUS 値の詳細については、usb.h に関する説明と、「エラー イベントと状態コードについて」を参照してください。
#define USBD_STATUS_STALL_PID ((USBD_STATUS)0xC0000004L)
意味: デバイスがパケット停止識別子を返しました。 このエンドポイントで停止されたのは、どのような要求でしょうか? このイベントについて記録された他のデータは、要求が標準のデバイス制御要求であったことを示しています。 要求の解析内容は次のとおりです。
Frame: Number = 184, Captured Frame Length = 252, MediaType = NetEvent
+ NetEvent:
- MicrosoftWindowsUSBUSBPORT: Complete Internal URB_FUNCTION_CONTROL_TRANSFER
- USBPORT_ETW_EVENT_COMPLETE_INTERNAL_URB_FUNCTION_CONTROL_TRANSFER: Complete Internal URB_FUNCTION_CONTROL_TRANSFER
+ fid_USBPORT_HC:
+ fid_USBPORT_Device:
+ fid_USBPORT_Endpoint:
+ fid_USBPORT_Endpoint_Descriptor:
+ fid_URB_Ptr: 0x84539008
- ControlTransfer:
+ Urb: Status = 0xc0000004, Flags 0x3, Length = 0
- SetupPacket: GET_DESCRIPTOR
+ bmRequestType: (Standard request) 0x80
bRequest: (6) GET_DESCRIPTOR
Value_DescriptorIndex: 0 (0x0)
Value_DescriptorType: (1) DEVICE
_wIndex: 0 (0x0)
wLength: 64 (0x40)
bRequest (GET_DESCRIPTOR) と Value_DescriptorType (DEVICE) を組み合わせると、要求が get-device 記述子であったことを判断できます。
デバイスで USB 列挙を続行するには、デバイス記述子を使用してこの要求に応答する必要があります。 実際には、デバイスで要求が停止されたため、列挙が失敗しました。 したがって、4 つのデバイス作成エラーはすべて、デバイス記述子に対する要求の停止によって発生したということになります。 デバイスが不明なのは列挙が失敗したためであり、列挙が失敗したのは、デバイスでデバイス記述子の要求が完了されなかったためであると判断できました。