驱动中打印消息以及过滤机制

在驱动开发中打印消息主要使用DbgPrint/KdPrint函数,还有升级版的DbgPrintEx/KdPrintEx。

一、Dbg版本和Kd版本的区别

Dbg开头的才是正统的API,而Kd开头的只是一个宏,最终调用的还是DbgXXX方法,Kd版函数存在的意义是打印消息在Release版本中会被编译器优化掉,不会产生代码。

#if DBG 

#define KdPrint(_x_) DbgPrint _x_
#define KdPrintEx(_x_) DbgPrintEx _x_ 
#define vKdPrintEx(_x_) vDbgPrintEx _x_
#define vKdPrintExWithPrefix(_x_) vDbgPrintExWithPrefix _x_
#define KdBreakPoint() DbgBreakPoint() 

#define KdBreakPointWithStatus(s) DbgBreakPointWithStatus(s)

#else 

#define KdPrint(_x_)
#define KdPrintEx(_x_) 
#define vKdPrintEx(_x_)
#define vKdPrintExWithPrefix(_x_)
#define KdBreakPoint() 

#define KdBreakPointWithStatus(s)

#endif

DBG宏是用来判断Debug版本的,和Windows SDK中_DEBUG宏是一样的效果。

另外,Kd版函数调用时需要两对括号,像这样。

KdPrint(("KdPrint\n"));

二、消息过滤机制

驱动消息会用两个参数表示其类别:组件(Component)和等级(Level)。在dpfilter.h文件中有定义

//
// Component name filter id enumeration and levels.
//

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

