案例研究:使用 ETW 和 Netmon 針對未知的 USB 裝置進行疑難解答

本主題提供如何使用 USB ETW 和 Netmon 針對 Windows 無法辨識的 USB 裝置進行疑難解答的範例。

在此範例中,我們插入了裝置,並顯示為 裝置管理員 和使用者介面的其他部分的未知裝置, (UI) 。 硬體標識碼為 USB\UNKNOWN。 為了進一步診斷,我們已解除叢集裝置、開始 ETW 追蹤,然後再次插入裝置。 當裝置顯示為未知的裝置之後,我們已停止追蹤。

關於未知的裝置問題

若要對未知的 USB 裝置問題進行偵錯,它有助於瞭解當使用者將裝置插入系統時,USB 驅動程式堆疊會執行哪些動作來列舉裝置。 如需USB列舉的相關信息,請參閱標題為USB堆疊如何列舉裝置的部落格文章?

一般而言,當 USB 驅動程式堆疊無法列舉裝置時,中樞驅動程式仍會報告裝置抵達 Windows,且 USB 裝置在 裝置管理員 中標示為未知的裝置。 裝置具有 USB\VID_0000&PID_0000的裝置識別碼,以及 USB\UNKNOWN 的硬體識別碼和相容標識符。 下列事件會導致 USB 中樞驅動程式將 USB 裝置列舉為未知的裝置:

  • 埠重設要求在列舉期間逾時。
  • USB 裝置的 [設定位址] 要求失敗。
  • USB 裝置的裝置描述元要求失敗。
  • USB 裝置描述元格式不正確且驗證失敗。
  • 組態描述元的要求失敗。
  • USB 組態描述元格式不正確且驗證失敗。

在 Windows 7 中,失敗列舉的未知裝置會在 裝置管理員 中標示失敗代碼 43

如果裝置在 裝置管理員 中標示失敗代碼 28,則裝置已成功列舉,但仍為未知的裝置。 此失敗碼表示裝置未在列舉期間提供產品標識符字串,且 Windows 找不到裝置安裝驅動程式的相符 INF。

啟動事件追蹤分析

因為這是裝置失敗,建議您搭配USB剖析器使用Netmon來分析記錄檔。

檢視事件追蹤記錄檔

  1. 執行 Netmon,按兩下 [檔案 -> 開啟 -> 擷取],然後選取檔案。

  2. 選取 [ 框架摘要 ] 窗格中的第一個事件,其描述為 SystemTrace。 此影像顯示當您選取第一個事件時畫面的外觀。

    在選取第一個事件之後顯示 [Microsoft 網络監視器] 視窗的螢幕快照。

  3. 若要自定義 Netmon 顯示的數據行,請以滑鼠右鍵按兩下資料行名稱,然後選取 [ 選擇資料行]。

  4. 第一個識別為 SystemTrace 類型的事件包含記錄檔的一般資訊。 您可以在 [ 框架詳細 數據] 窗格中展開資訊樹狀結構,以查看遺失的事件數目和追蹤開始時間等資訊。

USB 裝置摘要事件

事件 2 是記錄檔中的第一個 USB 事件。 這和數個後續事件描述我們在啟動追蹤時連線到系統的USB主機控制器、中樞和裝置。 我們可以呼叫此事件群組的裝置摘要事件,或只呼叫摘要事件。 如同第一個事件,摘要事件不會描述驅動程序活動。 摘要事件會在記錄會話開始時記錄裝置的狀態。 其他事件代表總線上發生的情況、與客戶端驅動程式或系統的互動,或內部狀態的變更。

USB 中樞和 USB 埠驅動程式都會記錄摘要事件。 記錄事件的驅動程式會在 [通訊協定名稱] 資料行中識別。 例如,USB 埠驅動程式所記錄的事件具有USBPort_MicrosoftWindowsUSBPORT通訊協定名稱。 USB 事件追蹤通常包含埠摘要事件序列,後面接著一連串中樞摘要事件。 許多USB埠和USB中樞摘要事件在其描述中有「資訊」或「屬性」一詞。

如何識別摘要事件的結尾? 如果在記錄開頭的 USB 中樞事件中發生時間戳模式的重大中斷,該中斷可能是裝置摘要的結尾。 否則,任何 USB 中樞事件之後的第一個 USB 埠事件可能是第一個非摘要事件。 下圖 3 在下列頁面上顯示此範例追蹤中的第一個非摘要事件。

在此範例中,我們啟動追蹤時,感興趣的裝置並未連線到系統,因此您現在可以略過裝置摘要事件。

此螢幕快照顯示啟動追蹤時未連線的「畫面格摘要」中選取感興趣的裝置。

事件描述和數據承載

