dynamic debug log输出机制
0. 注意该机制只对 dev_dbg -> dynamic_dev_dbg 定义的debug log输出加以控制
1. 如何使用:(kernel/Documentation/dynamic-debug-howto.txt)
mkdir /data/debugfs
mount -t debugfs none /data/debugfs
echo -n 'file ab8500_fg.c +p' > /data/debugfs/dynamic_debug/control //增加该文件dynamic debug的输出
echo -n 'file ab8500_fg.c -p' > /data/debugfs/dynamic_debug/control //去掉该文件dynamic debug的输出
2. 如果想使用debugfs 必须,在kernel的config文件(kernel/arch/arm/configs/semc_lotus_deconfig)中有CONFIG_DEBUG_FS=y
3. 如果需要使用Dynamic debug机制,需要在kernel的config文件(kernel/arch/arm/configs/semc_lotus_deconfig)中有CONFIG_DYNAMIC_DEBUG=y
4. dev_dbg@kernel/include/linux/device.h ->dynamic_dev_dbg@kernel/include/linux/dynamic_debug.h
#define dynamic_dev_dbg(dev, fmt, ...) do { \
static struct _ddebug descriptor \
__used \
__attribute__((section("__verbose"), aligned(8))) = \
{ KBUILD_MODNAME, __func__, __FILE__, fmt, DEBUG_HASH, \
DEBUG_HASH2, __LINE__, _DPRINTK_FLAGS_DEFAULT }; \
if (__dynamic_dbg_enabled(descriptor)) \
dev_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__); \
} while (0)
a. 该define 最终会展开在被调用dev_dbg函数的c文件中,也就是KBUILD_MODNAME, __func__, __FILE__, __LINE__ 会有对应的字符串
b. _DPRINTK_FLAGS_DEFAULT=0;
c. DEBUG_HASH和DEBUG_HASH2的定义在kernel/scripts/makefile.lib中
DEBUG_HASH=$(shell ./scripts/basic/hash djb2 $(@D)$(modname)) //利用djb2 hash算法,计算modname的DEBUG_HASH value;
DEBUG_HASH2=$(shell ./scripts/basic/hash r5 $(@D)$(modname)) //利用r5 hash算法,计算modname的DEBUG_HASH2 value;
d. 分析 kernel/scripts/basic/hash.c,会生成out/target/product/lotus/obj/kernel/scripts/basic/hash, shell可执行文件
e. 在编译连接完成后,该 descriptor 值会被保存到 data section的 __verbose区
5. dynamic_debug_init@kernel/lib/dynamic_debug.c
a. dir = debugfs_create_dir("dynamic_debug", NULL);
b. file = debugfs_create_file("control", 0644, dir, NULL, &ddebug_proc_fops); //在debugfs文件系统中创建dynamic_debug/control 文件
c. 通过__start___verbose和__stop___verbose@kernel/include/asm-generic/vmlinux.lds.h中,实际上是获取保存在__verbose区的 struct _ddebug 数据(就是前面编译后添加到data section的__verbose)
d. 如果是不同的modname,就添加到ddebug_tables 中,也就是所有dynamic_dev_dbg的模块(modname),文件(__FILE__),行(__LINE__),函数(__func__),是否输出的flag,对应的hash value都会逐条保存到ddebug_tables中
6. 分析 echo -n 'file ab8500_charger.c +p' > /data/debugfs/dynamic_debug/control 的实际操作
a. 通过system call,debugfs文件系统会调用到ddebug_proc_write,ddebug_parse_query和ddebug_parse_flags@kernel/lib/dynamic_debug.c分析传入的参数字符串
b. 在ddebug_change@kernel/lib/dynamic_debug.c中,会根据modname, __FILE__, __LINE__, __func__信息在ddebug_tables找到对应的item.
c. 然后根据输入的是 +p或-p ,来标志struct _ddebug中flag字段,还有根据struct _ddebug中的primary_hash和secondary_hash,来标志global value dynamic_debug_enabled和dynamic_debug_enabled2 对应的位,会在__dynamic_dbg_enabled@kernel/include/linux/dynamic_debug.h用到
7. 分析 #cat /data/debugfs/dynamic_debug/control的实际操作
a. 先ddebug_proc_open中有err = seq_open(file, &ddebug_proc_seqops);应用了seq file的读写机制
b. 然后seq_read,利用seq file机制逐个读出和显示ddebug_tables中的内容
8. long long dynamic_debug_enabled和dynamic_debug_enabled2@kernel/lib/dynamic_debugc,用于标志某个mod(可包含一个或多个文件,比如ab8500_fg mod,目前只包含ab8500_fg.c file)是否可以输出debug log的模块. 最多可以标志64*64=4096个dev_debug/dynamic_dev_dbg.
9. 是否输出dev_log/dynamic_dev_dbg的log, 关键是如下判断,@kernel/include/linux/dynamic_debug.h
#define __dynamic_dbg_enabled(dd) ({ \
int __ret = 0; \
if (unlikely((dynamic_debug_enabled & (1LL << DEBUG_HASH)) && \
(dynamic_debug_enabled2 & (1LL << DEBUG_HASH2)))) \
if (unlikely(dd.flags)) \
__ret = 1; \
__ret; })
a. dynamic_debug_enabled和dynamic_debug_enabled2就是前面分析的是否输出该modname的两个long long的组合标志位
b. DEBUG_HASH和DEBUG_HASH2 如前面所解释
c. dd.flag 默认为_DPRINTK_FLAGS_DEFAULT,但通过debugfs文件系统,最终操作ddebug_proc_write函数,会设置为_DPRINTK_FLAGS_PRIN或_DPRINTK_FLAGS_DEFAULT
10. debugfs 文件系统中的内容保存在那?????内存中,类似proc
11. 小结:如果你需要用到dynamic debug info, 你需要在你的 .c 文件中查看是否用到了dev_log 输出log。