内核版本:Linux-3.14

作者:彭东林

邮箱:pengdonglin137@163.com

 

下面我们简要分析

 > /sys/kernel/debug/dynamic_debug/control

的实现。

 

首先看一下dynamic_dedbg/control是如何生成的?

代码位置 lib/dynamic_debug.c

void)
   2: {
struct dentry *dir, *file;
   4:  
if (!ddebug_init_success)
return -ENODEV;
   7:  
, NULL);
if (!dir)
return -ENOMEM;
, 0644, dir, NULL,
  12:                     &ddebug_proc_fops);
if (!file) {
  14:         debugfs_remove(dir);
return -ENOMEM;
  16:     }
return 0;
  18: }
  19:  
  20: fs_initcall(dynamic_debug_init_debugfs);

这个函数会在kernel启动的时候执行,在/sys/kernel/debug下创建目录dynamic_debug,然后在dynamic_debug下面创建control节点,并将这个节点的操作函数集设置为ddebug_proc_fops。

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:

/*
 * File_ops->open method for <debugfs>/dynamic_debug/control.  Does
 * the seq_file setup dance, and also creates an iterator to walk the
 * _ddebugs.  Note that we create a seq_file always, even for O_WRONLY
 * files where it's not needed, as doing so simplifies the ->release
 * method.
 */
struct file *file)
   9: {
struct ddebug_iter *iter;
int err;
  12:  
);
  14:  
sizeof(*iter), GFP_KERNEL);
if (iter == NULL)
return -ENOMEM;
  18:  
  19:     err = seq_open(file, &ddebug_proc_seqops);
if (err) {
  21:         kfree(iter);
return err;
  23:     }
private = iter;
return 0;
  26: }

其中:

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:

/*
 * File_ops->write method for <debugfs>/dynamic_debug/conrol.  Gathers the
 * command text from userspace, parses and executes it.
 */
#define USER_BUF_PAGE 4096
char __user *ubuf,
   7:                   size_t len, loff_t *offp)
   8: {
char *tmpbuf;
int ret;
  11:  
if (len == 0)
return 0;
if (len > USER_BUF_PAGE - 1) {
, USER_BUF_PAGE);
return -E2BIG;
  17:     }
  18:     tmpbuf = kmalloc(len + 1, GFP_KERNEL);
if (!tmpbuf)
return -ENOMEM;
if (copy_from_user(tmpbuf, ubuf, len)) {
  22:         kfree(tmpbuf);
return -EFAULT;
  24:     }
'\0';
int)len);
  27:  
  28:     ret = ddebug_exec_queries(tmpbuf, NULL);
  29:     kfree(tmpbuf);
if (ret < 0)
return ret;
  32:  
  33:     *offp += len;
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
  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:

/*
 * Seq_ops start method.  Called at the start of every
 * read() call from userspace.  Takes the ddebug_lock and
 * seeks the seq_file's iterator to the given position.
 */
struct seq_file *m, loff_t *pos)
   7: {
private;
struct _ddebug *dp;
int n = *pos;
  11:  
long)*pos);
  13:  
  14:     mutex_lock(&ddebug_lock);
  15:  
if (!n)
return SEQ_START_TOKEN;
if (n < 0)
return NULL;
  20:     dp = ddebug_iter_first(iter);
while (dp != NULL && --n > 0)
  22:         dp = ddebug_iter_next(iter);
return dp;
  24: }

第8行中的iter指向的内存是在ddebug_proc_open中调用kzalloc分配的。

第20行函数的目的是获取ddebug_tables中的第一项

 

ddebug_iter_first:

/*
 * Set the iterator to point to the first _ddebug object
 * and return a pointer to that first object.  Returns
 * NULL if there are no _ddebugs at all.
 */
struct ddebug_iter *iter)
   7: {
if (list_empty(&ddebug_tables)) {
   9:         iter->table = NULL;
  10:         iter->idx = 0;
return NULL;
  12:     }
  13:     iter->table = list_entry(ddebug_tables.next,
struct ddebug_table, link);
  15:     iter->idx = 0;
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:

/*
 * Advance the iterator to point to the next _ddebug
 * object from the one the iterator currently points at,
 * and returns a pointer to the new _ddebug.  Returns
 * NULL if the iterator has seen all the _ddebugs.
 */
struct ddebug_iter *iter)
   8: {
if (iter->table == NULL)
return NULL;
if (++iter->idx == iter->table->num_ddebugs) {
/* iterate to next table */
  13:         iter->idx = 0;
if (list_is_last(&iter->table->link, &ddebug_tables)) {
  15:             iter->table = NULL;
return NULL;
  17:         }
  18:         iter->table = list_entry(iter->table->link.next,
struct ddebug_table, link);
  20:     }
return &iter->table->ddebugs[iter->idx];
  22: }

 

ddebug_proc_next:

/*
 * Seq_ops next method.  Called several times within a read()
 * call from userspace, with ddebug_lock held.  Walks to the
 * next _ddebug object with a special case for the header line.
 */
