内核版本:Linux-3.14
作者:彭东林
下面我们简要分析
> /sys/kernel/debug/dynamic_debug/control
的实现。
首先看一下dynamic_dedbg/control是如何生成的?
代码位置 lib/dynamic_debug.c
void)
2: {
struct dentry *dir, *file;
4:
if (!ddebug_init_success)
return -ENODEV;
7:
, NULL);
if (!dir)
return -ENOMEM;
, 0644, dir, NULL,
12: &ddebug_proc_fops);
if (!file) {
14: debugfs_remove(dir);
return -ENOMEM;
16: }
return 0;
18: }
19:
20: fs_initcall(dynamic_debug_init_debugfs);
这个函数会在kernel启动的时候执行,在/sys/kernel/debug下创建目录dynamic_debug,然后在dynamic_debug下面创建control节点,并将这个节点的操作函数集设置为ddebug_proc_fops。
struct file_operations ddebug_proc_fops = {
2: .owner = THIS_MODULE,
3: .open = ddebug_proc_open,
4: .read = seq_read,
5: .llseek = seq_lseek,
6: .release = seq_release_private,
7: .write = ddebug_proc_write
8: };
这里涉及到了顺序文件seq_file,关于这部分知识可以参考:
序列文件(seq_file)接口
这里我们需要看一下ddebug_proc_open:
/*
* File_ops->open method for <debugfs>/dynamic_debug/control. Does
* the seq_file setup dance, and also creates an iterator to walk the
* _ddebugs. Note that we create a seq_file always, even for O_WRONLY
* files where it's not needed, as doing so simplifies the ->release
* method.
*/
struct file *file)
9: {
struct ddebug_iter *iter;
int err;
12:
);
14:
sizeof(*iter), GFP_KERNEL);
if (iter == NULL)
return -ENOMEM;
18:
19: err = seq_open(file, &ddebug_proc_seqops);
if (err) {
21: kfree(iter);
return err;
23: }
private = iter;
return 0;
26: }
其中:
struct seq_operations ddebug_proc_seqops = {
2: .start = ddebug_proc_start,
3: .next = ddebug_proc_next,
4: .show = ddebug_proc_show,
5: .stop = ddebug_proc_stop
6: };
从上面的代码可以看出,我们需要分析的函数主要是ddebug_proc_seqops中的以及ddebug_proc_write。
其中,对于 echo –n “file demo.c +p”> /sys/kernel/debug/dynamic_debug/control来说,函数调用:ddebug_proc_open ---> ddebug_proc_write
下面我们一个一个看。
ddebug_proc_write:
/*
* File_ops->write method for <debugfs>/dynamic_debug/conrol. Gathers the
* command text from userspace, parses and executes it.
*/
#define USER_BUF_PAGE 4096
char __user *ubuf,
7: size_t len, loff_t *offp)
8: {
char *tmpbuf;
int ret;
11:
if (len == 0)
return 0;
if (len > USER_BUF_PAGE - 1) {
, USER_BUF_PAGE);
return -E2BIG;
17: }
18: tmpbuf = kmalloc(len + 1, GFP_KERNEL);
if (!tmpbuf)
return -ENOMEM;
if (copy_from_user(tmpbuf, ubuf, len)) {
22: kfree(tmpbuf);
return -EFAULT;
24: }
'\0';
int)len);
27:
28: ret = ddebug_exec_queries(tmpbuf, NULL);
29: kfree(tmpbuf);
if (ret < 0)
return ret;
32:
33: *offp += len;
return len;
35: }
如果以 echo –n “file demo.c +p” > /sys/kernel/debug/dynamic_debug/control 为例:
上面的代码第28行传给ddebug_exec_queries的参数tembuf中存放的就是 “file demo.c +p”,函数ddebug_exec_queries的分析在上一篇博客中已经分析过了。
此外,如何查看某个文件中pr_debug或者dev_dbg的设置情况呢?
1: [root@TQ2440 /]# cat /sys/kernel/debug/dynamic_debug/control
2: # filename:lineno [module]function flags format
11: ......
上面每一行对应的就是一个pr_debug或者dev_dbg,以第5行为例
arch/arm/kernel/unwind.c:162 [unwind]unwind_find_origin =_ "%s(%p, %p)\012"
表示在文件unwind.c的第162行,modname是unwind,function是unwind_find_origin,”=_”表示不打印, 最后一个表示的是pr_debug要打印的内容。
如果执行
echo -n "file unwind.c +pfmlt" > /sys/kernel/debug/dynamic_debug/control
然后再次执行
cat /sys/kernel/debug/dynamic_debug/control | grep “unwind.c:162”
得到的结果就是:
arch/arm/kernel/unwind.c:162 [unwind]unwind_find_origin =pmflt "%s(%p, %p)\012"
然后我们分析一下 cat /sys/kernel/debug/dynamic_debug/control 的函数调用:
ddebug_proc_open ---> seq_read,在seq_read中又依次调用了ddebug_proc_seqops中的:
ddebug_proc_start/ddebug_proc_next/ddebug_proc_show/ddebug_proc_stop函数。
ddebug_proc_start:
/*
* Seq_ops start method. Called at the start of every
* read() call from userspace. Takes the ddebug_lock and
* seeks the seq_file's iterator to the given position.
*/
struct seq_file *m, loff_t *pos)
7: {
private;
struct _ddebug *dp;
int n = *pos;
11:
long)*pos);
13:
14: mutex_lock(&ddebug_lock);
15:
if (!n)
return SEQ_START_TOKEN;
if (n < 0)
return NULL;
20: dp = ddebug_iter_first(iter);
while (dp != NULL && --n > 0)
22: dp = ddebug_iter_next(iter);
return dp;
24: }
第8行中的iter指向的内存是在ddebug_proc_open中调用kzalloc分配的。
第20行函数的目的是获取ddebug_tables中的第一项
ddebug_iter_first:
/*
* Set the iterator to point to the first _ddebug object
* and return a pointer to that first object. Returns
* NULL if there are no _ddebugs at all.
*/
struct ddebug_iter *iter)
7: {
if (list_empty(&ddebug_tables)) {
9: iter->table = NULL;
10: iter->idx = 0;
return NULL;
12: }
13: iter->table = list_entry(ddebug_tables.next,
struct ddebug_table, link);
15: iter->idx = 0;
return &iter->table->ddebugs[iter->idx];
17: }
这里ddebug_tables是在解析__verbose段的时候填充,这里是获取第一项;注意这里每个文件中的pr_debug和dev_dbg的modname都相同,也就是文件名,每个modname在ddebug_tables中只有一项,每个ddebug_table中的ddebugs指向了模块名为modname的descriptor的第一个,因为这些descriptor在内存中是连续存放的,所以通过ddebugs就可索引了,个数是num_ddebugs.
ddebug_iter_next:
/*
* Advance the iterator to point to the next _ddebug
* object from the one the iterator currently points at,
* and returns a pointer to the new _ddebug. Returns
* NULL if the iterator has seen all the _ddebugs.
*/
struct ddebug_iter *iter)
8: {
if (iter->table == NULL)
return NULL;
if (++iter->idx == iter->table->num_ddebugs) {
/* iterate to next table */
13: iter->idx = 0;
if (list_is_last(&iter->table->link, &ddebug_tables)) {
15: iter->table = NULL;
return NULL;
17: }
18: iter->table = list_entry(iter->table->link.next,
struct ddebug_table, link);
20: }
return &iter->table->ddebugs[iter->idx];
22: }
ddebug_proc_next:
/*
* Seq_ops next method. Called several times within a read()
* call from userspace, with ddebug_lock held. Walks to the
* next _ddebug object with a special case for the header line.
*/
void *p, loff_t *pos)
7: {
private;
struct _ddebug *dp;
10:
,
long)*pos);
13:
if (p == SEQ_START_TOKEN)
15: dp = ddebug_iter_first(iter);
else
17: dp = ddebug_iter_next(iter);
18: ++*pos;
return dp;
20: }
下面是我画的一张图,大概表示出了ddebug_tables/ddebug_tables/_ddebug的关系:
上面的函数返回的类型是struct _ddebug,用于遍历所有的_ddebug.
ddebug_proc_show:
/*
* Seq_ops show method. Called several times within a read()
* call from userspace, with ddebug_lock held. Formats the
* current _ddebug as a single human-readable line, with a
* special case for the header line.
*/
void *p)
8: {
private;
struct _ddebug *dp = p;
char flagsbuf[10];
12:
, m, p);
14:
if (p == SEQ_START_TOKEN) {
16: seq_puts(m,
);
return 0;
19: }
20:
,
22: trim_prefix(dp->filename), dp->lineno,
23: iter->table->mod_name, dp->function,
sizeof(flagsbuf)));
);
);
27:
return 0;
29: }
我们最终看到的结果:
1: [root@TQ2440 /]# cat /sys/kernel/debug/dynamic_debug/control | head -n 10
2: # filename:lineno [module]function flags format
上面的内容就是ddebug_proc_show打印出来的。
trim_prefix:
/* Return the path relative to source root */
char *path)
3: {
);
5:
if (strncmp(path, __FILE__, skip))
/* prefix mismatch, don't skip */
8:
return path + skip;
10: }
这个函数的目的是将文件名的绝对路径转换为相对路径,相对于kernel源码的根目录。
ddebug_describe_flags:
char opt_char; } opt_array[] = {
'p' },
'm' },
'f' },
'l' },
't' },
'_' },
8: };
9:
/* format a string into buf[] which describes the _ddebug's flags */
char *buf,
12: size_t maxlen)
13: {
char *p = buf;
int i;
16:
17: BUG_ON(maxlen < 6);
for (i = 0; i < ARRAY_SIZE(opt_array); ++i)
if (dp->flags & opt_array[i].flag)
20: *p++ = opt_array[i].opt_char;
if (p == buf)
'_';
'\0';
24:
return buf;
26: }
这个函数是将flags的值转换为字符串,存放到buf中,并将buf返回。
ddebug_proc_stop:
/*
* Seq_ops stop method. Called at the end of each read()
* call from userspace. Drops ddebug_lock.
*/
void *p)
6: {
, m, p);
8: mutex_unlock(&ddebug_lock);
9: }
在上一篇博客中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)
第11行就是判断descriptor.flags的值,看是否能够打印。
__dynamic_pr_debug:
char *fmt, ...)
2: {
3: va_list args;
int res;
struct va_format vaf;
char buf[PREFIX_SIZE];
7:
8: BUG_ON(!descriptor);
9: BUG_ON(!fmt);
10:
11: va_start(args, fmt);
12:
13: vaf.fmt = fmt;
14: vaf.va = &args;
15:
,
17: dynamic_emit_prefix(descriptor, buf), &vaf);
18:
19: va_end(args);
20:
return res;
22: }
dynamic_emit_prefix:
char *buf)
2: {
int pos_after_tid;
int pos = 0;
5:
'\0';
7:
if (desc->flags & _DPRINTK_FLAGS_INCL_TID) {
if (in_interrupt())
);
else
,
13: task_pid_vnr(current));
14: }
15: pos_after_tid = pos;
if (desc->flags & _DPRINTK_FLAGS_INCL_MODNAME)
,
18: desc->modname);
if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME)
,
21: desc->function);
if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO)
,
24: desc->lineno);
if (pos - pos_after_tid)
);
if (pos >= PREFIX_SIZE)
'\0';
29:
return buf;
31: }
这个函数会根据flags(如fmtl)的值向buf中填充内容,然后将buf返回给printk。
先分析到这里,下一篇总结一些开启pr_debug的一些方法。
完。