typedef enum _DPFLTR_TYPE {
    DPFLTR_SYSTEM_ID = 0,
    DPFLTR_SMSS_ID = 1,
    DPFLTR_SETUP_ID = 2,
    DPFLTR_NTFS_ID = 3,
    DPFLTR_FSTUB_ID = 4,
    DPFLTR_CRASHDUMP_ID = 5,
    DPFLTR_CDAUDIO_ID = 6,
    DPFLTR_CDROM_ID = 7,
    DPFLTR_CLASSPNP_ID = 8,
    DPFLTR_DISK_ID = 9,
    DPFLTR_REDBOOK_ID = 10,
    DPFLTR_STORPROP_ID = 11,
    DPFLTR_SCSIPORT_ID = 12,
    DPFLTR_SCSIMINIPORT_ID = 13,
    DPFLTR_CONFIG_ID = 14,
    DPFLTR_I8042PRT_ID = 15,
    DPFLTR_SERMOUSE_ID = 16,
    DPFLTR_LSERMOUS_ID = 17,
    DPFLTR_KBDHID_ID = 18,
    DPFLTR_MOUHID_ID = 19,
    DPFLTR_KBDCLASS_ID = 20,
    DPFLTR_MOUCLASS_ID = 21,
    DPFLTR_TWOTRACK_ID = 22,
    DPFLTR_WMILIB_ID = 23,
    DPFLTR_ACPI_ID = 24,
    DPFLTR_AMLI_ID = 25,
    DPFLTR_HALIA64_ID = 26,
    DPFLTR_VIDEO_ID = 27,
    DPFLTR_SVCHOST_ID = 28,
    DPFLTR_VIDEOPRT_ID = 29,
    DPFLTR_TCPIP_ID = 30,
    DPFLTR_DMSYNTH_ID = 31,
    DPFLTR_NTOSPNP_ID = 32,
    DPFLTR_FASTFAT_ID = 33,
    DPFLTR_SAMSS_ID = 34,
    DPFLTR_PNPMGR_ID = 35,
    DPFLTR_NETAPI_ID = 36,
    DPFLTR_SCSERVER_ID = 37,
    DPFLTR_SCCLIENT_ID = 38,
    DPFLTR_SERIAL_ID = 39,
    DPFLTR_SERENUM_ID = 40,
    DPFLTR_UHCD_ID = 41,
    DPFLTR_RPCPROXY_ID = 42,
    DPFLTR_AUTOCHK_ID = 43,
    DPFLTR_DCOMSS_ID = 44,
    DPFLTR_UNIMODEM_ID = 45,
    DPFLTR_SIS_ID = 46,
    DPFLTR_FLTMGR_ID = 47,
    DPFLTR_WMICORE_ID = 48,
    DPFLTR_BURNENG_ID = 49,
    DPFLTR_IMAPI_ID = 50,
    DPFLTR_SXS_ID = 51,
    DPFLTR_FUSION_ID = 52,
    DPFLTR_IDLETASK_ID = 53,
    DPFLTR_SOFTPCI_ID = 54,
    DPFLTR_TAPE_ID = 55,
    DPFLTR_MCHGR_ID = 56,
    DPFLTR_IDEP_ID = 57,
    DPFLTR_PCIIDE_ID = 58,
    DPFLTR_FLOPPY_ID = 59,
    DPFLTR_FDC_ID = 60,
    DPFLTR_TERMSRV_ID = 61,
    DPFLTR_W32TIME_ID = 62,
    DPFLTR_PREFETCHER_ID = 63,
    DPFLTR_RSFILTER_ID = 64,
    DPFLTR_FCPORT_ID = 65,
    DPFLTR_PCI_ID = 66,
    DPFLTR_DMIO_ID = 67,
    DPFLTR_DMCONFIG_ID = 68,
    DPFLTR_DMADMIN_ID = 69,
    DPFLTR_WSOCKTRANSPORT_ID = 70,
    DPFLTR_VSS_ID = 71,
    DPFLTR_PNPMEM_ID = 72,
    DPFLTR_PROCESSOR_ID = 73,
    DPFLTR_DMSERVER_ID = 74,
    DPFLTR_SR_ID = 75,
    DPFLTR_INFINIBAND_ID = 76,
    DPFLTR_IHVDRIVER_ID = 77,
    DPFLTR_IHVVIDEO_ID = 78,
    DPFLTR_IHVAUDIO_ID = 79,
    DPFLTR_IHVNETWORK_ID = 80,
    DPFLTR_IHVSTREAMING_ID = 81,
    DPFLTR_IHVBUS_ID = 82,
    DPFLTR_HPS_ID = 83,
    DPFLTR_RTLTHREADPOOL_ID = 84,
    DPFLTR_LDR_ID = 85,
    DPFLTR_TCPIP6_ID = 86,
    DPFLTR_ISAPNP_ID = 87,
    DPFLTR_SHPC_ID = 88,
    DPFLTR_STORPORT_ID = 89,
    DPFLTR_STORMINIPORT_ID = 90,
    DPFLTR_PRINTSPOOLER_ID = 91,
    DPFLTR_VSSDYNDISK_ID = 92,
    DPFLTR_VERIFIER_ID = 93,
    DPFLTR_VDS_ID = 94,
    DPFLTR_VDSBAS_ID = 95,
    DPFLTR_VDSDYN_ID = 96,
    DPFLTR_VDSDYNDR_ID = 97,
    DPFLTR_VDSLDR_ID = 98,
    DPFLTR_VDSUTIL_ID = 99,
    DPFLTR_DFRGIFC_ID = 100,
    DPFLTR_DEFAULT_ID = 101,
    DPFLTR_MM_ID = 102,
    DPFLTR_DFSC_ID = 103,
    DPFLTR_WOW64_ID = 104,
    DPFLTR_ALPC_ID = 105,
    DPFLTR_WDI_ID = 106,
    DPFLTR_PERFLIB_ID = 107,
    DPFLTR_KTM_ID = 108,
    DPFLTR_IOSTRESS_ID = 109,
    DPFLTR_HEAP_ID = 110,
    DPFLTR_WHEA_ID = 111,
    DPFLTR_USERGDI_ID = 112,
    DPFLTR_MMCSS_ID = 113,
    DPFLTR_TPM_ID = 114,
    DPFLTR_THREADORDER_ID = 115,
    DPFLTR_ENVIRON_ID = 116,
    DPFLTR_EMS_ID = 117,
    DPFLTR_WDT_ID = 118,
    DPFLTR_FVEVOL_ID = 119,
    DPFLTR_NDIS_ID = 120,
    DPFLTR_NVCTRACE_ID = 121,
    DPFLTR_LUAFV_ID = 122,
    DPFLTR_APPCOMPAT_ID = 123,
    DPFLTR_USBSTOR_ID = 124,
    DPFLTR_SBP2PORT_ID = 125,
    DPFLTR_COVERAGE_ID = 126,
    DPFLTR_CACHEMGR_ID = 127,
    DPFLTR_MOUNTMGR_ID = 128,
    DPFLTR_CFR_ID = 129,
    DPFLTR_TXF_ID = 130,
    DPFLTR_KSECDD_ID = 131,
    DPFLTR_FLTREGRESS_ID = 132,
    DPFLTR_MPIO_ID = 133,
    DPFLTR_MSDSM_ID = 134,
    DPFLTR_UDFS_ID = 135,
    DPFLTR_PSHED_ID = 136,
    DPFLTR_STORVSP_ID = 137,
    DPFLTR_LSASS_ID = 138,
    DPFLTR_SSPICLI_ID = 139,
    DPFLTR_CNG_ID = 140,
    DPFLTR_EXFAT_ID = 141,
    DPFLTR_FILETRACE_ID = 142,
    DPFLTR_XSAVE_ID = 143,
    DPFLTR_SE_ID = 144,
    DPFLTR_DRIVEEXTENDER_ID = 145,
    DPFLTR_POWER_ID = 146,
    DPFLTR_CRASHDUMPXHCI_ID = 147,
    DPFLTR_GPIO_ID = 148,
    DPFLTR_REFS_ID = 149,
    DPFLTR_WER_ID = 150,
    DPFLTR_ENDOFTABLE_ID
} DPFLTR_TYPE;

