内核版本:Linux-3.14
作者:彭东林
邮箱:pengdonglin137@163.com
pr_debug:
#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_dbg:
#if defined(CONFIG_DYNAMIC_DEBUG)
#define dev_dbg(dev, format, ...) \
do { \
dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \
} while (0)
#elif defined(DEBUG)
#define dev_dbg(dev, format, arg...) \
dev_printk(KERN_DEBUG, dev, format, ##arg)
#else
#define dev_dbg(dev, format, arg...) \
({ \
if (0) \
dev_printk(KERN_DEBUG, dev, format, ##arg); \
0; \
})
#endif
从上面的宏定义可以看出,要使用dynamic_*的话需要配置CONFIG_DYNAMIC_DEBUG。
Kernel hacking --->
Compile-time checks and compiler options --->
[*] Debug Filesystem
printk and dmesg options --->
[*] Enable dynamic printk() support
在[*] Enable dynamic printk() support 上点击h,可以看到帮助信息。
我们以pr_debug为例分析,
#define DEFINE_DYNAMIC_DEBUG_METADATA(name, fmt) \
struct _ddebug __aligned(8) \
))) name = { \
4: .modname = KBUILD_MODNAME, \
5: .function = __func__, \
6: .filename = __FILE__, \
7: .format = (fmt), \
8: .lineno = __LINE__, \
9: .flags = _DPRINTK_FLAGS_DEFAULT, \
10: }
11:
#define dynamic_pr_debug(fmt, ...) \
do { \
14: DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \
if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \
16: __dynamic_pr_debug(&descriptor, pr_fmt(fmt), \
17: ##__VA_ARGS__); \
while (0)
pr_fmt:
#ifndef pr_fmt
#define pr_fmt(fmt) fmt
#endif
所以,如果把pr_debug展开,那么就是:
do {
struct _ddebug __aligned(8) \
))) descriptor = { \
4: .modname = KBUILD_MODNAME, \
5: .function = __func__, \
6: .filename = __FILE__, \
7: .format = (fmt), \
8: .lineno = __LINE__, \
9: .flags = _DPRINTK_FLAGS_DEFAULT, \
10: }
if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \
12: __dynamic_pr_debug(&descriptor, fmt, \
13: ##__VA_ARGS__); \
while (0)
其中flags的赋值_DPRINTK_FLAGS_DEFAULT依赖DEBUG宏,
#if defined DEBUG
#define _DPRINTK_FLAGS_DEFAULT _DPRINTK_FLAGS_PRINT
#else
#define _DPRINTK_FLAGS_DEFAULT 0
#endif
即,如果没有定义DEBUG宏,pr_debug默认无法打印出来,需要按照下面介绍的方法。 flags的值对于打印什么和如何打印都非常重要。
此外还定义了static类型的变量descriptor,它在编译链接时会被放到__verbose段,可以看看arch/arm/kernel/vmlinux.lds:
1: OUTPUT_ARCH(arm)
2: ENTRY(stext)
3: jiffies = jiffies_64;
4: SECTIONS
5: {
/*
* XXX: The linker does not define how output sections are
* assigned to input sections when there are multiple statements
* matching the same input section name. There is no documented
* order of matching.
*
* unwind exit sections must be discarded before the rest of the
* unwind sections get included.
*/
15: /DISCARD/ : {
16: *(.ARM.exidx.exit.text)
17: *(.ARM.extab.exit.text)
18: *(.ARM.exidx.cpuexit.text)
19: *(.ARM.extab.cpuexit.text)
20:
21:
22: *(.exitcall.exit)
23: *(.alt.smp.init)
24: *(.discard)
25: *(.discard.*)
26: }
27: . = 0xC0000000 + 0x00008000;
28: .head.text : {
29: _text = .;
30: *(.head.text)
31: }
/* Real text segment */
/* Text and read-only data */
34: __exception_text_start = .;
35: *(.exception.text)
36: __exception_text_end = .;
37:
38: . = ALIGN(8); *(.text.hot) *(.text) *(.ref.text) *(.text.unlikely)
39: . = ALIGN(8); __sched_text_start = .; *(.sched.text) __sched_text_end = .;
40: . = ALIGN(8); __lock_text_start = .; *(.spinlock.text) __lock_text_end = .;
41: . = ALIGN(8); __kprobes_text_start = .; *(.kprobes.text) __kprobes_text_end = .;
42: . = ALIGN(8); __idmap_text_start = .; *(.idmap.text) __idmap_text_end = .; . = ALIGN(32); __hyp_idmap_text_start = .; *(.hyp.idmap.text) __hyp_idmap_text_end = .;
43: *(.fixup)
44: *(.gnu.warning)
45: *(.glue_7)
46: *(.glue_7t)
47: . = ALIGN(4);
/* Global offset table */
49:
50: }
51: . = ALIGN(((1 << 12))); .rodata : AT(ADDR(.rodata) - 0) { __start_rodata = .; *(.rodata) *(.rodata.*) *(__vermagic) . = ALIGN(8); __start___tracepoints_ptrs = .; *(__tracepoints_ptrs) __stop___tracepoints_ptrs = .; *(__tracepoints_strings) } .rodata1 : AT(ADDR(.rodata1) - 0) { *(.rodata1) } . = ALIGN(8); __bug_table : AT(ADDR(__bug_table) - 0) { __start___bug_table = .; *(__bug_table) __stop___bug_table = .; } .pci_fixup : AT(ADDR(.pci_fixup) - 0) { __start_pci_fixups_early = .; *(.pci_fixup_early) __end_pci_fixups_early = .; __start_pci_fixups_header = .; *(.pci_fixup_header) __end_pci_fixups_header = .; __start_pci_fixups_final = .; *(.pci_fixup_final) __end_pci_fixups_final = .; __start_pci_fixups_enable = .; *(.pci_fixup_enable) __end_pci_fixups_enable = .; __start_pci_fixups_resume = .; *(.pci_fixup_resume) __end_pci_fixups_resume = .; __start_pci_fixups_resume_early = .; *(.pci_fixup_resume_early) __end_pci_fixups_resume_early = .; __start_pci_fixups_suspend = .; *(.pci_fixup_suspend) __end_pci_fixups_suspend = .; } .builtin_fw : AT(ADDR(.builtin_fw) - 0) { __start_builtin_fw = .; *(.builtin_fw) __end_builtin_fw = .; } __ksymtab : AT(ADDR(__ksymtab) - 0) { __start___ksymtab = .; *(SORT(___ksymtab+*)) __stop___ksymtab = .; } __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - 0) { __start___ksymtab_gpl = .; *(SORT(___ksymtab_gpl+*)) __stop___ksymtab_gpl = .; } __ksymtab_unused : AT(ADDR(__ksymtab_unused) - 0) { __start___ksymtab_unused = .; *(SORT(___ksymtab_unused+*)) __stop___ksymtab_unused = .; } __ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - 0) { __start___ksymtab_unused_gpl = .; *(SORT(___ksymtab_unused_gpl+*)) __stop___ksymtab_unused_gpl = .; } __ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - 0) { __start___ksymtab_gpl_future = .; *(SORT(___ksymtab_gpl_future+*)) __stop___ksymtab_gpl_future = .; } __kcrctab : AT(ADDR(__kcrctab) - 0) { __start___kcrctab = .; *(SORT(___kcrctab+*)) __stop___kcrctab = .; } __kcrctab_gpl : AT(ADDR(__kcrctab_gpl) - 0) { __start___kcrctab_gpl = .; *(SORT(___kcrctab_gpl+*)) __stop___kcrctab_gpl = .; } __kcrctab_unused : AT(ADDR(__kcrctab_unused) - 0) { __start___kcrctab_unused = .; *(SORT(___kcrctab_unused+*)) __stop___kcrctab_unused = .; } __kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - 0) { __start___kcrctab_unused_gpl = .; *(SORT(___kcrctab_unused_gpl+*)) __stop___kcrctab_unused_gpl = .; } __kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - 0) { __start___kcrctab_gpl_future = .; *(SORT(___kcrctab_gpl_future+*)) __stop___kcrctab_gpl_future = .; } __ksymtab_strings : AT(ADDR(__ksymtab_strings) - 0) { *(__ksymtab_strings) } __init_rodata : AT(ADDR(__init_rodata) - 0) { *(.ref.rodata) } __param : AT(ADDR(__param) - 0) { __start___param = .; *(__param) __stop___param = .; } __modver : AT(ADDR(__modver) - 0) { __start___modver = .; *(__modver) __stop___modver = .; . = ALIGN(((1 << 12))); __end_rodata = .; } . = ALIGN(((1 << 12)));
52: . = ALIGN(4);
53: __ex_table : AT(ADDR(__ex_table) - 0) {
54: __start___ex_table = .;
55: *(__ex_table)
56: __stop___ex_table = .;
57: }
/*
* Stack unwinding tables
*/
61: . = ALIGN(8);
62: .ARM.unwind_idx : {
63: __start_unwind_idx = .;
64: *(.ARM.exidx*)
65: __stop_unwind_idx = .;
66: }
67: .ARM.unwind_tab : {
68: __start_unwind_tab = .;
69: *(.ARM.extab*)
70: __stop_unwind_tab = .;
71: }
72: .notes : AT(ADDR(.notes) - 0) { __start_notes = .; *(.note.*) __stop_notes = .; }
/* End of text and rodata section */
74: . = ALIGN((1 << 12));
75: __init_begin = .;
/*
* The vectors and stubs are relocatable code, and the
* only thing that matters is their relative offsets
*/
80: __vectors_start = .;
81: .vectors 0 : AT(__vectors_start) {
82: *(.vectors)
83: }
84: . = __vectors_start + SIZEOF(.vectors);
85: __vectors_end = .;
86: __stubs_start = .;
87: .stubs 0x1000 : AT(__stubs_start) {
88: *(.stubs)
89: }
90: . = __stubs_start + SIZEOF(.stubs);
91: __stubs_end = .;
92: . = ALIGN(8); .init.text : AT(ADDR(.init.text) - 0) { _sinittext = .; *(.init.text) *(.meminit.text) _einittext = .; }
93: .exit.text : {
94: *(.exit.text) *(.memexit.text)
95: }
96: .init.proc.info : {
97: . = ALIGN(4); __proc_info_begin = .; *(.proc.info.init) __proc_info_end = .;
98: }
99: .init.arch.info : {
100: __arch_info_begin = .;
101: *(.arch.info.init)
102: __arch_info_end = .;
103: }
104: .init.tagtable : {
105: __tagtable_begin = .;
106: *(.taglist.init)
107: __tagtable_end = .;
108: }
109: .init.pv_table : {
110: __pv_table_begin = .;
111: *(.pv_table)
112: __pv_table_end = .;
113: }
114: .init.data : {
115: *(.init.data) *(.meminit.data) *(.init.rodata) *(.meminit.rodata) . = ALIGN(32); __dtb_start = .; *(.dtb.init.rodata) __dtb_end = .;
116: . = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .;
117: __initcall_start = .; *(.initcallearly.init) __initcall0_start = .; *(.initcall0.init) *(.initcall0s.init) __initcall1_start = .; *(.initcall1.init) *(.initcall1s.init) __initcall2_start = .; *(.initcall2.init) *(.initcall2s.init) __initcall3_start = .; *(.initcall3.init) *(.initcall3s.init) __initcall4_start = .; *(.initcall4.init) *(.initcall4s.init) __initcall5_start = .; *(.initcall5.init) *(.initcall5s.init) __initcallrootfs_start = .; *(.initcallrootfs.init) *(.initcallrootfss.init) __initcall6_start = .; *(.initcall6.init) *(.initcall6s.init) __initcall7_start = .; *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;
118: __con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .;
119: __security_initcall_start = .; *(.security_initcall.init) __security_initcall_end = .;
120: . = ALIGN(4); __initramfs_start = .; *(.init.ramfs) . = ALIGN(8); *(.init.ramfs.info)
121: }
122: .exit.data : {
123: *(.exit.data) *(.memexit.data) *(.memexit.rodata)
124: }
125: __init_end = .;
126: . = ALIGN(8192);
127: __data_loc = .;
128: .data : AT(__data_loc) {
/* address in memory */
130: _sdata = .;
/*
* first, the init task union, aligned
* to an 8192 byte boundary.
*/
135: . = ALIGN(8192); *(.data..init_task)
136: . = ALIGN((1 << 12)); __nosave_begin = .; *(.data..nosave) . = ALIGN((1 << 12)); __nosave_end = .;
137: . = ALIGN((1 << 5)); *(.data..cacheline_aligned)
138: . = ALIGN((1 << 5)); *(.data..read_mostly) . = ALIGN((1 << 5));
/*
* and the usual data section
*/
___verbose = .;
143: CONSTRUCTORS
144: _edata = .;
145: }
146: _edata_loc = __data_loc + SIZEOF(.data);
147: . = ALIGN(0); __bss_start = .; . = ALIGN(0); .sbss : AT(ADDR(.sbss) - 0) { *(.sbss) *(.scommon) } . = ALIGN(0); .bss : AT(ADDR(.bss) - 0) { *(.bss..page_aligned) *(.dynbss) *(.bss) *(COMMON) } . = ALIGN(0); __bss_stop = .;
148: _end = .;
149: .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) }
150: .comment 0 : { *(.comment) }
151: }
可以看到,将__verbose段链接到了__start__verbose和__stop__verbose之间,也就是将来可以利用__start_verbose和__stop_verbose这两个符号来找到__verbose段。
一般我们使用debugfs的时候,在系统启动时挂载debugfs文件系统:
1: [root@TQ2440 /]# mount
2: rootfs on / type rootfs (rw)
3: /dev/root on / type yaffs2 (rw,relatime)
4: proc on /proc type proc (rw,relatime)
5: tmpfs on /tmp type tmpfs (rw,relatime)
6: sysfs on /sys type sysfs (rw,relatime)
7: tmpfs on /dev type tmpfs (rw,relatime)
8: debugfs on /sys/kernel/debug type debugfs (rw,relatime)
9: devpts on /dev/pts type devpts (rw,relatime,mode=600)
上面我们将debugfs挂载到了/sys/kernel/debug目录下面。
如果我们想打开某个文件中的pr_debug,如demo.c,可以如下操作:
echo -n "file demo.c +p" > /sys/kernel/debug/dynamic_debug/control
关闭的话:
echo -n "file demo.c -p" > /sys/kernel/debug/dynamic_debug/control
如果想分析一下为什么这样操作就可以实现将某个文件中的pr_debug打开,就需要分析背后的原理,如control节点的创建.
上面pr_debug展开后的descriptor变量被链接到了__verbose段,那么系统一定会有解析这个段的代码。
具体的代码位置是lib/dynamic_debug.c
__verbose的解析:
void)
2: {
struct _ddebug *iter, *iter_start;
char *modname = NULL;
char *cmdline;
int ret = 0;
int n = 0, entries = 0, modct = 0;
int verbose_bytes = 0;
9:
// __start___verbose 和 __stop___verbose 之间存放的都是struct _ddebug类型的静态变量,
// pr_debug展开后的descriptor就在这里。
if (__start___verbose == __stop___verbose) {
);
return 1;
15: }
16:
// 下面这段代码完成的任务是解析__verbose段。
18: iter = __start___verbose;
19: modname = iter->modname;
20: iter_start = iter;
for (; iter < __stop___verbose; iter++) {
22: entries++;
23: verbose_bytes += strlen(iter->modname) + strlen(iter->function)
24: + strlen(iter->filename) + strlen(iter->format);
25:
if (strcmp(modname, iter->modname)) {
27: modct++;
// 给每个modname(descriptor和modname是一对一(如demo.c中只有一个pr_debug或者dev_dbg)
// 或者多对一(如demo.c中有多个pr_debug或者dev_dbg)的关系,一个文件对应一个modname,
// 这些modname相同的descriptor在__verbose中是连续存放的)分配一个struct ddebug_table,
// 然后将其存放到全局变量ddebug_tables中,这里的n表示modname相同的descriptor的个数
32: ret = ddebug_add_module(iter_start, n, modname);
if (ret)
goto out_err;
35: n = 0;
36: modname = iter->modname;
37: iter_start = iter;
38: }
39: n++;
40: }
41: ret = ddebug_add_module(iter_start, n, modname);
if (ret)
goto out_err;
44:
45: ddebug_init_success = 1;
,
struct ddebug_table)),
int)(__stop___verbose - __start___verbose));
49:
/* apply ddebug_query boot param, dont unload tables on err */
// 这个用于处理系统boot过程中的pr_debug或者pr_dev的打印
'\0') {
);
56: ret = ddebug_exec_queries(ddebug_setup_string, NULL);
if (ret < 0)
,
59: ddebug_setup_string);
else
, ret);
62: }
/* now that ddebug tables are loaded, process all boot args
* again to find and activate queries given in dyndbg params.
* While this has already been done for known boot params, it
* ignored the unknown ones (dyndbg in particular). Reusing
* parse_args avoids ad-hoc parsing. This will also attempt
* to activate queries for not-yet-loaded modules, which is
* slightly noisy if verbose, but harmless.
*/
// 解析bootargs中的参数如有个module的名字是modname是unwind,那么可以是 bootargs 中包含 unwind.dyndbg
72: cmdline = kstrdup(saved_command_line, GFP_KERNEL);
, cmdline, NULL,
74: 0, 0, 0, &ddebug_dyndbg_boot_param_cb);
75: kfree(cmdline);
return 0;
77:
78: out_err:
79: ddebug_remove_all_tables();
return 0;
81: }
/* Allow early initialization for boot messages via boot param */
83: early_initcall(dynamic_debug_init);
第32行的n的含义也可以理解为demo.c中pr_debug和dev_dbg的个数;
ddebug_add_module:
int n,
char *name)
3: {
struct ddebug_table *dt;
char *new_name;
6:
sizeof(*dt), GFP_KERNEL);
if (dt == NULL)
return -ENOMEM;
10: new_name = kstrdup(name, GFP_KERNEL);
if (new_name == NULL) {
12: kfree(dt);
return -ENOMEM;
14: }
15: dt->mod_name = new_name;
16: dt->num_ddebugs = n;
17: dt->ddebugs = tab;
18:
19: mutex_lock(&ddebug_lock);
20: list_add_tail(&dt->link, &ddebug_tables);
21: mutex_unlock(&ddebug_lock);
22:
, n, dt->mod_name);
return 0;
25: }
第16行的num_ddebugs表示modname相同的descriptor的个数,将来在ddebug_change中会依次索引,这里只需要存放这些descriptor中的第一个;
接下来分析一下数组ddebug_setup_string的赋值:
/lib/dynamic_debug.c
#define DDEBUG_STRING_SIZE 1024
char ddebug_setup_string[DDEBUG_STRING_SIZE];
3:
char *str)
5: {
if (strlen(str) >= DDEBUG_STRING_SIZE) {
);
return 0;
9: }
10: strlcpy(ddebug_setup_string, str, DDEBUG_STRING_SIZE);
return 1;
12: }
13:
, ddebug_setup_query);
第14行代码表示的意思是如果cmdline(也就是u-boot传给kernel的参数)中如果有ddebug_query=”file demo.c +p”的部分,那么在kernel启动的时候就会调用函数ddebug_setup_query,然后将”file demo.c +p”拷贝到这个ddebug_setup_debug数组中。
ddebug_exec_queries:
/* handle multiple queries in query string, continue on error, return
last error or number of matching callsites. Module name is either
in param (for boot arg) or perhaps in query string.
*/
char *modname)
7: {
char *split;
int i, errs = 0, exitcode = 0, rc, nfound = 0;
10:
for (i = 0; query; query = split) {
// NULL
if (split)
'\0';
15:
16: query = skip_spaces(query);
'#')
continue;
19:
, i, query);
21:
23: rc = ddebug_exec_query(query, modname);
if (rc < 0) {
25: errs++;
26: exitcode = rc;
else {
28: nfound += rc;
29: }
30: i++;
31: }
,
33: i, nfound, errs);
34:
if (exitcode)
return exitcode;
return nfound;
38: }
ddebug_exec_query:
char *modname)
2: {
int flags = 0, mask = 0;
struct ddebug_query query;
#define MAXWORDS 9
int nwords, nfound;
char *words[MAXWORDS];
8:
// 这个函数完成的功能是以空格为分隔符,对query_string进行分割,然后将分割出来的
// 每个项的首地址存放到words中,即words每个元素指向分割出来的一项,返回值是项数
// 对于我们的例子就是:
// nwords的值是3
16: nwords = ddebug_tokenize(query_string, words, MAXWORDS);
if (nwords <= 0) {
);
return -EINVAL;
20: }
/* check flags 1st (last arg) so query is pairs of spec,val */
// +p 对应的是 flags |= _DPRINTK_FLAGS_PRINT, mask是~0U
// -p 对应的是 flags = 0 mask = ~_DPRINTK_FLAGS_PRINT
if (ddebug_parse_flags(words[nwords-1], &flags, &mask)) {
);
return -EINVAL;
28: }
29:
if (ddebug_parse_query(words, nwords-1, &query, modname)) {
);
return -EINVAL;
35: }
36:
/* actually go and implement the change */
// 改变demo.c中的pr_debug或者dev_dbg对应的descriptor的flags的值
39: nfound = ddebug_change(&query, flags, mask);
);
41:
return nfound;
43: }
ddebug_tokenize:
/*
* Split the buffer `buf' into space-separated words.
* or <0 on error.
*/
int maxwords)
8: {
int nwords = 0;
10:
while (*buf) {
char *end;
13:
/* Skip leading whitespace */
15: buf = skip_spaces(buf);
if (!*buf)
/* oh, it was trailing whitespace */
'#')
/* token starts comment, skip rest of line */
20:
/* find `end' of word, whitespace separated or quoted */
'\'') {
int quote = *buf++;
for (end = buf; *end && *end != quote; end++)
25: ;
if (!*end) {
, buf);
/* unclosed quote */
29: }
else {
for (end = buf; *end && !isspace(*end); end++)
32: ;
33: BUG_ON(end == buf);
34: }
35:
/* `buf' is start of word, `end' is one past its end */
if (nwords == maxwords) {
, maxwords);
/* ran out of words[] before bytes */
40: }
if (*end)
/* terminate the word */
43: words[nwords++] = buf;
44: buf = end;
45: }
46:
if (verbose) {
int i;
);
for (i = 0; i < nwords; i++)
, words[i]);
);
53: }
54:
return nwords;
56: }
ddebug_parse_flags:
char opt_char; } opt_array[] = {
'p' },
'm' },
'f' },
'l' },
't' },
'_' },
8: };
9:
10:
/*
* Parse `str' as a flags specification, format [-+=][p]+.
* Sets up *maskp and *flagsp to be used when changing the
* flags fields of matched _ddebug's. Returns 0 on success
* or <0 on error.
*/
int *flagsp,
int *maskp)
19: {
unsigned flags = 0;
'=', i;
22:
switch (*str) {
'+':
'-':
'=':
27: op = *str++;
break;
default:
, *str, str);
return -EINVAL;
32: }
, op);
34:
for (; *str ; ++str) {
for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) {
if (*str == opt_array[i].opt_char) {
38: flags |= opt_array[i].flag;
break;
40: }
41: }
if (i < 0) {
, *str, str);
return -EINVAL;
45: }
46: }
, flags);
48:
/* calculate final *flagsp, *maskp according to mask and op */
switch (op) {
'=':
52: *maskp = 0;
53: *flagsp = flags;
break;
'+':
56: *maskp = ~0U;
57: *flagsp = flags;
break;
'-':
60: *maskp = ~flags;
61: *flagsp = 0;
break;
63: }
, *flagsp, *maskp);
return 0;
66: }
这个函数用于解析"+p",他的含义是是否可以打印。此外,这里还可以是"+pmfl",这个参数打印的是最全的。如果要打印"+p"一定是要有的,"fmlt"可以根据需要任选。
其中, m 表示把模块名也打印出来;
f 表示把调用pr_debug的函数名也打印出来;
l 表示把pr_debug的行号也打印出来;
t 表示把进程号打印出来;
如下所示:
ddebug_parse_query:
/*
* Parse words[] as a ddebug query specification, which is a series
* of (keyword, value) pairs chosen from these possibilities:
*
* func <function-name>
* file <full-pathname>
* file <base-filename>
* module <module-name>
* format <escaped-string-to-find-in-format>
* line <lineno>
* line <first-lineno>-<last-lineno> // where either may be empty
*
* Only 1 of each type is allowed.
* Returns 0 on success, <0 on error.
*/
int nwords,
char *modname)
18: {
int i;
int rc = 0;
21:
/* check we have an even number of words */
if (nwords % 2 != 0) {
);
return -EINVAL;
26: }
sizeof(*query));
28:
if (modname)
/* support $modname.dyndbg=<multiple queries> */
31: query->module = modname;
32:
for (i = 0; i < nwords; i += 2) {
)) {
);
)) {
);
)) {
);
)) {
41: string_unescape_inplace(words[i+1], UNESCAPE_SPACE |
42: UNESCAPE_OCTAL |
43: UNESCAPE_SPECIAL);
);
)) {
char *first = words[i+1];
'-');
if (query->first_lineno || query->last_lineno) {
);
return -EINVAL;
51: }
if (last)
'\0';
if (parse_lineno(first, &query->first_lineno) < 0)
return -EINVAL;
if (last) {
/* range <first>-<last> */
if (parse_lineno(last, &query->last_lineno) < 0)
return -EINVAL;
60:
if (query->last_lineno < query->first_lineno) {
,
63: query->last_lineno,
64: query->first_lineno);
return -EINVAL;
66: }
else {
68: query->last_lineno = query->first_lineno;
69: }
else {
, words[i]);
return -EINVAL;
73: }
if (rc)
return rc;
76: }
);
return 0;
79: }
这个函数用于解析"file" "demo.c",然后构造出一个过滤器query,对于这个例子,将来query->filename会赋值为"demo.c".如果在bootargs中设置了demo.dyndbg="func xxx",表示打印模块demo中函数xxx中的pr_debug或者dev_dbg,此时query->function会被赋值为"xxx".当然也可以使用echo -n 'file demo.c line 1603 +p' > /sys/kernel/debug/dynamic_debug/control,其中:
words[0] --- "file"
words[1] --- "demo.c"
words[2] --- "line"
words[3] --- "1603"
words[4] --- "+p"
表示打印文件demo.c中第1603行的pr_debug或者dev_dbg.
ddebug_change:
/*
* Search the tables for _ddebug's which match the given `query' and
* apply the `flags' and `mask' to them. Returns number of matching
* callsites, normally the same as number of changes. If verbose,
* logs the changes. Takes ddebug_lock.
*/
struct ddebug_query *query,
int mask)
9: {
int i;
struct ddebug_table *dt;
int newflags;
int nfound = 0;
char flagbuf[10];
15:
/* search for matching ddebugs */
17: mutex_lock(&ddebug_lock);
18: list_for_each_entry(dt, &ddebug_tables, link) {
19:
/* match against the module name */
// match_wildcard 如果匹配成功返回true
if (query->module &&
23: !match_wildcard(query->module, dt->mod_name))
continue;
25:
for (i = 0; i < dt->num_ddebugs; i++) {
// modname相同的descriptor是连续存放的
28:
/* match against the source filename */
if (query->filename &&
31: !match_wildcard(query->filename, dp->filename) &&
32: !match_wildcard(query->filename,
33: kbasename(dp->filename)) &&
34: !match_wildcard(query->filename,
35: trim_prefix(dp->filename)))
continue;
37:
/* match against the function */
if (query->function &&
40: !match_wildcard(query->function, dp->function))
continue;
42:
/* match against the format */
if (query->format &&
45: !strstr(dp->format, query->format))
continue;
47:
/* match against the line number range */
if (query->first_lineno &&
50: dp->lineno < query->first_lineno)
continue;
if (query->last_lineno &&
53: dp->lineno > query->last_lineno)
continue;
55:
// 找到了匹配项
57:
// 赋值
if (newflags == dp->flags)
continue;
// 将descriptor的flags修改成新的值,在打印是会判断
,
63: trim_prefix(dp->filename), dp->lineno,
64: dt->mod_name, dp->function,
65: ddebug_describe_flags(dp, flagbuf,
sizeof(flagbuf)));
67: }
68: }
69: mutex_unlock(&ddebug_lock);
70:
if (!nfound && verbose)
);
73:
return nfound;
75: }
函数match_wildcard支持通配符匹配,比如我想打印文件demo_driver.c中的pr_debug,那么我可以这样来:
echo -n "file demo_dri* +p" > /sys/kernel/debug/dynamic_debug/control
上面query可以认为是过滤器,如果以上面的demo_driver.c为例,那么"file demo_driver.c +p",最后的处理结果是将demo_driver.c中所有的pr_debug和dev_dbg对应的descriptor的flags的值都设置为_DPRINTK_FLAGS_PRINT,那么就可以打印出来了。
match_wildcard:
/**
* match_wildcard: - parse if a string matches given wildcard pattern
* @pattern: wildcard pattern
* @str: the string to be parsed
*
* Description: Parse the string @str to check if matches wildcard
* pattern @pattern. The pattern may contain two type wildcardes:
* '*' - matches zero or more characters
* '?' - matches one character
* If it's matched, return true, else return false.
*/
char *str)
13: {
char *s = str;
char *p = pattern;
bool star = false;
17:
while (*s) {
switch (*p) {
'?':
21: s++;
22: p++;
break;
'*':
25: star = true;
26: str = s;
if (!*++p)
return true;
29: pattern = p;
break;
default:
if (*s == *p) {
33: s++;
34: p++;
else {
if (!star)
return false;
38: str++;
39: s = str;
40: p = pattern;
41: }
break;
43: }
44: }
45:
'*')
47: ++p;
return !*p;
49: }
下面是如果在bootargs中设置了诸如”demo.dyndbg”或者”demo.dyndbg=+/-p”或者"demo.dyndbg="func xxx_write line 10 +p""那么下面的这个函数就会在执行parse_args的时候被调用
ddebug_dyndbg_param_cb:
1: 如在bootargs中设置了: demo.dyndbg 或者 demo.dyndbg=+p,那么demo.c的pr_debug就会打开
2:
/* handle both dyndbg and $module.dyndbg params at boot */
char *val,
char *unused)
6: {
, param, val);
return ddebug_dyndbg_param_cb(param, val, NULL, 0);
9: }
10:
/* helper for ddebug_dyndbg_(boot|module)_param_cb */
char *val,
int on_err)
14: {
char *sep;
16:
'.');
if (sep) {
/* needed only for ddebug_dyndbg_boot_param_cb */
'\0';
21: modname = param;
22: param = sep + 1;
23: }
))
/* determined by caller */
// 如果val是空的话(即只有demo.dyndbg),默认打开pr_debug
), modname);
28:
/* query failure shouldnt stop module load */
30: }
31:
先分析到这里,接下请看下一篇博客,主要分析一下:
echo –n “file demo.c +p”> /sys/kernel/debug/dynamic_debug/control 的原理。
==========================================================================================
转载:http://blog.csdn.net/pillarbuaa/article/details/7634546
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。