1. printk

打印级别控制:/proc/sys/kernel/printk

printk_index:/sys/kernel/debug/printk/index/

2. pr_fmt和dev_fmt

在阅读Linux内核或其他开源代码时,在文件的开始部分,通常能看到类似如下代码。但是代码中又没有看到使用的地方。

1#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2
3#include <...>
4#include <...>

其实pr_fmt是通常和大名鼎鼎的printk一起来合作实现其他宏,比较常见的就是pr_*()系列宏,另外一些模块或驱动中也会利用pr_fmt来输出日志信息。使用pr_fmt可以在要输出的信息前再添加额外的信息。 如果在#include <linux/printk.h>之前没有定义pr_fmtinclude/linux/printk.h中会将pr_fmt(fmt)定义为fmt,也就是输出信息的原始格式。如果在#include <linux/printk.h>之后定义pr_fmt,编译时就会提示重复定义警告或错误。所以通常来说,对pr_fmt的定义都是放到了文件的最前端。

 1/// include/linux/printk.h
 2/**
 3 * pr_fmt - used by the pr_*() macros to generate the printk format string
 4 * @fmt: format string passed from a pr_*() macro
 5 *
 6 * This macro can be used to generate a unified format string for pr_*()
 7 * macros. A common use is to prefix all pr_*() messages in a file with a common
 8 * string. For example, defining this at the top of a source file:
 9 *
10 *        #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11 *
12 * would prefix all pr_info, pr_emerg... messages in the file with the module
13 * name.
14 */
15#ifndef pr_fmt
16#define pr_fmt(fmt) fmt
17#endif

dev_fmtpr_fmt类似,也是用来定义打印信息的前缀。

1/// include/linux/dev_printk.h
2#ifndef dev_fmt
3#define dev_fmt(fmt) fmt
4#endif

3. 常见pr_*()接口

接口定义在include/linux/printk.h

三种类型的接口说明如下:

  • pr_xxx:按照pr_fmt(fmt)指定的格式输出日志。
  • pr_xxx_once:日志只输出一次。
  • pr_xxx_ratelimited:通常来说终端是慢速设备,pr_xxx_ratelimited可以限制日志输出速率,避免信息过多导致CPU阻塞。
pr_xxx pr_xxx_once pr_xxx_ratelimited loglevel description
pr_emerg pr_emerg_once pr_emerg_ratelimited KERN_EMERG system is unusable
pr_alert pr_alert_once pr_alert_ratelimited KERN_ALERT action must be taken immediately
pr_crit pr_crit_once pr_crit_ratelimited KERN_CRIT critical conditions
pr_err pr_err_once pr_err_ratelimited KERN_ERR error conditions
pr_warn pr_warn_once pr_warn_ratelimited KERN_WARNING warning conditions
pr_notice pr_notice_once pr_notice_ratelimited KERN_NOTICE normal but significant condition
pr_info pr_info_once pr_info_ratelimited KERN_INFO informational
pr_devel pr_devel_once pr_devel_ratelimited KERN_DEBUG debug-level message conditionally
pr_debug pr_debug_once pr_debug_ratelimited KERN_DEBUG/dynamic (dynamic) debug-level message conditionally

对于驱动代码,内核提供了dev_xxx函数来方便打印调试信息,这些函数的实现都是调用printkinclude/linux/dev_printk.h

dev_xxx dev_xxx_once dev_xxx_ratelimited loglevel description
dev_emerg dev_emerg_once dev_emerg_ratelimited KERN_EMERG system is unusable
dev_alert dev_alert_once dev_alert_ratelimited KERN_ALERT action must be taken immediately
dev_crit dev_crit_once dev_crit_ratelimited KERN_CRIT critical conditions
dev_err dev_err_once dev_err_ratelimited KERN_ERR error conditions
dev_warn dev_warn_once dev_warn_ratelimited KERN_WARNING warning conditions
dev_notice dev_notice_once dev_notice_ratelimited KERN_NOTICE normal but significant condition
dev_info dev_info_once dev_info_ratelimited KERN_INFO informational
dev_dbg dev_dbg_once dev_dbg_ratelimited KERN_DEBUG/dynamic (dynamic) debug-level message conditionally
 1/// include/linux/dev_printk.h
 2#ifdef VERBOSE_DEBUG
 3#define dev_vdbg	dev_dbg
 4#else
 5#define dev_vdbg(dev, fmt, ...)						\
 6({									\
 7	if (0)								\
 8		dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__); \
 9})
