zoukankan      html  css  js  c++  java
  • 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
    ");
      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
    ", 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] = '';
      26:     vpr_info("read %d bytes from userspace
    ", (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 @ %i12"
       4: init/main.c:686 [main]do_one_initcall_debug =p "initcall %pF returned %d after %lld usecs12"
       5: arch/arm/kernel/unwind.c:162 [unwind]unwind_find_origin =_ "%s(%p, %p)12"
       6: arch/arm/kernel/unwind.c:173 [unwind]unwind_find_origin =_ "%s -> %p12"
       7: arch/arm/kernel/unwind.c:461 [unwind]unwind_table_add =_ "%s(%08lx, %08lx, %08lx, %08lx)12"
       8: arch/arm/kernel/unwind.c:117 [unwind]search_index =_ "%s(%08lx, %p, %p, %p)12"
       9: arch/arm/kernel/unwind.c:341 [unwind]unwind_frame =_ "%s(pc = %08lx lr = %08lx sp = %08lx)12"
      10: arch/arm/kernel/unwind.c:182 [unwind]unwind_find_idx =_ "%s(%08lx)12"
      11: ......

    上面每一行对应的就是一个pr_debug或者dev_dbg,以第5行为例

    arch/arm/kernel/unwind.c:162 [unwind]unwind_find_origin =_ "%s(%p, %p)12"

    表示在文件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)12"

    然后我们分析一下 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
    ", 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
    ",
      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
    ", m, p);
      14:  
      15:     if (p == SEQ_START_TOKEN) {
      16:         seq_puts(m,
      17:              "# filename:lineno [module]function flags format
    ");
      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, "	
    "");
      26:     seq_puts(m, ""
    ");
      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 @ %i12"
       4: init/main.c:686 [main]do_one_initcall_debug =p "initcall %pF returned %d after %lld usecs12"
       5: arch/arm/kernel/unwind.c:162 [unwind]unwind_find_origin =_ "%s(%p, %p)12"
       6: arch/arm/kernel/unwind.c:173 [unwind]unwind_find_origin =_ "%s -> %p12"

    上面的内容就是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 = '';
      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
    ", 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 = '';
       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] = '';
      29:  
      30:     return buf;
      31: }

    这个函数会根据flags(如fmtl)的值向buf中填充内容,然后将buf返回给printk。

     

    先分析到这里,下一篇总结一些开启pr_debug的一些方法。

     

    完。

  • 相关阅读:
    windows10下Mysql5.7安装指南
    SVN迁移Gitlab步骤
    java.lang.String中的replace方法到底替换了一个还是全部替换了。
    MongoDB升级导致启动失败
    阿里云CentOS7.3搭建多用户私有git服务器(从安装git开始)
    CentOS7.3编译安装Nginx设置开机启动
    关于使用Xshell远程连接启动tomcat导致图片不显示,报错Could not initialize class sun.awt.X11GraphicsEnvironment解决方案
    easyui报错“Cannot read poperty 'options' of undefined”问题解决方案之一
    Ubuntu18.04系统中vi键盘输入字符不匹配
    ux.form.field.TreePicker 扩展,修复火狐不能展开bug
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/4622238.html
Copyright © 2011-2022 走看看