内核打印函数介绍

       在驱动中,有很多打印信息,有些默认不能打开的,如dev_dbg或dev_vdbg等,为了方便调试,需要将这些打印信息全部打印出来,可通过如下步骤来打开开关。

我们先来看printk的打印控制

kernel/printk/xxx

asmlinkage __visible int printk(const char *fmt, ...)
{
	r = vprintk_func(fmt, args);
}

__printf(1, 0) int vprintk_func(const char *fmt, va_list args)
{
	return vprintk_default(fmt, args);
}

int vprintk_default(const char *fmt, va_list args)
{
	r = vprintk_emit(0, LOGLEVEL_DEFAULT, NULL, 0, fmt, args);
}

asmlinkage int vprintk_emit(int facility, int level,const char *dict, size_t dictlen,const char *fmt, va_list args)
{

	console_unlock();
}

void console_unlock(void)
{

	call_console_drivers(level, text, len);
}

static void call_console_drivers(int level, const char *text, size_t len)
{
	struct console *con;

	trace_console(text, len);

	if (level >= console_loglevel && !ignore_loglevel)
		return;
	if (!console_drivers)
		return;

	for_each_console(con) {
		if (exclusive_console && con != exclusive_console)
			continue;
		if (!(con->flags & CON_ENABLED))
			continue;
		if (!con->write)
			continue;
		if (!cpu_online(smp_processor_id()) &&
		    !(con->flags & CON_ANYTIME))
			continue;
		con->write(con, text, len);
	}
}

只有高于控制台的的信息才能打印出来,可以这样修改控制台的打印级别

echo 8 > /proc/sys/kernel/printk或者在rc脚本中write n1 n2 n3 n4到printk节点,如

device/qcom/common/rootdir/etc/init.qcom.rc   write /proc/sys/kernel/printk "6 6 1 7")

或者忽略这一限制(在cmdline中加入ignore_loglevel)