10#endif

除此之外,还有netdev_xxxnetif_xxx系列接口,另外提供了netdev_vdbgnetif_vdbgnetif_cond_dbg等宏来实现更精细化的输出。

3.1. pr_devel和pr_debugbug`

pr_*()中比较特殊的是pr_develpr_debug,它们需要在特定条件下才会有实际作用。

  1. pr_devel在没有定义DEBUG的情况下不做任何工作。
  2. pr_debugpr_devel的基础上增加了动态调试部分,如果使能了动态调试,则实际为dynamic_pr_debug,否则与pr_devel效果一致。
 1/// include/linux/printk.h
 2/**
 3 * pr_devel - Print a debug-level message conditionally
 4 * @fmt: format string
 5 * @...: arguments for the format string
 6 *
 7 * This macro expands to a printk with KERN_DEBUG loglevel if DEBUG is
 8 * defined. Otherwise it does nothing.
 9 *
10 * It uses pr_fmt() to generate the format string.
11 */
12#ifdef DEBUG
13#define pr_devel(fmt, ...) \
14	printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
15#else
16#define pr_devel(fmt, ...) \
17	no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
18#endif
 1/* If you are writing a driver, please use dev_dbg instead */
 2#if defined(CONFIG_DYNAMIC_DEBUG) || \
 3	(defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))
 4#include <linux/dynamic_debug.h>
 5
 6/**
 7 * pr_debug - Print a debug-level message conditionally
 8 * @fmt: format string
 9 * @...: arguments for the format string
10 *
11 * This macro expands to dynamic_pr_debug() if CONFIG_DYNAMIC_DEBUG is
12 * set. Otherwise, if DEBUG is defined, it's equivalent to a printk with
13 * KERN_DEBUG loglevel. If DEBUG is not defined it does nothing.
14 *
15 * It uses pr_fmt() to generate the format string (dynamic_pr_debug() uses
16 * pr_fmt() internally).
17 */
18#define pr_debug(fmt, ...)			\
19	dynamic_pr_debug(fmt, ##__VA_ARGS__)
20#elif defined(DEBUG)
21#define pr_debug(fmt, ...) \
22	printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
23#else
24#define pr_debug(fmt, ...) \
25	no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
26#endif

3.2. 关于no_printk

no_printk表示信息不会被输出,但是为了在不同的配置下,内核都可以正常工作,需要让编译器对传入pr_xxx的参数进行检查。

4. 常用pr_fmt定义

4.1. 打印模块名、函数名和行号

KBUILD_MODNAME是编译时通过编译选项指定的,无需自己定义。

1#define pr_fmt(fmt) \
2    KBUILD_MODNAME ": [%s:%d] " fmt, __func__, __LINE__

4.2. 打印CPU号

使用raw_smp_processor_id需要包含linux/smp.h

1#define pr_fmt(fmt) \
2    KBUILD_MODNAME ": <C%u> " fmt, raw_smp_processor_id()

4.3. 打印线程pid和线程名

线程的task_struct使用current宏来获取,需要包含linux/sched.h

1#define pr_fmt(fmt) \
2    "{t%d-%s} " fmt, current->pid, current->comm

4.4. 打印进程pid和进程名

进程的task_struct可以通过current->group_leader来获取。

1#define pr_fmt(fmt) \
2    "{p%d-%s} " fmt, \
3    current->group_leader->pid, current->group_leader->comm

4.5. 打印进程pid和线程pid

进程pid current->tgidcurrent->group_leader->pid相同。

1#define pr_fmt(fmt) \
2    "{p%d:t%d} " fmt, current->tgid, current->pid

4.6. 大杂烩

1#define pr_fmt(fmt) \
2    KBUILD_MODNAME ": <C%u>{p%d-%s}{t%d-%s}[%s:%d] " fmt, raw_smp_processor_id(), \
3    current->group_leader->pid, current->group_leader->comm, \
4    current->pid, current->comm, __func__, __LINE__