读取和筛选调试消息

DbgPrintExvDbgPrintExvDbgPrintExWithPrefixKdPrintEx 例程在指定的条件下向内核调试器发送消息。 此过程使你能够筛选出低优先级消息。

注意

在 Microsoft Windows Server 2003 和早期版本的 Windows 中, DbgPrintKdPrint 例程无条件地向内核调试器发送消息。 在 Windows Vista 和更高版本的 Windows 中,这些例程按条件发送消息,如 DbgPrintExKdPrintEx。 无论使用哪个版本的 Windows,都应使用 DbgPrintExvDbgPrintExvDbgPrintExWithPrefixKdPrintEx,因为这些例程使你能够控制发送消息的条件。

筛选调试消息

  1. 对于要发送到调试器的每条消息,请在驱动程序代码中使用 DbgPrintExvDbgPrintExvDbgPrintExWithPrefixKdPrintEx 。 将相应的组件名称传递给 ComponentId 参数,并将值传递给反映此消息的严重性或性质的 Level 参数。 消息本身通过使用与 printf 相同的语法传递给 Formatarguments 参数。

  2. 设置相应 组件筛选器掩码的值。 每个组件都有不同的掩码。 掩码值指示显示该组件的消息中的哪一个。 可以使用注册表编辑器或在内存中使用内核调试器在注册表中设置组件筛选器掩码。

  3. 将内核调试器附加到计算机。 每次驱动程序将消息传递给 DbgPrintExvDbgPrintExvDbgPrintExWithPrefixKdPrintEx 时,传递给 ComponentIdLevel 的值都会与相应的组件筛选器掩码的值进行比较。 如果这些值满足特定条件,则会将消息发送到内核调试器并显示。 否则,不会发送任何消息。

注意

此页上对 DbgPrintEx 的所有引用同样适用于 KdPrintExvDbgPrintExvDbgPrintExWithPrefix

标识组件名称

每个组件都有一个单独的筛选器掩码。 这允许调试器单独为每个组件配置筛选器。

根据上下文,以不同的方式引用每个组件。 在 DbgPrintExComponentId 参数中,组件名称的前缀为“DPFLTR_”,后缀为“_ID”。 在注册表中,组件筛选器掩码与组件本身同名。 在调试器中,组件筛选器掩码的前缀为“Kd_”,后缀为“_Mask”。

Microsoft Windows 驱动程序工具包 (WDK) 标头 dpfilter.h 中 (DPFLTR_XXXX_ID格式) 的所有组件名称的完整列表。 其中大多数组件名称是为 Windows 和 Microsoft 编写的驱动程序保留的。

有六个组件名称保留给独立硬件供应商。 若要避免将驱动程序的输出与 Windows 组件的输出混合,应使用以下组件名称之一:

组件名称 驱动程序类型
IHVVIDEO 视频驱动程序
IHVAUDIO 音频驱动程序
IHVNETWORK 网络驱动程序
IHVSTREAMING 内核流式处理驱动程序
IHVBUS 总线驱动程序
IHVDRIVER 任何其他类型的驱动程序

例如,如果要编写视频驱动程序,请使用 DPFLTR_IHVVIDEO_ID 作为 DbgPrintExComponentId 参数,在注册表中使用值名称 IHVVIDEO,并在调试器中引用Kd_IHVVIDEO_Mask

DbgPrintKdPrint 发送的所有消息都与 DEFAULT 组件相关联。

选择正确的级别

DbgPrintEx 例程的 Level 参数的类型为 DWORD。 它用于确定 重要性位字段Level 参数与此位字段之间的连接取决于 Level 的大小:

  • 如果 Level 等于介于 0 和 31(含)之间的数字,则解释为位移。 重要性位字段设置为值 1 <<级别。 因此,为 Level 选择介于 0 和 31 之间的值会生成一个恰好设置了一个位的位字段。 如果 Level 为 0,则位字段等效于0x00000001;如果 Level 为 31,则位字段等效于 0x80000000。

  • 如果 Level 是介于 32 和 0xFFFFFFFF(含)之间的数字,则重要性位字段设置为 Level 本身的值。

