案例研究:使用 ETW 和 Netmon 排查未知 USB 设备的问题

本主题提供有关如何使用 USB ETW 和 Netmon 对 Windows 无法识别的 USB 设备进行故障排除的示例。

在本示例中,我们插入了一个设备,它在用户界面 (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 字符串,并且 Windows 找不到设备安装驱动程序的匹配 INF。

启动事件跟踪分析

由于这是设备故障,建议将 Netmon 与 USB 分析程序配合使用来分析日志文件。

查看事件跟踪日志

  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 集线器驱动程序记录了事件。
字符串的其余部分 结构的数据所描述的对象的名称。 对于此事件,它是一个中心对象。

USB 集线器驱动程序使用 fid_USBHUB_Hub 结构来描述 USB 集线器。 在其数据有效负载中具有此中心结构的事件引用中心,我们可以通过使用结构的内容来标识特定的中心。 图 4 显示了“帧详细信息”窗格,其中 展开了fid_USBHUB_Hub 结构以显示其字段。

microsoft 网络监视器 - 帧详细信息。

集线器结构与 USB ETW 事件中常见的其他两个结构非常相似:fid_USBHUB_Devicefid_USBPORT_Device。 以下重要字段对所有三个结构通用:

字段 说明
fid_idVendor 设备的 USB 供应商 ID (VID)
fid_idProduct 设备的 USB 产品 ID (PID)
fid_PortPath USB 设备连接到的基于 1 的集线器端口号的列表。 列表中的端口号包含在 PortPathDepth 字段中。 对于根中心设备,此列表全部为零。 对于直接连接到根集线器端口的 USB 设备,PortPath[0] 中的值是设备连接到的端口的根集线器端口号。

对于通过一个或多个其他 USB 集线器连接的 USB 设备,集线器端口号列表从根集线器端口开始,并按照与根集线器) 的距离顺序继续与其他集线器 (。 忽略任何零。 例如:

示例值 说明
[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 错误筛选器将事件列表缩小为仅满足下表所示条件的事件。

筛选文本 说明
(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) ID 为 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 control transfer。 对于这两个事件, fid_USBPORT_Device 端口路径字段均为零,这表示传输的目标是根中心。 在完成事件的fid_USBPORT_URB_CONTROL_TRANSFER结构中,状态为零 (USBD_STATUS_SUCCESS) ,表示传输成功。 继续检查以前的事件。

接下来的两个事件是创建设备失败事件) (第四个 (最后) CreateDeviceFailure 异常,我们之前进行了检查。

下一个事件是 Endpoint Close。 此事件意味着终结点不再可用。 事件数据描述该设备和该设备上的终结点。 设备端口路径为 [1, 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 枚举,设备应已使用其设备描述符响应此请求。 相反,设备停止了请求,从而导致枚举失败。 因此,所有四个创建设备故障都是由设备描述符请求停止引起的。 你已确定设备未知,因为枚举失败,枚举失败,因为设备未完成对其设备描述符的请求。