在範例記錄檔中,裝置摘要事件之後的第一個事件是USB中樞等候喚醒 IRP 已完成事件。 我們插入了裝置,而主機控制器或中樞正在響應中喚醒。 若要判斷哪個元件正在喚醒,請查看事件的數據。 資料位於 [框架詳細資料] 窗格中,其以大約下列形式顯示在樹狀結構中:

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 中樞等候喚醒 IRP 已完成事件的承載數據,您會看到名為 fid_USBHUB_Hub 的 ETW 結構。 結構的名稱具有下列元件:

詞彙 描述
裂_ USB ETW 結構的一般前置詞。
USBHUB_ 指出 USB 中樞驅動程式已記錄事件。
字串的其餘部分 結構數據所描述的物件名稱。 針對此事件,它是 Hub 物件。

USB 中樞驅動程式會使用 fid_USBHUB_Hub 結構來描述 USB 中樞。 在其數據承載中具有這個中樞結構的事件是指中樞,我們可以使用 結構的內容來識別特定中樞。 圖 4 顯示 [框架詳細數據] 窗格,其中 已展開fid_USBHUB_Hub 結構以顯示其欄位。

microsoft 網路監視器 - 框架詳細數據。

中樞結構非常類似於USB ETW事件:fid_USBHUB_Devicefid_USBPORT_Device中經常出現的兩個其他結構。 下列重要欄位適用於所有三個結構:

欄位 Description
fid_idVendor 裝置的 USB 廠商標識碼 (VID)
fid_idProduct 裝置的 USB 產品標識碼 (PID)
fid_PortPath 連結USB裝置的一個型中樞埠號碼清單。 清單中的埠號碼包含在 [PortPathDepth ] 字段中。 針對根中樞裝置,此清單全都是零。 針對直接連線到根中樞埠的 USB 裝置,PortPath[0] 中的值是裝置所連結之埠的根中樞埠號碼。

