在進行Linux驅動開發時經常見到使用pr_debug和dev_dbg打印驅動的log,如果在內核配置時選擇了CONFIG_DYNAMIC_DEBUG宏,那麼就可以利用類似下面的命令打開對應文件的log:
echo -n "file xxx.c +p" > /sys/kernel/debug/dynamic_debug/control
但是有時候我們需要看到這個文件在內核啓動階段的log,那麼改怎麼辦呢?
這裏有兩種方法:
方法一 修改內核傳參
修改bootloader傳遞給kernel的bootargs,如果使用了設備樹的話,可以修改在chosen節點中bootargs屬性的值,具體方法內核文檔:Documentation/dynamic-debug-howto.txt
這種方案的優點是不需要修改驅動代碼。
方法二 在需要開啓log的驅動文件的開頭定義宏DEBUG
這樣該驅動文件中的pr_debug和dev_dbg就可以打開了。
比如驅動文件的名字是tfa98xx.c,那麼就在其第一個非註釋行添加DEBUG宏的定義:
/* * tfa98xx.c tfa98xx codec module * * Copyright (c) 2015 NXP Semiconductors * * Author: Sebastien Jan <[email protected]> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ #define DEBUG #define pr_fmt(fmt) "%s(): " fmt, __func__ #include <linux/module.h> #include <linux/i2c.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <linux/of_gpio.h> ......
爲什麼這樣做可以實現呢?下面我們以pr_debug爲例簡單分析。
在內核配置了CONFIG_DYNAMIC_DEBUG後,pr_debug的定義如下:
#define pr_debug(fmt, ...) \ dynamic_pr_debug(fmt, ##__VA_ARGS__)
dynamic_pr_debug的定義如下:
#define dynamic_pr_debug(fmt, ...) do { DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) __dynamic_pr_debug(&descriptor, pr_fmt(fmt), ##__VA_ARGS__); } while (0)
這裏用到了宏DEFINE_DYNAMIC_DEBUG_METADATA,定義如下:
#define DEFINE_DYNAMIC_DEBUG_METADATA(name, fmt) static struct _ddebug __aligned(8) __attribute__((section("__verbose"))) name = { .modname = KBUILD_MODNAME, .function = __func__, .filename = __FILE__, .format = (fmt), .lineno = __LINE__, .flags = _DPRINTK_FLAGS_DEFAULT, }
__dynamic_pr_debug的定義如下:
void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...) { va_list args; struct va_format vaf; char buf[PREFIX_SIZE]; BUG_ON(!descriptor); BUG_ON(!fmt); va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; printk(KERN_DEBUG "%s%pV", dynamic_emit_prefix(descriptor, buf), &vaf); va_end(args); }
從上面的代碼可以看到,每一個pr_debug都對應一個名爲descriptor,類型爲struct _ddebug的變量,存放在kernel的__verbose段。決定這個pr_debug能否輸出log的條件就是descriptor.flags & _DPRINTK_FLAGS_PRINT爲true。
在定義descriptor時,將其flags成員賦值爲了_DPRINTK_FLAGS_DEFAULT,下面看一下這兩個宏的定義:
#if defined DEBUG #define _DPRINTK_FLAGS_DEFAULT _DPRINTK_FLAGS_PRINT #else #define _DPRINTK_FLAGS_DEFAULT 0 #endif
可以看到,如果定義了宏DEBUG,那麼_DPRINTK_FLAGS_DEFAULT其實就是_DPRINTK_FLAGS_PRINT,所以默認就是可以打印的。如果沒有定義,那麼_DPRINTK_FLAGS_DEFAULT就是0,上面的條件不會成立,也就打印不出來。