FROM:http://www.cnblogs.com/pengdonglin137/p/4622238.html
内核版本:linux-3.14
作者:彭东林
邮箱:pengdonglin137@163.com
下面我们简要分析
1: echo -n "file demo.c +p" > /sys/kernel/debug/dynamic_debug/control的实现。
首先看一下dynamic_dedbg/control是如何生成的?
代码位置 lib/dynamic_debug.c
1: static int __init dynamic_debug_init_debugfs(void)2: {3: struct dentry *dir, *file;4:5: if (!ddebug_init_success)6: return -ENODEV;7:8: dir = debugfs_create_dir("dynamic_debug", NULL);9: if (!dir)10: return -ENOMEM;11: file = debugfs_create_file("control", 0644, dir, NULL,12: &ddebug_PRoc_fops);13: if (!file) {14: debugfs_remove(dir);15: return -ENOMEM;16: }17: return 0;18: }19:20: fs_initcall(dynamic_debug_init_debugfs);这个函数会在kernel启动的时候执行,在/sys/kernel/debug下创建目录dynamic_debug,然后在dynamic_debug下面创建control节点,并将这个节点的操作函数集设置为ddebug_proc_fops。
1: static const 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_write8: };这里涉及到了顺序文件seq_file,关于这部分知识可以参考:
序列文件(seq_file)接口
这里我们需要看一下ddebug_proc_open:
1: /*2: * File_ops->open method for <debugfs>/dynamic_debug/control. Does3: * the seq_file setup dance, and also creates an iterator to walk the4: * _ddebugs. Note that we create a seq_file always, even for O_WRONLY5: * files where it's not needed, as doing so simplifies the ->release6: * method.7: */8: static int ddebug_proc_open(struct inode *inode, struct file *file)9: {10: struct ddebug_iter *iter;11: int err;12:13: vpr_info("called/n");14:15: iter = kzalloc(sizeof(*iter), GFP_KERNEL);16: if (iter == NULL)17: return -ENOMEM;18:19: err = seq_open(file, &ddebug_proc_seqops);20: if (err) {21: kfree(iter);22: return err;23: }24: ((struct seq_file *)file->private_data)->private = iter;25: return 0;26: }其中:
1: static const 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_stop6: };从上面的代码可以看出,我们需要分析的函数主要是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:
1: /*2: * File_ops->write method for <debugfs>/dynamic_debug/conrol. Gathers the3: * command text from userspace, parses and executes it.4: */5: #define USER_BUF_PAGE 40966: static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf,7: size_t len, loff_t *offp)8: {9: char *tmpbuf;10: int ret;11:12: if (len == 0)13: return 0;14: if (len > USER_BUF_PAGE - 1) {15: pr_warn("expected <%d bytes into control/n", USER_BUF_PAGE);16: return -E2BIG;17: }18: tmpbuf = kmalloc(len + 1, GFP_KERNEL);19: if (!tmpbuf)20: return -ENOMEM;21: if (copy_from_user(tmpbuf, ubuf, len)) {22: kfree(tmpbuf);23: return -EFAULT;24: }25: tmpbuf[len] = '/0';26: vpr_info("read %d bytes from userspace/n", (int)len);27:28: ret = ddebug_exec_queries(tmpbuf, NULL);29: kfree(tmpbuf);30: if (ret < 0)31: return ret;32:33: *offp += len;34: 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/control2: # filename:lineno [module]function flags format3: init/main.c:679 [main]do_one_initcall_debug =p "calling %pF @ %i/012"4: init/main.c:686 [main]do_one_initcall_debug =p "initcall %pF returned %d after %lld usecs/012"5: arch/arm/kernel/unwind.c:162 [unwind]unwind_find_origin =_ "%s(%p, %p)/012"6: arch/arm/kernel/unwind.c:173 [unwind]unwind_find_origin =_ "%s -> %p/012"7: arch/arm/kernel/unwind.c:461 [unwind]unwind_table_add =_ "%s(%08lx, %08lx, %08lx, %08lx)/012"8: arch/arm/kernel/unwind.c:117 [unwind]search_index =_ "%s(%08lx, %p, %p, %p)/012"9: arch/arm/kernel/unwind.c:341 [unwind]unwind_frame =_ "%s(pc = %08lx lr = %08lx sp = %08lx)/012"10: arch/arm/kernel/unwind.c:182 [unwind]unwind_find_idx =_ "%s(%08lx)/012"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:
1: /*2: * Seq_ops start method. Called at the start of every3: * read() call from userspace. Takes the ddebug_lock and4: * seeks the seq_file's iterator to the given position.5: */6: static void *ddebug_proc_start(struct seq_file *m, loff_t *pos)7: {8: struct ddebug_iter *iter = m->private;9: struct _ddebug *dp;10: int n = *pos;11:12: vpr_info("called m=%p *pos=%lld/n", m, (unsigned long long)*pos);13:14: mutex_lock(&ddebug_lock);15:16: if (!n)17: return SEQ_START_TOKEN;18: if (n < 0)19: return NULL;20: dp = ddebug_iter_first(iter);21: while (dp != NULL && --n > 0)22: dp = ddebug_iter_next(iter);23: return dp;24: }第8行中的iter指向的内存是在ddebug_proc_open中调用kzalloc分配的。
第20行函数的目的是获取ddebug_tables中的第一项
ddebug_iter_first:
1: /*2: * Set the iterator to point to the first _ddebug object3: * and return a pointer to that first object. Returns4: * NULL if there are no _ddebugs at all.5: */6: static struct _ddebug *ddebug_iter_first(struct ddebug_iter *iter)7: {8: if (list_empty(&ddebug_tables)) {9: iter->table = NULL;10: iter->idx = 0;11: return NULL;12: }13: iter->table = list_entry(ddebug_tables.next,14: struct ddebug_table, link);15: iter->idx = 0;16: 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:
1: /*2: * Advance the iterator to point to the next _ddebug3: * object from the one the iterator currently points at,4: * and returns a pointer to the new _ddebug. Returns5: * NULL if the iterator has seen all the _ddebugs.6: */7: static struct _ddebug *ddebug_iter_next(struct ddebug_iter *iter)8: {9: if (iter->table == NULL)10: return NULL;11: if (++iter->idx == iter->table->num_ddebugs) {12: /* iterate to next table */13: iter->idx = 0;14: if (list_is_last(&iter->table->link, &ddebug_tables)) {15: iter->table = NULL;16: return NULL;17: }18: iter->table = list_entry(iter->table->link.next,19: struct ddebug_table, link);20: }21: return &iter->table->ddebugs[iter->idx];22: }
ddebug_proc_next:
1: /*2: * Seq_ops next method. Called several times within a read()3: * call from userspace, with ddebug_lock held. Walks to the4: * next _ddebug object with a special case for the header line.5: */6: static void *ddebug_proc_next(struct seq_file *m, void *p, loff_t *pos)7: {8: struct ddebug_iter *iter = m->private;9: struct _ddebug *dp;10:11: vpr_info("called m=%p p=%p *pos=%lld/n",12: m, p, (unsigned long long)*pos);13:14: if (p == SEQ_START_TOKEN)15: dp = ddebug_iter_first(iter);16: else17: dp = ddebug_iter_next(iter);18: ++*pos;19: return dp;20: }
下面是我画的一张图,大概表示出了ddebug_tables/ddebug_tables/_ddebug的关系:
上面的函数返回的类型是struct _ddebug,用于遍历所有的_ddebug.
ddebug_proc_show:
1: /*2: * Seq_ops show method. Called several times within a read()3: * call from userspace, with ddebug_lock held. Formats the4: * current _ddebug as a single human-readable line, with a5: * special case for the header line.6: */7: static int ddebug_proc_show(struct seq_file *m, void *p)8: {9: struct ddebug_iter *iter = m->private;10: struct _ddebug *dp = p;11: char flagsbuf[10];12:13: vpr_info("called m=%p p=%p/n", m, p);14:15: if (p == SEQ_START_TOKEN) {16: seq_puts(m,17: "# filename:lineno [module]function flags format/n");18: return 0;19: }20:21: seq_printf(m, "%s:%u [%s]%s =%s /"",22: trim_prefix(dp->filename), dp->lineno,23: iter->table->mod_name, dp->function,24: ddebug_describe_flags(dp, flagsbuf, sizeof(flagsbuf)));25: seq_escape(m, dp->format, "/t/r/n/"");26: seq_puts(m, "/"/n");27:28: return 0;29: }我们最终看到的结果:
1: [root@TQ2440 /]# cat /sys/kernel/debug/dynamic_debug/control | head -n 102: # filename:lineno [module]function flags format3: init/main.c:679 [main]do_one_initcall_debug =p "calling %pF @ %i/012"4: init/main.c:686 [main]do_one_initcall_debug =p "initcall %pF returned %d after %lld usecs/012"5: arch/arm/kernel/unwind.c:162 [unwind]unwind_find_origin =_ "%s(%p, %p)/012"6: arch/arm/kernel/unwind.c:173 [unwind]unwind_find_origin =_ "%s -> %p/012"上面的内容就是ddebug_proc_show打印出来的。
trim_prefix:
1: /* Return the path relative to source root */2: static inline const char *trim_prefix(const char *path)3: {4: int skip = strlen(__FILE__) - strlen("lib/dynamic_debug.c");5:6: if (strncmp(path, __FILE__, skip))7: skip = 0; /* prefix mismatch, don't skip */8:9: return path + skip;10: }这个函数的目的是将文件名的绝对路径转换为相对路径,相对于kernel源码的根目录。
ddebug_describe_flags:
1: static struct { unsigned flag:8; char opt_char; } opt_array[] = {2: { _DPRINTK_FLAGS_PRINT, 'p' },3: { _DPRINTK_FLAGS_INCL_MODNAME, 'm' },4: { _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' },5: { _DPRINTK_FLAGS_INCL_LINENO, 'l' },6: { _DPRINTK_FLAGS_INCL_TID, 't' },7: { _DPRINTK_FLAGS_NONE, '_' },8: };9:10: /* format a string into buf[] which describes the _ddebug's flags */11: static char *ddebug_describe_flags(struct _ddebug *dp, char *buf,