1。debug log信息
这个重要性不用说了 虽然是最低级的调试手段 但是历史告诉我们 往往简单才是最有效的。
debug最基本的初衷是为了让程序除了正常运行模式外,还能运行于debug模式,以方便跟踪程序的一举一动。
见过几种debug的方式
1)分等级
最典型常见的就是printk 分等级的目的是为了将不同等级区分对待。然而printk存在并不是为了作为debug信息,当然可以用来输出debug信息,printk是了规划和整理整个大系统各个模块各个部门报告的信息。因此debug log信息不益完全参考printk做法。
#define xdebug(n, f, a...) /
do { /
if ((n) <= debug_level) { /
printk ("<"#n">" "(%s, %d): %s: ", /
__FILE__, __LINE__, __FUNCTION__); /
printk (f, ## a); /
} /
} while (0)
上面的做法是通过控制debug_level的值控制符合等级要求的debug信息的输出。当然debug_level可以是常数,也可以是变量(动态调整)。
分等级的方法最大的缺点是使用时比较难于确定具体log信息的等级,等到最后信息都出来的时候,你会发现有的信息你本来希望是等级4,而过了几个你却希望它是等级5!
2)分模块log信息
分模块方法对于调试而言要好用的多,调试某个模块只用打开对应模块的信息即可。
通常用一个bit表示一个模块进行编码,这样可以同时标记多个模块。
下面是linux rpc的debug方法
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define dprintk(level,args...) /
do { if ((b2c2_flexcop_debug & level)) printk(args); } while (0)
#else
#define dprintk(level,args...)
#endif
#define deb_info(args...) dprintk(0x01,args)
#define deb_tuner(args...) dprintk(0x02,args)
#define deb_i2c(args...) dprintk(0x04,args)
#define deb_ts(args...) dprintk(0x08,args)
#define deb_sram(args...) dprintk(0x10,args)
#define deb_rdump(args...) dprintk(0x20,args)
3)(1)和(2)的结合
每个模块使用一段位图,多个模块需要位图数组,各个模块能按照自己等级打印。。。。。
2。assert
assert主要用于程序处于debug模式的测试,正常模式assert不应该参加编译。
assert可用范围相当广,应该养成对自己坚信不移的而缺乏验证的条件都加以assert,当然不相信的条件则更应该如此。有效的使用assert你会发现调试初期bug非常容易。
3。BUG()
对于某些无法处理的情况,又或者认定不会发生的情况,一旦发生了,BUG()是唯一选择。
应用程序中,进行简单的报错,或者加上core down之类,最后的操作一般是exit。kernel中当然也能显式的调用exit,但更正规的做法是主动造成一个硬件异常,使得程序被迫退出。x86 linux kernel利用cpu的保护模式,通过越权访问内存主动引发异常。不同处理器可以参考处理器编程说明。
这个重要性不用说了 虽然是最低级的调试手段 但是历史告诉我们 往往简单才是最有效的。
debug最基本的初衷是为了让程序除了正常运行模式外,还能运行于debug模式,以方便跟踪程序的一举一动。
见过几种debug的方式
1)分等级
最典型常见的就是printk 分等级的目的是为了将不同等级区分对待。然而printk存在并不是为了作为debug信息,当然可以用来输出debug信息,printk是了规划和整理整个大系统各个模块各个部门报告的信息。因此debug log信息不益完全参考printk做法。
#define xdebug(n, f, a...) /
do { /
if ((n) <= debug_level) { /
printk ("<"#n">" "(%s, %d): %s: ", /
__FILE__, __LINE__, __FUNCTION__); /
printk (f, ## a); /
} /
} while (0)
上面的做法是通过控制debug_level的值控制符合等级要求的debug信息的输出。当然debug_level可以是常数,也可以是变量(动态调整)。
分等级的方法最大的缺点是使用时比较难于确定具体log信息的等级,等到最后信息都出来的时候,你会发现有的信息你本来希望是等级4,而过了几个你却希望它是等级5!
2)分模块log信息
分模块方法对于调试而言要好用的多,调试某个模块只用打开对应模块的信息即可。
通常用一个bit表示一个模块进行编码,这样可以同时标记多个模块。
下面是linux rpc的debug方法
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define dprintk(level,args...) /
do { if ((b2c2_flexcop_debug & level)) printk(args); } while (0)
#else
#define dprintk(level,args...)
#endif
#define deb_info(args...) dprintk(0x01,args)
#define deb_tuner(args...) dprintk(0x02,args)
#define deb_i2c(args...) dprintk(0x04,args)
#define deb_ts(args...) dprintk(0x08,args)
#define deb_sram(args...) dprintk(0x10,args)
#define deb_rdump(args...) dprintk(0x20,args)
3)(1)和(2)的结合
每个模块使用一段位图,多个模块需要位图数组,各个模块能按照自己等级打印。。。。。
2。assert
assert主要用于程序处于debug模式的测试,正常模式assert不应该参加编译。
assert可用范围相当广,应该养成对自己坚信不移的而缺乏验证的条件都加以assert,当然不相信的条件则更应该如此。有效的使用assert你会发现调试初期bug非常容易。
3。BUG()
对于某些无法处理的情况,又或者认定不会发生的情况,一旦发生了,BUG()是唯一选择。
应用程序中,进行简单的报错,或者加上core down之类,最后的操作一般是exit。kernel中当然也能显式的调用exit,但更正规的做法是主动造成一个硬件异常,使得程序被迫退出。x86 linux kernel利用cpu的保护模式,通过越权访问内存主动引发异常。不同处理器可以参考处理器编程说明。