kernel相较于user,debug起来更加困难,原因有很多,比如kernel中代码的执行是并发的,靠event来驱动;而且kernel一旦出现了bug,很容易把整个系统搞挂,导致后面的现场无法trace,本章就介绍了一些kernel中的debug技巧。
Debugging Support in the Kernel
kernel的发行版都是production code,也就是说里面包含的debug用的信息很少。作为device driver的开发人员,自然得要自己build debug版的kernel才方便debug。
kernel的config包含了很多debug相关的配置,这些配置在“kernel hacking”中都可以找到:
CONFIG_DEBUG_KERNEL
在其他的debug选项打开之前,先要打开这个,虽然它并不能enable什么debug feature。
CONFIG_DEBUG_SLAB
主要针对kernel memory的检查,比如访问未初始化的内存,或者访问已经被释放的内存,以及写越界等。debug选项打开的时候,kernel会在被分配的memory前后加一些guard,以检查对memory的不合理的访问。
CONFIG_DEBUG_PAGEALLOC
减慢page free的速度,以检查可能存在的memory corrupt问题。(?)
CONFIG_DEBUG_SPINLOCK
选项打开的时候,可以检查使用没有初始化 的spinlock,或者lock了两次这种问题。
CONFIG_DEBUG_SPINLOCK_SLEEP
用于检查在持有spinlock的情况下sleep的发生。
CONFIG_INIT_DEBUG
在module init的时候,标记了__init,或者__initdata的函数或者数据,会在init做完以后被释放。打开了这个选项,可以检查在init做完以后仍然对初始化函数或者初始化数据的访问。
CONFIG_DEBUG_INFO
如果打开了这个选项,生成的kernel image会包含各种debug的信息,这样就可以使用gdb来debug kernel。当然,如果要使用gdb,还需要打开CONFIG_FRAME_POINTER。
CONFIG_MAGIC_SYSRQ
打开“magic SysRq” 按键,后面会讲。
CONFIG_DEBUG_STACKOVERFLOW
CONFIG_DEBUG_STACK_USAGE
打开这两个选项,可以trace stack overflow的问题。一般情况下,如果发生了stack overflow,往往stack是被破坏的。如果打开了DEBUG_STACKOVERFLOW,kernel会在stack上增加check;如果打开了STACK_USAGE,kernel会统计stack的使用信息。
CONFIG_KALLSYMS
打开这个选项,build出来的kernel image包含kernel的symbol信息。默认是打开的,否则stack打印出来全是十六进制,没有函数名等。这个选项在“General setup/Standard features”下面。
CONFIG_IKCONFIG
CONFIG_IKCONFIG_PROC
这两个选项在“General setup”下面。会把kernel所有的config都打开,并且在/proc下创建节点?
CONFIG_ACPI_DEBUG
在“Power management/ACPI.”下面,如果在检查power相关的bug,可以打开这个选项。所谓的ACPI,意思是Advanced Configuration and Power Interface。
CONFIG_DEBUG_DRIVER
在“Device Driver”下面,如果在driver core中打开这个选项,可以trace low-level里的code。
CONFIG_SCSI_CONSTANTS
在“Device drivers/SCSI device support,”选项下面,打开SCSI的信息打印。
CONFIG_INPUT_EVBUG
在“Device drivers/Input device support”下面,打开之后会打印很多和input device相关的信息。
CONFIG_PROFILING
在“Profiling support.”下面,主要用于分析kernel的performance,也能用于trace system hang或别的error。
下面是介绍debug kernel的一些手段。
Debugging by Printing
最常用的debug手段仍然是print。在user mode,可以使用printf来打印信息,在kernel中,需要使用printk。
printk
printk和user mode printf有些不同,这些不同体现在很多方面。比如printk可以指定loglevel,或者优先级,在打印时通过一些宏就可以指定loglevel,比如:
KERN_EMERG
用在紧急情况下,通常是发生了crash。
KERN_ALERT
提醒,表示需要立即操作。
KERN_CRIT
critical情况的发生,通常是硬件或者软件操作失败。
KERN_ERR
有错误发生,通常用来指硬件设备产生了错误。
KERN_WARNING
产生警告
KERN_NOTICE
情况一般,但是需要提醒,很多安全相关的log是这个等级。
KERN_INFO
一般的信息打印,很多driver启动或者运行时会通过这个loglevel打印信息。
KERN_DEBUG
用于打印debug信息
上面的8个loglevel,对应了0-7这些整形数,越小的数字log level越高。如果打印时没有指定,就使用DEFAULT_MESSAGE_LOGLEVEL,这个值具体是哪个,需要看当前kernel的配置。
根据当前的loglevel,kernel可以控制打印到什么地方,比如console,terminal,serial port,或者打印机等。如果log等级比console_loglevel低,那么就一行行的打印到console里去。如果klogd和syslogd都在运行,kernel的log会被打印到/var/log/messages里面去。如果klogd没有运行,那么kernel的log不会主动送到user space,除非自己去读/proc/kmsg(可以使用dmesg命令)。kernel的loglevel可以自己修改:
# echo 8 > /proc/sys/kernel/printk
通过向printk写值,可以控制信息打印的等级。
Redirecting Console Messages
这里似乎用不到,不看了。
How Messages Get Logged
To be continued