因此,如果要将位字段设置为0x00004000,则可以将 Level 指定为 0x00004000 或仅指定为 14。 请注意,此系统无法提供某些位字段值,包括完全为零的位字段。

以下常量可用于设置 Level 的值。 它们在 Microsoft Windows 驱动程序工具包 (WDK) 标头 dpfilter.h 和 Windows SDK 标头 ntrtl.h 中定义:

#define   DPFLTR_ERROR_LEVEL     0
#define   DPFLTR_WARNING_LEVEL   1
#define   DPFLTR_TRACE_LEVEL     2
#define   DPFLTR_INFO_LEVEL      3
#define   DPFLTR_MASK   0x80000000

使用 Level 参数的一种简单方法是始终使用介于 0 和 31 之间的值 -- 使用位 0、1、2、3 以及DPFLTR_XXXX_LEVEL给出的含义,并使用其他位表示你选择的任何值。

使用 Level 参数的另一种简单方法是始终使用显式位字段。 如果选择此方法,则可能希望将值与位字段DPFLTR_MASK OR;这可确保不会意外使用小于 32 的值。

为了使驱动程序与 Windows 使用消息级别的方式兼容,只应在发生严重错误时设置重要性位字段的最低位 (0x1) 。 如果使用的 级别 值小于 32,则对应于DPFLTR_ERROR_LEVEL。 如果设置了此位,则每当有人将内核调试器附加到运行驱动程序的计算机时,都会看到你的消息。

应在适当情况下使用警告、跟踪和信息级别。 其他位可以自由用于你认为有用的任何目的。 这允许你拥有各种可以选择性地查看或隐藏的消息类型。

DbgPrintKdPrint 发送的所有消息的行为类似于级别等于 DPFLTR_INFO_LEVEL 的 DbgPrintExKdPrintEx 消息。 换句话说,这些消息设置了其重要性位字段的第三位。

设置组件筛选器掩码

有两种方法可以设置组件筛选器掩码:

  • 可以在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Debug Print Filter 的注册表项中 访问组件筛选器掩码。 使用注册表编辑器创建或打开此密钥。 在此键下,使用所需组件的名称(大写)创建一个值。 将其设置为要用作组件筛选器掩码的 DWORD 值。

  • 如果内核调试器处于活动状态,它可以通过取消引用存储在符号 Kd_XXXX_Mask 中的地址来访问组件筛选器掩码值,其中 XXXX 是所需的组件名称。 可以使用 dd (Display DWORD) 命令在 WinDbg 或 KD 中显示此掩码的值,或者使用 ed (Enter DWORD) 命令输入新的组件筛选器掩码。 如果存在符号歧义的危险,建议将此符号指定为 nt!Kd_XXXX_Mask

存储在注册表中的筛选器掩码在启动期间生效。 调试器创建的筛选器掩码会立即生效,并一直持续到 Windows 重新启动。 调试器可以重写注册表中设置的值,但如果系统重新启动,组件筛选器掩码将返回到注册表中指定的值。

还有一个名为 “WIN2000”的系统级掩码。 默认情况下,这等于0x1,但可以像所有其他组件一样通过注册表或调试器对其进行更改。 执行筛选时,每个组件筛选器掩码首先使用 WIN2000 掩码进行 ORed。 具体而言,这意味着从未指定掩码的组件默认0x1。

显示消息的条件

在内核模式代码中调用 DbgPrintEx 时,Windows 会将 Level 指定的消息重要性位字段与 ComponentId 指定的组件的筛选器掩码进行比较。

注意

回想一下,当 Level 参数介于 0 和 31 之间时,重要性位字段等于 1 <<Level。 但是,当 Level 参数为 32 或更高时,重要性位字段仅等于 Level

Windows 对重要性位字段和组件筛选器掩码执行 AND 操作。 如果结果为非零,则会将消息发送到调试器。

调试筛选器示例

假设在上次启动之前,你在 “调试打印筛选器” 键中创建了以下值:

  • IHVVIDEO,其值等于 DWORD 0x2

  • IHVBUS,等于 DWORD 0x7FF

现在,在内核调试器中发出以下命令:

