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");