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

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

完。

pr_debug、dev_dbg等动态调试二的更多相关文章

  1. pr_debug、dev_dbg等动态调试三

    内核版本:Linux-3.14 作者:彭东林 邮箱:pengdonglin137@163.com 如果没有使用CONFIG_DYNAMIC_DEBUG,那么就需要定义DEBUG,那么此时pr_debu ...

  2. pr_debug、dev_dbg等动态调试一

    内核版本:Linux-3.14 作者:彭东林 邮箱:pengdonglin137@163.com pr_debug: #if defined(CONFIG_DYNAMIC_DEBUG) /* dyna ...

  3. linux内核动态调试技术

    动态调试功能就是你可以决定在程序运行过程中是否要 pr_debug(), dev_dbg(), print_hex_dump_debug(), print_hex_dump_bytes() 这些函数正 ...

  4. Android动态方式破解apk前奏篇(Eclipse动态调试smail源码)

    一.前言 今天我们开始apk破解的另外一种方式:动态代码调试破解,之前其实已经在一篇文章中说到如何破解apk了: Android中使用静态方式破解Apk  主要采用的是静态方式,步骤也很简单,首先使用 ...

  5. [转]Android逆向之动态调试总结

    一.在SO中关键函数上下断点 刚学逆向调试时.大多都满足于在SO中某关键函数上下断点.然后通过操作应用程序,去触发这个断点,然后进行调试 详细的步骤可以参见非虫大大的<Android软件安全与逆 ...

  6. Android Studio动态调试smali代码

    工具: Android Studio版本: 3.0.1 smalidea插件: https://github.com/JesusFreke/smali/wiki/smalidea. 反编译工具:本节先 ...

  7. 【转】安卓逆向实践5——IDA动态调试so源码

    之前的安卓逆向都是在Java层上面的,但是当前大多数App,为了安全或者效率问题,会把一些重要功能放到native层,所以这里通过例子记录一下使用IDA对so文件进行调试的过程并对要点进行总结. 一. ...

  8. Anroid逆向学习从编写so到静动态调试分析arm的一次总结

    Anroid逆向学习从编写so到静动态调试分析arm的一次总结 一.前言 最近跟着教我兄弟学逆向这篇教程学习Android逆向,在第七课后作业反复折腾了好几天,正好在折腾的时候对前面的学习总结一波,动 ...

  9. Java 动态调试技术原理及实践

    本文转载自Java 动态调试技术原理及实践 导语 断点调试是我们最常使用的调试手段,它可以获取到方法执行过程中的变量信息,并可以观察到方法的执行路径.但断点调试会在断点位置停顿,使得整个应用停止响应. ...

随机推荐

  1. ssh 免交互登录 ,远程执行命令脚本。

    ##免交互SSH登录auto_login_ssh () {    expect -c "set timeout -1;                spawn -noecho ssh -o ...

  2. JavaWeb笔记(九)Ajax&Json

    AJAX 实现方式 原生的JS实现方式 //1.创建核心对象 var xmlhttp; if (window.XMLHttpRequest) {// code for IE7+, Firefox, C ...

  3. 软工实践Alpha冲刺(6/10)

    队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务 描述: 已经解决登录注册等基本功能的界面. 完成了主界面的基本布局 ...

  4. [洛谷P4725]【模板】多项式对数函数

    题目大意:给出$n-1$次多项式$A(x)$,求一个 $\bmod{x^n}$下的多项式$B(x)$,满足$B(x) \equiv \ln A(x)$.在$\bmod{998244353}$下进行.保 ...

  5. 使用python读取mysql数据库并进行数据的操作

    (一)环境的配置 使用python调用mysql数据库要引进一些库. 目前我使用的python版本是python3.6.引进库为pymysql 其他对应的库可以有以下选择: mysqldb,oursq ...

  6. code forces Codeforces Round #487 (Div. 2) C

    C. A Mist of Florescence time limit per test 1 second memory limit per test 256 megabytes input stan ...

  7. 编写Shell脚本的最佳实践,规范二

    需要养成的习惯如下: 代码有注释 #!/bin/bash # Written by steven # Name: mysqldump.sh # Version: v1.0 # Parameters : ...

  8. 51Nod 1561 另一种括号序列

    题目链接 分析: 卡常数~~~好气啊~~~这是看脸的时代啊~~~ $A$代表$($的数量,$B$代表$)$的数量... 如果$($的数量多于$)$的数量,那么最有方案显然是添加$A-B$个$)$... ...

  9. JSP中include指令和include动作浅析

    一. JSP工作原理 JSP文件是一种Servlet,其工作方式是先部署源代码后编译为.class文件.JSP会在客户端第一次请求JSP文件时被编译成Servlet,由Servlet处理客户端的请求. ...

  10. makefile函数集锦【转】

    转自:http://blog.csdn.net/turkeyzhou/article/details/8612841 Makefile  常用函数表一.字符串处理函数1.$(subst FROM,TO ...