針對透過一或多個其他 USB 中樞連線的 USB 裝置,中樞埠號碼的清單會以根中樞埠開頭,並依與根中樞) 距離的順序繼續執行其他中樞 (。 忽略任何零。 例如:

範例值 Description
[0, 0, 0, 0, 0, 0] 此事件是指計算機上的埠 (根中樞,由USB主機控制器直接控制) 。
[3, 0, 0, 0, 0, 0] 此事件是指插入根中樞埠號碼 3 的中樞或裝置。
[3, 1, 0, 0, 0, 0] 中樞已插入根中樞的埠 3。 此事件是指插入此外部中樞埠 1 的中樞或裝置。

您應該監視任何感興趣的裝置的埠路徑。 列舉裝置時,VID 和 PID 是未知的,並記錄為 0。 VID 和 PID 不會出現在某些低階裝置要求期間,例如重設和暫停。 這些要求會傳送至裝置已插入的中樞。

在我們的範例記錄檔中,等候喚醒完成事件有六個零的埠路徑。 事件指出根中樞上的等候喚醒動作。 這是邏輯的,因為我們的動作:我們已將裝置插入根中樞埠,因此根中樞會喚醒。

USB Netmon 篩選器

如果您有時間,您可以在記錄檔中依時間順序檢查每個事件。 即使體驗過,也很難藉由掃描事件描述的清單來快速識別重要事件。 若要更快速地找出未知裝置的原因,您可以使用 Netmon 篩選功能。

USB 錯誤篩選器

若要在 Netmon 中啟用 USB 錯誤篩選器,請按下 [篩選 - 顯示篩選 ->> 載入篩選器 -> 標準篩選 - USB -> USB 中>樞錯誤],然後按兩下 [顯示篩選] 窗格中的 [套用]。

USB 錯誤篩選條件只會將事件清單縮小為符合下表所示準則的事件清單。

篩選文字 Description
(USBPort_MicrosoftWindowsUSBUSBPORT AND NetEvent.Header.Descriptor.Opcode == 34) 具有 opcode 34 的 USB 連接埠事件是埠錯誤。
(USBHub_MicrosoftWindowsUSBUSBHUB AND NetEvent.Header.Descriptor.Opcode == 11) 具有 opcode 11 的 USB 中樞事件是中樞錯誤。
(NetEvent.Header.Descriptor.Level == 0x2) 具有層級0x2的事件通常是錯誤。
(USBHub_MicrosoftWindowsUSBUSBHUB AND NetEvent.Header.Descriptor.Id == 210) 標識元為 210 的 USB 中樞事件是「USB 中樞例外狀況記錄」事件。 如需詳細資訊,請參閱 瞭解錯誤事件和狀態代碼

此影像顯示在將 USB 錯誤篩選套用至範例追蹤記錄檔之後,出現在 [ 框架摘要 ] 窗格中的較小事件集。

此螢幕快照顯示套用 USB 錯誤篩選條件之後,[框架摘要] 窗格中的一組事件。

若要查看錯誤順序的概觀,您可以簡短檢視每個錯誤事件。 要觀察的重要欄位包括 fid_NtStatusfid_UsbdStatusfid_DebugText。 如需詳細資訊,請參閱 瞭解錯誤事件和狀態代碼。 若要關閉篩選,請按兩下 [顯示篩選] 窗格中的 [移除] 按鈕。

自訂 Netmon 篩選

您可以在 Netmon 中建立自訂篩選。 最簡單的方法是使用下列其中一種方式,從螢幕上的數據建立篩選:

  • 以滑鼠右鍵按兩下 [框架詳細資料 ] 窗格中的欄位,然後選取[ 新增選取的值以顯示篩選]。
  • 以滑鼠右鍵按兩下 [框架摘要 ] 窗格中的欄位,然後選取[ 新增[功能變數名稱] 以顯示篩選

您可以變更運算子 (,例如 OR、AND 和 ==) 和篩選值,以建立適當的篩選表達式。

瞭解錯誤事件和狀態代碼

在我們的未知裝置範例中,大部分的USB中樞例外狀況都有 CreateDeviceFailure 的fid_DebugText 數據。 這並不清楚例外狀況有多嚴重,但偵錯文字會提供提示,說明原因:與新裝置相關的作業失敗。 現在,假設相鄰的建立裝置失敗事件是多餘的。 最後兩個例外狀況是CreateDeviceFailure_Popup和GenErr_UserIoctlFailed。 快顯例外狀況聽起來像是向使用者公開的錯誤,但任何和所有錯誤都可能與未知的裝置問題有關。

USB 錯誤事件和其他事件在其數據中有狀態值,可提供問題的重要資訊。 您可以使用下表中的資源來尋找狀態值的相關信息。

狀態類型 資源
fid_NtStatus 請參閱 NTSTATUS值
USB 要求區塊的狀態字段 (URB) 或 fid_UsbdStatus 在 Windows 驅動程式套件 (WDK) 中,將值查閱為 inc\api\usb.h 中的USBD_STATUS。 您也可以使用 USBD_STATUS。 本主題列出USBD_STATUS值的符號名稱和意義。

從問題事件回溯讀取

在錯誤事件之前記錄的事件可能會提供錯誤原因的重要線索。 您應該查看錯誤之前所記錄的事件,以嘗試判斷未知裝置的根本原因。 在此範例中,從CreateDeviceFailure_Popup事件開始回溯查看,也就是第二到最後一個例外狀況。 啟用 USB 錯誤篩選時選取此事件,然後按下 [顯示篩選] 窗格中的 [移除]。 USB 錯誤篩選仍然會出現在 [ 顯示篩選] 窗格中,您稍後可以重新套用它。 但現在篩選已停用,[ 框架摘要 ] 窗格會顯示此影像中顯示的所有事件。

microsoft 網路監視器。

在CreateDeviceFailure_Popup事件之前記錄的兩個事件是 Dispatch 和 Complete of a USB 控件傳輸。 這兩個事件的 fid_USBPORT_Device 埠路徑欄位都是零,這表示傳輸的目標是根中樞。 在完成事件的fid_USBPORT_URB_CONTROL_TRANSFER結構中,狀態為零 (USBD_STATUS_SUCCESS) ,表示傳輸成功。 繼續檢查先前的事件。

接下來的兩個前兩個事件是第四個 (最終) 建立裝置失敗事件,第四個 (最後一個) CreateDeviceFailure 例外狀況,我們稍早會檢查此事件。

下一個上一個事件是 Endpoint Close。 此事件表示端點已無法使用。 事件數據會描述該裝置上的裝置和端點。 裝置埠路徑為 [1, 0, 0, 0, 0, 0] 。 我們執行追蹤的系統只有主控制器 (根中樞) 加上我們連線的裝置,因此此埠路徑不會描述中樞。 關閉的端點必須位於我們插入的單一裝置上,現在我們知道裝置的路徑是 1。 驅動程式可能會因為稍早遇到的問題而使裝置的端點無法存取。 繼續檢查先前的事件。

下一個上一個事件是已完成的USB控制傳輸。 事件數據顯示傳輸的目標為裝置, (埠路徑為1) 。 fid_USBPORT_Endpoint_Descriptor 結構表示端點的位址為 0,因此這是 USB 定義的預設控制端點。 URB 狀態為 0xC0000004。 因為狀態不是零,所以傳輸可能不成功。 如需此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 列舉繼續,裝置應該已使用其裝置描述項來回應此要求。 相反地,裝置會停止要求,導致列舉失敗。 因此,所有四個建立裝置失敗都是因為裝置描述元的要求停止所造成。 您已判斷裝置未知,因為列舉失敗,且列舉失敗,因為裝置未完成其裝置描述元的要求。