kd> ed Kd_IHVVIDEO_Mask 0x8 
kd> ed Kd_IHVAUDIO_Mask 0x7 

此时,IHVVIDEO 组件具有0x8的筛选器掩码,IHVAUDIO 组件的筛选器掩码为 0x7,IHVBUS 组件的筛选器掩码为 0x7FF。

但是,由于这些掩码会自动使用 WIN2000 系统范围的掩码 ((通常等于 0x1) )进行 ORed, 因此 IHVVIDEO 掩码实际上等于0x9。 事实上,尚未设置筛选器掩码的组件 (例如 IHVSTREAMINGDEFAULT) 将具有0x1的筛选器掩码。

现在假设以下函数调用发生在各种驱动程序中:

DbgPrintEx( DPFLTR_IHVVIDEO_ID,  DPFLTR_INFO_LEVEL,   "First message.\n");
DbgPrintEx( DPFLTR_IHVAUDIO_ID,  7,                   "Second message.\n");
DbgPrintEx( DPFLTR_IHVBUS_ID,    DPFLTR_MASK | 0x10,  "Third message.\n");
DbgPrint( "Fourth message.\n");

第一条消息的 Level 参数等于 DPFLTR_INFO_LEVEL,即 3。 由于小于 32,因此被视为位移,导致重要性位字段0x8。 然后,使用 0x9 的有效 IHVVIDEO 组件筛选器掩码对此值进行 AND,从而得出非零结果。 因此,第一条消息将传输到调试器。

第二条消息的 Level 参数等于 7。 同样,这被视为位移,导致重要性位字段0x80。 然后使用 0x7 的 IHVAUDIO 组件筛选器掩码进行 ANDed,结果为零。 因此,不会传输第二条消息。

第三条消息的 Level 参数等于 DPFLTR_MASK |0x10。 此值大于 31,因此重要性位字段设置为等于 Level 的值,即0x80000010。 然后,使用 0x7FF 的 IHVBUS 组件筛选器掩码进行 ANDed,从而得到非零结果。 因此,第三条消息将传输到调试器。

第四条消息传递到 DbgPrint ,而不是 DbgPrintEx。 在 Windows Server 2003 和早期版本的 Windows 中,始终传输传递到此例程的消息。 在 Windows Vista 和更高版本的 Windows 中,始终为传递到此例程的消息提供默认筛选器。 重要性位字段等于 1 << DPFLTR_INFO_LEVEL,这是0x00000008。 此例程的组件为 DEFAULT。 由于尚未设置 DEFAULT 组件筛选器掩码,因此其值为 0x1。 当使用重要性位字段进行 ANDed 时,结果为零。 因此,不会传输第四条消息。

DbgPrint 缓冲区和调试器

DbgPrintDbgPrintExvDbgPrintExvDbgPrintExWithPrefixKdPrintKdPrintEx 例程将消息传输到调试器时,格式化字符串将发送到 DbgPrint 缓冲区。 除非使用 GFlags 的 Buffer DbgPrint 输出 选项禁用了此显示,否则此缓冲区的内容将立即显示在“调试器命令”窗口中。

在本地内核调试期间,以及禁用此显示的任何其他时间,只能使用 !dbgprint 扩展命令查看 DbgPrint 缓冲区的内容。

DbgPrintDbgPrintExvDbgPrintExvDbgPrintExWithPrefixKdPrintKdPrintEx 的任何 单个调用仅传输 512 字节的信息。 超过 512 字节的任何输出都将丢失。 DbgPrint 缓冲区本身在 Windows 免费版本中最多可以容纳 4 KB 数据,在已检查的 Windows 版本中最多可以容纳 32 KB 数据。 在 Windows Server 2003 及更高版本的 Windows 上,可以使用 KDbgCtrl 工具来更改 DbgPrint 缓冲区的大小。 此工具是 Windows 调试工具的一部分。

注意

Windows 10 版本 1803 之前的旧版 Windows 上提供已检查的版本。 使用驱动程序验证器和 GFlags 等工具在更高版本的 Windows 中检查驱动程序代码。

如果消息因 ComponentIdLevel 值而被筛选掉,则不会通过调试连接传输该消息。 因此,无法在此调试器中显示此消息。