大部分都是系统使用的,留给开发者使用的一般只有以下几个

DPFLTR_IHVVIDEO_ID
DPFLTR_IHVAUDIO_ID
DPFLTR_IHVNETWORK_ID
DPFLTR_IHVSTREAMING_ID
DPFLTR_IHVBUS_ID
DPFLTR_IHVDRIVER_ID

NT内核驱动一般用DPFLTR_IHVDRIVER_ID这个。Level也预设了几个

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

对于系统而言,它有一个公式,如果把你提供的Level带入计算后结果是一个非零值,就会将消息发送到调试器,否则消息会被过滤掉。

公式用一段C代码表示可以写成

unsigned Calc(const unsigned filterMask, const unsigned level)
{
	unsigned result = 0;
	if (level < 32) // 0~31
	{
		result = 1;
		result = result << level;
	}
	else
	{
		result = level;
	}
	return filterMask & result;
}

如果level值介于0~31时就表示位移的位数,否则自身就是level值。得到的结果再与FilterMask进行AND操作,结果如果是非零的话就会被发送给调试器。我猜测这样的设计是因为可以将Level数量限制在32个以内,另一个原因是就算你填了0也是可以的。

另一个filterMask是哪来的呢?它是由系统提供的,这个掩码默认是DPFLTR_ERROR_LEVEL,我们可以通过注册表来指定它

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Debug Print Filter]
"IHVDRIVER"=dword:00000008

当我们设置了掩码后需要重启计算机才可以生效。

上面这个注册表文件是什么意思呢?IHVDRIVER表示指定DPFLTR_IHVDRIVER_ID类别的掩码,值8的二进制是1000,根据计算公式,它是DPFLTR_INFO_LEVEL移位后的结果。

那么这么设置后,DPFLTR_IHVDRIVER_ID类别的DPFLTR_INFO_LEVEL级别消息将会被发送到调试器。

三、不被推荐的DbgPrint/KdPrint

在Windows XP、Windows Server 2003系统上DbgPrint函数不受消息过滤机制的影响,将会畅通无阻的将消息发送到调试器。

但是从Vista系统开始,DbgPrint函数被定义为DPFLTR_INFO_LEVEL级别,而系统在默认情况下只会接受ERROR_LEVEL的消息,其他等级的消息会被过滤掉,所以这也就造成了网上有很多类似"看不到DbgPrint打印的消息"这样的提问。

如果继续用DbgPrint方法的话,则在Vista以后的系统上你只能修改用户系统的过滤掩码(DPFLTR_INFO_LEVEL)后才能看到消息。

四、使用DbgPrintEx/KdPrintEx

由于DbgPrint在新的系统上被限制,所以建议使用可以指定component和level的DbgPrintEx函数。

KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "DPFLTR_ERROR_LEVEL\n"));
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_WARNING_LEVEL, "DPFLTR_WARNING_LEVEL\n"));
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, "DPFLTR_TRACE_LEVEL\n"));
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "DPFLTR_INFO_LEVEL\n"));

根据过滤机制来看,我猜微软是期望开发者只输出严重的错误消息,其他调试跟踪性的消息应只在开发阶段使用。

参考资料:

https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-dbgprint

https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-dbgprintex

https://docs.microsoft.com/zh-tw/windows-hardware/drivers/devtest/reading-and-filtering-debugging-messages

你可能感兴趣的:(Windows编程,驱动开发)