void *p, loff_t *pos)
   7: {
private;
struct _ddebug *dp;
  10:  
,
long)*pos);
  13:  
if (p == SEQ_START_TOKEN)
  15:         dp = ddebug_iter_first(iter);
else
  17:         dp = ddebug_iter_next(iter);
  18:     ++*pos;
return dp;
  20: }

 

下面是我画的一张图,大概表示出了ddebug_tables/ddebug_tables/_ddebug的关系:

 

pr_debug、dev_dbg等动态调试二

 

上面的函数返回的类型是struct _ddebug,用于遍历所有的_ddebug.

 

ddebug_proc_show:

/*
 * Seq_ops show method.  Called several times within a read()
 * call from userspace, with ddebug_lock held.  Formats the
 * current _ddebug as a single human-readable line, with a
 * special case for the header line.
 */
void *p)
   8: {
private;
struct _ddebug *dp = p;
char flagsbuf[10];
  12:  
, m, p);
  14:  
if (p == SEQ_START_TOKEN) {
  16:         seq_puts(m,
);
return 0;
  19:     }
  20:  
,
  22:            trim_prefix(dp->filename), dp->lineno,
  23:            iter->table->mod_name, dp->function,
sizeof(flagsbuf)));
);
);
  27:  
return 0;
  29: }

我们最终看到的结果:

   1: [root@TQ2440 /]# cat /sys/kernel/debug/dynamic_debug/control | head -n 10
   2: # filename:lineno [module]function flags format

上面的内容就是ddebug_proc_show打印出来的。

trim_prefix:

/* Return the path relative to source root */
char *path)
   3: {
);
   5:  
if (strncmp(path, __FILE__, skip))
/* prefix mismatch, don't skip */
   8:  
return path + skip;
  10: }

这个函数的目的是将文件名的绝对路径转换为相对路径,相对于kernel源码的根目录。

 

ddebug_describe_flags:

char opt_char; } opt_array[] = {
'p' },
'm' },
'f' },
'l' },
't' },
'_' },
   8: };
   9:  
/* format a string into buf[] which describes the _ddebug's flags */
char *buf,
  12:                     size_t maxlen)
  13: {
char *p = buf;
int i;
  16:  
  17:     BUG_ON(maxlen < 6);
for (i = 0; i < ARRAY_SIZE(opt_array); ++i)
if (dp->flags & opt_array[i].flag)
  20:             *p++ = opt_array[i].opt_char;
if (p == buf)
'_';
'\0';
  24:  
return buf;
  26: }

这个函数是将flags的值转换为字符串,存放到buf中,并将buf返回。

 

ddebug_proc_stop:

/*
 * Seq_ops stop method.  Called at the end of each read()
 * call from userspace.  Drops ddebug_lock.
 */
void *p)
   6: {
, m, p);
   8:     mutex_unlock(&ddebug_lock);
   9: }

 

上一篇博客中pr_debug展开结果:

do {
struct _ddebug  __aligned(8)            \
))) descriptor = {        \
   4:         .modname = KBUILD_MODNAME,            \
   5:         .function = __func__,                \
   6:         .filename = __FILE__,                \
   7:         .format = (fmt),                \
   8:         .lineno = __LINE__,                \
   9:         .flags =  _DPRINTK_FLAGS_DEFAULT,        \
  10:     }
if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT))    \
  12:         __dynamic_pr_debug(&descriptor, fmt,    \
  13:                    ##__VA_ARGS__);        \
while (0)

第11行就是判断descriptor.flags的值,看是否能够打印。

 

__dynamic_pr_debug:

char *fmt, ...)
   2: {
   3:     va_list args;
int res;
struct va_format vaf;
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:  
,
  17:              dynamic_emit_prefix(descriptor, buf), &vaf);
  18:  
  19:     va_end(args);
  20:  
return res;
  22: }

 

dynamic_emit_prefix:

char *buf)
   2: {
int pos_after_tid;
int pos = 0;
   5:  
'\0';
   7:  
if (desc->flags & _DPRINTK_FLAGS_INCL_TID) {
if (in_interrupt())
);
else
,
  13:                     task_pid_vnr(current));
  14:     }
  15:     pos_after_tid = pos;
if (desc->flags & _DPRINTK_FLAGS_INCL_MODNAME)
,
  18:                 desc->modname);
if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME)
,
  21:                 desc->function);
if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO)
,
  24:                 desc->lineno);
if (pos - pos_after_tid)
);
if (pos >= PREFIX_SIZE)
'\0';
  29:  
return buf;
  31: }

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

 

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

 

完。

相关文章:

  • 2021-08-29
  • 2021-10-02
  • 2021-08-17
  • 2021-05-22
  • 2022-03-06
  • 2021-11-05
  • 2022-12-23
猜你喜欢
  • 2021-07-02
  • 2021-12-02
  • 2021-09-18
  • 2022-12-23
  • 2021-12-23
  • 2021-07-16
  • 2022-12-23
相关资源
相似解决方案