1、起因
一般在驱动中输出日志使用宏KdPrint,格式如下:
KdPrint(("******", ...));
这个只是在check版本下才输出日志,另外,这里不是手误,确实是需要两层括号。因为该宏如下:
#if DBG
#define KdPrint(_x_) DbgPrint _x_
#else
#define KdPrint(_x_)
#endif
大多数时候,驱动开发时,在开发人员本地虚拟机或者实体机上才会连接内核调试。测试人员那边有问题,比如蓝屏了,拿回dump文件分析,或者编译一个check版本的驱动,使用DebugView查看输出日志(经常也需要查看ring3层程序日志定位流程问题)。这个时候经常会有一个头疼的问题,那就是日志太多了。
通常使用过滤条件设置,有时候效果也不够好。比较方便的做法,是自己的日志,按照一定规则显示,比如日志内加上产品,模块信息,如下格式:
产品:模块:详细日志信息
这样,我们上层使用OutPutDebugString时(A或者W都行),可以格式化字符串如下格式:
OutPutDebugStringA("app:module:Create Socket Failed! error code = xxx\n");
通过设置产品过滤条件,能查看整个产品的输出日志,设置产品+模块过滤条件,能查看具体某个模块的日志。
驱动中也可以使用如下格式:
KdPrint(("drivername:some function ExAllocatePoolWithTag failed! error code = %08lX\n", errorcode));
这样后续解决问题方便了,但是无形中开发的工作加重了,特别是维护代码的开发者,在茫茫的代码中修改日志输出,也是已经很无聊的事情!于是写了宏,包括ring0和ring3,使用特别方便,甚至比以前还要便利些,至于以前代码转换,可以使用python之类的脚本语言工具,批量一键转换(略脚本代码)。
ring3层宏定义如下:
#define PRO_MODULE_NANE "xxx:xxx:" // 这里根据实际定义
#define ODS_BUFFER_SIZE 1024 // 缓冲大小
#define OutPutModuleNameString( _x_ ) PRO_MODULE_NANE ## _x_
#define xxOutPutDebugString( _x_ , ...) xxODS(OutPutModuleNameString( _x_ ), ##__VA_ARGS__)
// 这是一个函数
void xxODS(const char *szMsg, ... );
void xxODS(const char *szMsg, ... )
{
va_list marker;
// 这个缓冲区,也可以使用全局(或static),但是后者非多线程安全
char szTmp[ODS_BUFFER_SIZE] = {0};
va_start( marker ,szMsg );
vsprintf(szTmp,szMsg, marker);
va_end( marker );
OutputDebugStringA(szTmp);
}
// 比如如下调用:
WCHAR wszFileName[] = {L"wtest.dat"};
CHAR szFileName[] = {"test.dat"};
int icount = 100;
xxOutPutDebugString("wszFileName=%ws,szFileName=%s,count=%d\n", wszFileName, szFileName, icount);
#if DBG
#define xxModuleString(_x_) "xxx:" ## _x_
#define xxDbgPrint(_x_, ...) DbgPrint(xxModuleString(_x_), ##__VA_ARGS__)
#else
#define xxDbgPrint(_x_)
#endif
// 比如如下调用
xxDbgPrint("DriverEntry->IoCreateDevice failed! error code = %08lX\n", status);