在驱动开发中打印消息主要使用DbgPrint/KdPrint函数,还有升级版的DbgPrintEx/KdPrintEx。
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级别消息将会被发送到调试器。
在Windows XP、Windows Server 2003系统上DbgPrint函数不受消息过滤机制的影响,将会畅通无阻的将消息发送到调试器。
但是从Vista系统开始,DbgPrint函数被定义为DPFLTR_INFO_LEVEL级别,而系统在默认情况下只会接受ERROR_LEVEL的消息,其他等级的消息会被过滤掉,所以这也就造成了网上有很多类似"看不到DbgPrint打印的消息"这样的提问。
如果继续用DbgPrint方法的话,则在Vista以后的系统上你只能修改用户系统的过滤掩码(DPFLTR_INFO_LEVEL)后才能看到消息。
由于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