核心列印函式介紹 printk、、pr_xxx()API、dev_xxx()
核心列印函式介紹
2016年08月20日 20:01:20 那顆流星的祕密 閱讀數:7000 標籤: 驅動 更多
個人分類: 驅動開發
版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/mike8825/article/details/52262774
在驅動中,有很多列印資訊,有些是預設不能開啟的,如dev_dbg或dev_vdbg等,為了方便除錯,需要將這些列印資訊全部打印出來,可通過如下步驟來開啟開關。
我們先來看printk的列印控制
-
asmlinkage int printk(const char *fmt, ...)
-
{
-
r = vprintk_emit(0, -1, 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")
或者忽略這一限制(在cmdlinx中加入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: "的字首。(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_test.c用來測試上面的列印函式,如下
#define pr_fmt(fmt) KBUILD_MODNAME " pr_test::::::" fmt
#define DEBUG
#define VERBOSE_DEBUG
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/delay.h>
static struct miscdevice dev;
#define GTP_INFO(fmt,arg...) printk("<<-GTP-INFO->> "fmt"\n",##arg)
#define DEVICE_NAME "printk"
static int printk_test(struct device *dev)
{
printk(KERN_INFO KBUILD_MODNAME " %d\n",__LINE__);
dev_emerg(dev,"dev_emerg %d\n",__LINE__);
dev_alert(dev,"dev_alert %d\n",__LINE__);
dev_crit(dev,"dev_crit %d\n",__LINE__);
dev_err(dev,"dev_err %d\n",__LINE__);
dev_warn(dev,"dev_warn %d\n",__LINE__);
dev_notice(dev,"dev_notice %d\n",__LINE__);
dev_info(dev,"dev_info %d\n",__LINE__);
_dev_info(dev,"_dev_info %d\n",__LINE__);
pr_emerg("pr_emerg %d\n",__LINE__);
pr_alert("pr_alert %d\n",__LINE__);
pr_crit("pr_crit %d\n",__LINE__);
pr_err("pr_err %d\n",__LINE__);
pr_warning("pr_warning %d\n",__LINE__);
pr_warn("pr_warn %d\n",__LINE__);
pr_notice("pr_notice %d\n",__LINE__);
pr_info("pr_info %d\n",__LINE__);
pr_cont("pr_cont %d\n",__LINE__);
GTP_INFO("%d\n",__LINE__);
msleep(8000);
pr_debug("pr_debug\n");
dev_dbg(dev,"dev_dbg %d\n",__LINE__);
dev_vdbg(dev,"dev_vdbg %d\n",__LINE__);
return 0;
}
static struct miscdevice dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
};
static int __init printk_init(void)
{
int ret;
printk("%s\n",__func__);
ret = misc_register(&dev);
ret = printk_test(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,列印資訊為
這裡的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來開啟除錯資訊。