Linux内核学习笔记:动态输出调试

上篇说到printk调试,但printk是 全局的 ,只能设置输出等级。而动态输出可以动态选择打开某个内核子系统的输出,可以有选择性地打开某些模块的输出。

配置内核编译选项

要使用动态输出,必须在配置内核时打开CONFIG_DYNAMIC_DEBUG宏。内核代码里使用大量pr_debug()/dev_dbg()函数来输出信息,这些就使用了动态输出。

需要打开的内核配置选项:

CONFIG_DEBUG_FS=y

CONFIG_DYNAMIC_DEBUG=y

CONFIG_DYNAMIC_DEBUG是配置动态输出,它依赖于CONFIG_DEBUG_FS,而CONFIG_DEBUG_FS是debugfs 文件系统 。

打开内核配置后,我们还需要挂载debugfs 文件系统 。

debugfs文件系统挂载

动态输出在debugfs文件系统中有一个control文件节点,这个文件节点记录了系统中所有使用动态输出技术的 文件名路径、输出所在的行号、模块名字和要输出的语句 。

debugfs默认会挂载到/sys/kernel/debug,如果没有挂载,可以执行以下命令挂载:

# mount -t debugfs none /sys/kernel/debug/

挂载debugfs文件系统后,可以查看control节点内容:

# cat /sys/kernel/debug/dynamic_debug/control

动态输出使用

打开svcsock.c文件中所有的动态输出语句

# echo ‘file svcsock.c +p’ > /sys/kernel/debug/dynamic_debug/control

打开usbcore模块中所有的动态输出语句

# echo ‘module usbcore +p’ > /sys/kernel/debug/dynamic_debug/control

打开svc_process()函数中所有的动态输出语句

# echo ‘func svc_process() +p’ > /sys/kernel/debug/dynamic_debug/control

打开文件路径包含usb的文件里所有的动态输出语句

# echo -n ‘*usb* +p’ > /sys/kernel/debug/dynamic_debug/control

打开系统所有的动态输出语句

# echo -n ‘+p’ > /sys/kernel/debug/dynamic_debug/control

上面是打开动态输出语句的例子,除了能输出pr_debug()/dev_dbg()函数中定义的输出信息外,还能输出一些额外信息,如函数名、行号、模块名字以及线程ID等

p:打开动态输出语句

f:输出函数名

l:输出行号

m:输出模块名字

t:输出线程ID

另外,还可以在各个子系统的Makefile中添加ccflags来打开动态输出语句

< ../Makefile >

ccflags-y += -DDEBUG

ccflags-y += -DVERBOSE_DEBUG

实际案例

例如在一个led驱动中的open()、write()等函数开头添加一句pr_debug(“%s enter\n”, **func **** ** );

#include < linux/module.h >

#include < linux/fs.h >

#include < linux/errno.h >

#include < linux/miscdevice.h >

#include < linux/kernel.h >

#include < linux/major.h >

#include < linux/mutex.h >

#include < linux/proc_fs.h >

#include < linux/seq_file.h >

#include < linux/stat.h >

#include < linux/init.h >

#include < linux/device.h >

#include < linux/tty.h >

#include < linux/kmod.h >

#include < linux/gfp.h >

static int major = 0;

static char kernel_buf[1024];

static struct class *hello_class;

#define MIN(a, b) (a < b ? a : b)

static ssize_t hello_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)

{

int err;

pr_debug(“%s enter\n”, __func__);

err = copy_to_user(buf, kernel_buf, MIN(1024, size));

return MIN(1024, size);

}

static ssize_t hello_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)

{

int err;

pr_debug(“%s enter\n”, __func__);

err = copy_from_user(kernel_buf, buf, MIN(1024, size));

return MIN(1024, size);

}

static int hello_drv_open (struct inode *node, struct file *file)

{

pr_debug(“%s enter\n”, __func__);

return 0;

}

static int hello_drv_close (struct inode *node, struct file *file)

{

pr_debug(“%s enter\n”, __func__);

return 0;

}

/* 2. 定义自己的file_operations结构体 */

static struct file_operations hello_drv = {

.owner = THIS_MODULE,

.open = hello_drv_open,

.read = hello_drv_read,

.write = hello_drv_write,

.release = hello_drv_close,

};

static int __init hello_init(void)

{

发表评论