pr_debug、dev_dbg等动态调试二
内核版本:Linux-3.14
作者:彭东林
下面我们简要分析
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_write
8: };
这里涉及到了顺序文件seq_file,关于这部分知识可以参考:
序列文件(seq_file)接口
这里我们需要看一下ddebug_proc_open:
1: /*
2: * File_ops->open method for <debugfs>/dynamic_debug/control. Does
3: * the seq_file setup dance, and also creates an iterator to walk the
4: * _ddebugs. Note that we create a seq_file always, even for O_WRONLY
5: * files where it's not needed, as doing so simplifies the ->release
6: * 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_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:
1: /*
2: * File_ops->write method for <debugfs>/dynamic_debug/conrol. Gathers the
3: * command text from userspace, parses and executes it.
4: */
5: #define USER_BUF_PAGE 4096
6: 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/control
2: # filename:lineno [module]function flags format
3: 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 every
3: * read() call from userspace. Takes the ddebug_lock and
4: * 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 object
3: * and return a pointer to that first object. Returns
4: * 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 _ddebug
3: * object from the one the iterator currently points at,
4: * and returns a pointer to the new _ddebug. Returns
5: * 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 the
4: * 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: else
17: 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 the
4: * current _ddebug as a single human-readable line, with a
5: * 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 10
2: # filename:lineno [module]function flags format
3: 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,
12: size_t maxlen)
13: {
14: char *p = buf;
15: int i;
16:
17: BUG_ON(maxlen < 6);
18: for (i = 0; i < ARRAY_SIZE(opt_array); ++i)
19: if (dp->flags & opt_array[i].flag)
20: *p++ = opt_array[i].opt_char;
21: if (p == buf)
22: *p++ = '_';
23: *p = '\0';
24:
25: return buf;
26: }
这个函数是将flags的值转换为字符串,存放到buf中,并将buf返回。
ddebug_proc_stop:
1: /*
2: * Seq_ops stop method. Called at the end of each read()
3: * call from userspace. Drops ddebug_lock.
4: */
5: static void ddebug_proc_stop(struct seq_file *m, void *p)
6: {
7: vpr_info("called m=%p p=%p\n", m, p);
8: mutex_unlock(&ddebug_lock);
9: }
在上一篇博客中pr_debug展开结果:
1: do {
2: static struct _ddebug __aligned(8) \
3: __attribute__((section("__verbose"))) descriptor = { \
4: .modname = KBUILD_MODNAME, \
5: .function = __func__, \
6: .filename = __FILE__, \
7: .format = (fmt), \
8: .lineno = __LINE__, \
9: .flags = _DPRINTK_FLAGS_DEFAULT, \
10: }
11: if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \
12: __dynamic_pr_debug(&descriptor, fmt, \
13: ##__VA_ARGS__); \
14: } while (0)
第11行就是判断descriptor.flags的值,看是否能够打印。
__dynamic_pr_debug:
1: int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
2: {
3: va_list args;
4: int res;
5: struct va_format vaf;
6: 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:
16: res = printk(KERN_DEBUG "%s%pV",
17: dynamic_emit_prefix(descriptor, buf), &vaf);
18:
19: va_end(args);
20:
21: return res;
22: }
dynamic_emit_prefix:
1: static char *dynamic_emit_prefix(const struct _ddebug *desc, char *buf)
2: {
3: int pos_after_tid;
4: int pos = 0;
5:
6: *buf = '\0';
7:
8: if (desc->flags & _DPRINTK_FLAGS_INCL_TID) {
9: if (in_interrupt())
10: pos += snprintf(buf + pos, remaining(pos), "<intr> ");
11: else
12: pos += snprintf(buf + pos, remaining(pos), "[%d] ",
13: task_pid_vnr(current));
14: }
15: pos_after_tid = pos;
16: if (desc->flags & _DPRINTK_FLAGS_INCL_MODNAME)
17: pos += snprintf(buf + pos, remaining(pos), "%s:",
18: desc->modname);
19: if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME)
20: pos += snprintf(buf + pos, remaining(pos), "%s:",
21: desc->function);
22: if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO)
23: pos += snprintf(buf + pos, remaining(pos), "%d:",
24: desc->lineno);
25: if (pos - pos_after_tid)
26: pos += snprintf(buf + pos, remaining(pos), " ");
27: if (pos >= PREFIX_SIZE)
28: buf[PREFIX_SIZE - 1] = '\0';
29:
30: return buf;
31: }
这个函数会根据flags(如fmtl)的值向buf中填充内容,然后将buf返回给printk。
先分析到这里,下一篇总结一些开启pr_debug的一些方法。
完。
本文来自博客园,作者:摩斯电码,未经同意,禁止转载