1.kernel中打印日志形式
(1)printk
(2)pr_xxx (pr_debug支持动态打印)
(3)dev_xxx (dev_dbg支持动态打印)
(4)module_param_named (支持动态动态打印)
目前在kernel驱动代码中,不在建议直接使用printk直接添加打印信息,而是使用pr_debug、pr_info、dev_info、dev_dbg之类的函数替代,这些函数的本质还是printk打印,但相比具有以下优点:
(1)支持打印模块信息、dev信息(指的是dev_xxx)
(2)支持动态调试(dynamic debug)方式
2.日志打印分析
//pr_debug()函数定义,定义在include/linux/printk.h
/* If you are writing a driver, please use dev_dbg instead */
#if defined(CONFIG_DYNAMIC_DEBUG)
#include <linux/dynamic_debug.h>
/* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */
#define pr_debug(fmt, ...) \
dynamic_pr_debug(fmt, ##__VA_ARGS__)
#elif defined(DEBUG)
#define pr_debug(fmt, ...) \
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_debug(fmt, ...) \
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif
//dev_dbg()函数定义的定义,定义在include/linux/device.h
#if defined(CONFIG_DYNAMIC_DEBUG)
#define dev_dbg(dev, fmt, ...) \
dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
#elif defined(DEBUG)
#define dev_dbg(dev, fmt, ...) \
dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__)
#else
#define dev_dbg(dev, fmt, ...) \
({ \
if (0) \
dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__); \
})
#endif
3个判断条件决定用法:
(1)如果定义了CONFIG_DYNAMIC_DEBUG ,就使用debug机制dynamic_dev_dbg
(2)如果定义了DEBUG,就使用printk(KERN_DEBUG…)
(3)默认情况下,不打印
> 备注:
以上只定义了pr_debug 和 dev_dbg动态打印,其他函数并没有进行定义
3. CONFIG_DYNAMIC_DEBUG 方式使用
(1)内核配置: CONFIG_DEBUG_FS CONFIG_DYNAMIC_DEBUG
Symbol: DEBUG_FS [=y] │
│ Type : bool │
│ Prompt: Debug Filesystem │
│ Location: │
│ -> Kernel hacking │
│ (1) -> Compile-time checks and compiler options
Symbol: DYNAMIC_DEBUG [=y] │
│ Type : bool │
│ Prompt: Enable dynamic printk() support │
│ Location: │
│ -> Kernel hacking │
│ (5) -> printk and dmesg options │
│ Defined at lib/Kconfig.debug:79 │
│ Depends on: PRINTK [=y] && DEBUG_FS [=y]
(2)查看CONFIG_DEBUG_FS是否启用成功:
//手动挂载debugfs,如果不进行挂载或者不启用,/sys/kernel/debug是没有的
mount -t debugfs none /sys/kernel/debug
//在fstab中配置,进行自动挂载
# <file system> <mount pt> <type> <options> <dump> <pass>
debug /sys/kernel/debug debugfs defaults 0 0
(3)查看CONFIG_DYNAMIC_DEBUG是否启用成功:
存在/sys/kernel/debug/dynamic_debug/control
(4)操作
查看动态日志的状态,可以看到前面为_,则表明动态日志没有打开
/sys/kernel/debug/dynamic_debug # cat control | grep "drivers/watchdog/log.c"
drivers/watchdog/log.c:39 [log]log_probe =_ "pr_debug\012"
drivers/watchdog/log.c:50 [log]log_probe =_ "dev_dbg\012"
drivers/watchdog/log.c:99 [log]log_drv_exit =_ "log_remove pr_debug\012"
//动态打开日志
echo -n 'file log.c +p' > /sys/kernel/debug/dynamic_debug/control
//验证方式,可以看到前面为p
/sys/kernel/debug/dynamic_debug # cat control | grep "drivers/watchdog/log.c"
drivers/watchdog/log.c:39 [log]log_probe =p "pr_debug\012"
drivers/watchdog/log.c:50 [log]log_probe =p "dev_dbg\012"
drivers/watchdog/log.c:99 [log]log_drv_exit =p "log_remove pr_debug\012"
打开日志后就可以通过dmesg查看日志打印情况
//动态关闭日志
echo -n 'file log.c -p' > /sys/kernel/debug/dynamic_debug/control
/sys/kernel/debug/dynamic_debug # cat control | grep "drivers/watchdog/log.c"
drivers/watchdog/log.c:39 [log]log_probe =_ "pr_debug\012"
drivers/watchdog/log.c:50 [log]log_probe =_ "dev_dbg\012"
drivers/watchdog/log.c:99 [log]log_drv_exit =_ "log_remove pr_debug\012"
4. DEBUG 方式使用
(1)取消内核的配置:CONFIG_DYNAMIC_DEBUG,在makefile中定义DEBUG宏定义
obj-m += log.o
//在这个makefile都进行宏定义
ccflags-y := -DDEBUG
//在指定的.c文件中进行宏定义,驱动文件名称为log.c
CFLAGS_log.o := -DDEBUG
//给底下的所有文件定义宏,包括子文件夹
subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
备注:
(1)在源码中直接定义DEBUG,没有起作用,原因暂时不清楚
(2)如果内核中定义了CONFIG_DYNAMIC_DEBUG,并且在makefile中定义了DEBUG宏定义,则直接可以打印信息,原因暂时不清楚
5. module_param_named 方式使用
static int dbg_enable;
//dbg_level:对外呈现的数值,dbg_enable内在使用的数值,在外部通过修改dbg_level的值从而改变dbg_enable的值,会生成在/sys/module/模块名称/parameters/dbg_level文件
module_param_named(dbg_level, dbg_enable, int, 0644);
#define DBG(args...) \
do { \
if (dbg_enable) { \
pr_info(args); \
} \
} while (0)
6. console日志打印情况
//日志是否在串口输出
//查看当前串口输出的级别,可以看到只有<4的级别才能输出
cat /proc/sys/kernel/printk
4 4 1 7
//修改串口输出级别
echo 8 > /proc/sys/kernel/printk
cat /proc/sys/kernel/printk
8 4 1 7
//下面是各个字符串对用的日志级别
#define KERN_EMERG KERN_SOH "0" /* system is unusable */
#define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
#define KERN_CRIT KERN_SOH "2" /* critical conditions */
#define KERN_ERR KERN_SOH "3" /* error conditions */
#define KERN_WARNING KERN_SOH "4" /* warning conditions */
#define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */
#define KERN_INFO KERN_SOH "6" /* informational */
#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */
7. platform下的devices和driver
platform下的driver
/sys/bus/platform/drivers/log_module
log_module.0 //匹配成功后,里面会包含对应的设备
platform下的devices
/sys/bus/platform/devices/log_module.0 -> ../../../devices/platform/log_module.0 //是一个软连接
/sys/devices/platform/log_module.0 //为真正的实体
driver //匹配成功后,里面会包含对应的设备
8. code
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
//暂时不清楚在该处直接进行宏定义DEBUG不起作用,原因暂时不知
//#define DEBUG
static int dbg_enable;
module_param_named(dbg_level, dbg_enable, int, 0644);
#define DBG(args...) \
do { \
if (dbg_enable) { \
pr_info(args); \
} \
} while (0)
static int log_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
printk("test printk start..................................\n");
printk(KERN_EMERG ":KERN_EMERG\n");
printk(KERN_ALERT "KERN_ALERT\n");
printk(KERN_CRIT "KERN_CRIT\n");
printk(KERN_ERR "KERN_ERR\n");
printk(KERN_WARNING "KERN_WARNING\n");
printk(KERN_NOTICE "KERN_NOTICE\n");
printk(KERN_INFO "KERN_INFO\n");
printk(KERN_DEBUG "KERN_DEBUG\n");
printk("test printk end..................................\n");
printk("test pr log start..................................\n");
pr_emerg("pr_emerg\n");
pr_alert("pr_alert\n");
pr_crit("pr_crit\n");
pr_err("pr_err\n");
pr_warning("pr_warning\n");
pr_notice("pr_notice\n");
pr_info("pr_info\n");
pr_debug("pr_debug\n");
printk("test pr log end..................................\n");
printk("test dev log start..................................\n");
dev_emerg(dev, "dev_emerg\n");
dev_crit(dev, "dev_crit\n");
dev_alert(dev, "dev_alert\n");
dev_err(dev, "dev_err\n");
dev_warn(dev, "dev_warn\n");
dev_notice(dev, "dev_notice\n");
dev_info(dev, "dev_info\n");
dev_dbg(dev, "dev_dbg\n");
printk("test dev log start..................................\n");
#if defined(DEBUG)
printk("define DEBUG .\n");
#endif
return 0;
}
static void log_release(struct device *dev)
{
}
static int log_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver log_driver = {
.probe = log_probe,
.remove = log_remove,
.driver = {
.name = "log_module",
.owner = THIS_MODULE,
},
};
static struct platform_device log_device = {
.name = "log_module",
.dev = {
.release = log_release,
}
};
static int log_drv_init(void){
platform_device_register(&log_device);
platform_driver_register(&log_driver);
return 0;
}
static void log_drv_exit(void){
pr_info("log_remove pr_info\n");
pr_debug("log_remove pr_debug\n");
DBG("test DBG\n");
platform_device_unregister(&log_device);
platform_driver_unregister(&log_driver);
}
module_init(log_drv_init);
module_exit(log_drv_exit);
MODULE_DESCRIPTION("log Driver");
MODULE_LICENSE("GPL");