上篇說到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)
{