kernel/kernel/sysctl.c
static struct ctl_table kern_table[] = {
#if defined CONFIG_PRINTK
	{
		.procname	= "printk",
		.data		= &console_loglevel,
		.maxlen		= 4*sizeof(int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec,
	},
}
#define console_loglevel (console_printk[0])
int console_printk[4] = {
	DEFAULT_CONSOLE_LOGLEVEL,	/* console_loglevel */
	DEFAULT_MESSAGE_LOGLEVEL,	/* default_message_loglevel */
	MINIMUM_CONSOLE_LOGLEVEL,	/* minimum_console_loglevel */
	DEFAULT_CONSOLE_LOGLEVEL,	/* default_console_loglevel */
};

 

static int __init ignore_loglevel_setup(char *str)
{
	ignore_loglevel = 1;
	printk(KERN_INFO "debug: ignoring loglevel setting.\n");

	return 0;
}

early_param("ignore_loglevel", ignore_loglevel_setup);

printk的参数控制

#define    KERN_EMERG      "<0>"    
#define    KERN_ALERT      "<1>"    
#define    KERN_CRIT       "<2>"    
#define    KERN_ERR        "<3>"    
#define    KERN_WARNING    "<4>"    
#define    KERN_NOTICE     "<5>"    
#define    KERN_INFO       "<6>"    
#define    KERN_DEBUG      "<7>"

如printk (KERN_NOTICE "floppy disk write protected\n");
此外还有很多printk函数的变形,如pr_xxx,dev_xxx函数,
 

对应pr_xxx()API的好处是,可以在文件最开头通过pr_fmt定义一个打印格式,比如在kernel/watchdog.c的最开头通过如下定义保证以后watchdog.c调用的

所以pr_xxx()打印的信息都自动带有" NMI watchdog: "的前缀(除了pr_cont)。

linux/include/printk.h

#define pr_fmt(fmt)  " NMI watchdog: " fmt

#ifndef pr_fmt
#define pr_fmt(fmt) fmt
#endif

#define pr_emerg(fmt, ...) \
    printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
#define pr_alert(fmt, ...) \
    printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_crit(fmt, ...) \
    printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_err(fmt, ...) \
    printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warning(fmt, ...) \
    printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn pr_warning
#define pr_notice(fmt, ...) \
    printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info(fmt, ...) \
    printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
#define pr_cont(fmt, ...) \
    printk(KERN_CONT fmt, ##__VA_ARGS__)

/* pr_devel() should produce zero code unless DEBUG is defined */
#ifdef DEBUG
#define pr_devel(fmt, ...) \
    printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_devel(fmt, ...) \
    no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif

#include <linux/dynamic_debug.h>

/* If you are writing a driver, please use dev_dbg instead */
#if defined(CONFIG_DYNAMIC_DEBUG)
/* 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_xxx()族API打印的时候,设备名称对自动加到打印信息的前头。 (drivers/base/core.c)

static int __dev_printk(const char *level, const struct device *dev,
			struct va_format *vaf)
{
	if (!dev)
		return printk("%s(NULL device *): %pV", level, vaf);

	return dev_printk_emit(level[1] - '0', dev,
			       "%s %s: %pV",
			       dev_driver_string(dev), dev_name(dev), vaf);
}

int dev_printk(const char *level, const struct device *dev,
	       const char *fmt, ...)
{
	struct va_format vaf;
	va_list args;
	int r;

	va_start(args, fmt);

	vaf.fmt = fmt;
	vaf.va = &args;

	r = __dev_printk(level, dev, &vaf);

	va_end(args);

	return r;
}
EXPORT_SYMBOL(dev_printk);

#define define_dev_printk_level(func, kern_level)		\
int func(const struct device *dev, const char *fmt, ...)	\
{								\
	struct va_format vaf;					\
	va_list args;						\
	int r;							\
								\
	va_start(args, fmt);					\
								\
	vaf.fmt = fmt;						\
	vaf.va = &args;						\
								\
	r = __dev_printk(kern_level, dev, &vaf);		\
								\
	va_end(args);						\
								\
	return r;						\
}								\
EXPORT_SYMBOL(func);

define_dev_printk_level(dev_emerg, KERN_EMERG);
define_dev_printk_level(dev_alert, KERN_ALERT);
define_dev_printk_level(dev_crit, KERN_CRIT);
define_dev_printk_level(dev_err, KERN_ERR);
define_dev_printk_level(dev_warn, KERN_WARNING);
define_dev_printk_level(dev_notice, KERN_NOTICE);
define_dev_printk_level(_dev_info, KERN_INFO);

此外,打印中还经常看到pr_debug函数,可以动态的打印调试信息(http://blog.csdn.net/weiqifa0/article/details/44038907

如要打开该函数,只需在内核中配置CONFIG_DYNAMIC_DEBUG,并且将debugfs挂载到某个文件夹mount -t debugfs none /sys/kernel/debug/,

就可以通过echo -n "file xxxxxx.c +p" > /sys/kernel/debug/dynamic_debug/control来打开调试信息啦。

#if defined(CONFIG_DYNAMIC_DEBUG)
/* 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_bdb一般是没打印出来的。

在linux/device.h文件中:

#ifdef DEBUG
#define dev_dbg(dev, format, arg...)        \
    dev_printk(KERN_DEBUG , dev , format , ## arg)
#else
static inline int __attribute__ ((format (printf, 2, 3)))
dev_dbg(struct device * dev, const char * fmt, ...)
{
    return 0;
}
#endif

那我们在包含该头文件之前,需要声明一下DEBUG,并修改控制台的打印级别,就可以打印出来了。

#define DEBUG
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/platform_device.h>

下面的程序printk.c用来测试上面的打印函数,如下

#define pr_fmt(fmt)  "pr family" fmt
#define DEBUG
#define VERBOSE_DEBUG
#include <linux/module.h>
#include <linux/miscdevice.h>

static struct miscdevice dev;

#define GTP_INFO(fmt,arg...)   printk("<<-GTP-INFO->> "fmt"\n",##arg)
#define DEVICE_NAME "printk"
static int printk_demo(struct device *dev)
{
	printk(KERN_INFO "%s\n",__FUNCTION__);

	dev_emerg(dev,"dev_emerg\n");
	dev_alert(dev,"dev_alert\n");
	dev_crit(dev,"dev_crit\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_info(dev,"_dev_info\n");
	dev_dbg(dev,"dev_dbg\n");
	dev_vdbg(dev,"dev_vdbg\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_warn("pr_warn\n");
	pr_notice("pr_noticen\n");
	pr_info("pr_info\n");
	pr_debug("pr_debug\n");
	pr_cont("pr_cont\n");
	
	GTP_INFO("%d\n",__LINE__);

	return 0;
}


static struct miscdevice dev = {
	.minor	= MISC_DYNAMIC_MINOR,
	.name	= DEVICE_NAME,
};

static int __init printk_init(void)
{
	int ret;
	ret = misc_register(&dev);
	ret = printk_demo(dev.this_device);
	return ret;
}

static void __exit printk_exit(void)
{
	misc_deregister(&dev);
	printk("%s\n",__func__);
}

module_init(printk_init);
module_exit(printk_exit);
MODULE_LICENSE("GPL");

编译并安装驱动,修改打印级别echo 8 >/proc/sys/kernel/printk,打印信息为

[12814.860489] printk_demo
[12814.860493] misc printk: dev_emerg
[12814.860499] misc printk: dev_alert
[12814.860502] misc printk: dev_crit
[12814.860514] misc printk: dev_err
[12814.860516] misc printk: dev_warn
[12814.860518] misc printk: dev_notice
[12814.860520] misc printk: dev_info
[12814.860522] misc printk: _dev_info
[12814.860524] misc printk: dev_dbg
[12814.860526] misc printk: dev_vdbg
[12814.860527] PR pr_emerg
[12814.860529] PR pr_alert
[12814.860530] PR pr_crit
[12814.860532] PR pr_err
[12814.860533] PR pr_warning
[12814.860534] PR pr_warn
[12814.860534] PR pr_noticen
[12814.860535] PR pr_info
[12814.860536] PR pr_debug
[12814.860537] pr_cont
[12814.860538] <<-GTP-INFO->> 37


这里的pr_debug函数也打印出来了,因为把DEBUG的声明放在CONFIG_DYNAMIC_DEBUG声明之前,pr_debug走的是printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)

如果没有声明DEBUG,可通过echo -n "file printk_test.c +p" > /sys/kernel/debug/dynamic_debug/control来打开调试信息。

 

以上的打印级别对于真实的串口才有效,对于环形缓冲区(dmesg)无效。

修改dmesg缓冲区的容量CONFIG_LOG_BUF_SHIFT=17,由于这个会占用内存,也不可设置太大。

init/Kconfig

config LOG_BUF_SHIFT
	int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"
	range 12 25
	default 17
	depends on PRINTK
	help
	  Select the minimal kernel log buffer size as a power of 2.
	  The final size is affected by LOG_CPU_MAX_BUF_SHIFT config
	  parameter, see below. Any higher size also might be forced
	  by "log_buf_len" boot parameter.

	  Examples:
        25 => 32768 KB
		24 => 16384 KB
		23 => 8192 KB
		22 => 4096 KB 
		21 => 2048 KB 
		20 => 1024 KB 
		19 => 512 KB 
		18 => 256 KB 
		17 => 128 KB
		16 => 64 KB
		15 => 32 KB
		14 => 16 KB
		13 =>  8 KB
		12 =>  4 KB

 

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值