pr_debug、dev_dbg等动态调试二


内核版本: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_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的关系:

 

image

 

上面的函数返回的类型是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的一些方法。

 

完。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM