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的一些方法。

 

完。

posted @ 2015-07-05 15:36  摩斯电码  阅读(4059)  评论(0编辑  收藏  举报