调试Linux内核模块的时候,最简便的方法是通过内置的printk,对正在运行内核模块的运行状态进行跟踪观察分析,不用搭建复杂的调试环境。如果在内核模块中增加了大量的打印,那么会导致模块运行缓慢,同时大量的打印会导致刷屏严重,对分析关键信息有很强的干扰作用。

以往的做法是把printk的调用用自定义宏包裹起来,把把打印进行分类,一般分类为: ERROR,INFO,WARN,TRACE,VERBOSE这么几类,在一个模块中,被分为这么几类的的打印也都特别多,那么用预定义打印级别的宏编译好后,那么同类的打印都会出现,比如在函数A与函数B中都有TRACE类别的打印,实际上我本次调试,主要的目的是为了跟踪分析函数A的运行情况,但是放开了打印后,函数B的打印也会出现,造成了分析上的干扰, 这个时候,解决方法有两个,注释掉函数B的TRACE类别的打印,或者把TRACE类别的打印调为更低的VERBOSE级别

第一步需要在代码中使用pr_debug宏实现调试跟踪代码, 默认这些打印不会出现.需要打开他.

参考

Linux内核里的DebugFS

https://www.cnblogs.com/wwang/archive/2011/01/17/1937609.html

DebugFS,顾名思义,是一种用于内核调试的虚拟文件系统,内核开发者通过debugfs和用户空间交换数据。类似的虚拟文件系统还有procfs和sysfs等,这几种虚拟文件系统都并不实际存储在硬盘上,而是Linux内核运行起来后才建立起来。

通常情况下,最常用的内核调试手段是printk。但printk并不是所有情况都好用,比如打印的数据可能过多,我们真正关心的数据在大量的输出里不是那么一目了然;或者我们在调试时可能需要修改某些内核变量,这种情况下printk就无能为力,而如果为了修改某个值重新编译内核或者驱动又过于低效,此时就需要一个临时的文件系统可以把我们需要关心的数据映射到用户空间。在过去,procfs可以实现这个目的,到了2.6时代,新引入的sysfs也同样可以实现,但不论是procfs或是sysfs,用它们来实现某些debug的需求,似乎偏离了它们创建的本意。比如procfs,其目的是反映进程的状态信息;而sysfs主要用于Linux设备模型。不论是procfs或是sysfs的接口应该保持相对稳定,因为用户态程序很可能会依赖它们。当然,如果我们只是临时借用procfs或者sysfs来作debug之用,在代码发布之前将相关调试代码删除也无不可。但如果相关的调试借口要在相当长的一段时间内存在于内核之中,就不太适合放在procfs和sysfs里了。故此,debugfs应运而生。

默认情况下,debugfs会被挂载在目录/sys/kernel/debug之下,如果您的发行版里没有自动挂载,可以用如下命令手动完成:

`# mount -t debugfs none /your/debugfs/dir`

Linux内核为debugfs提供了非常简洁的API,本文接下来将以一个实作为例来介绍,sample code可以从这里下载。

这个实作会在debugfs中建立如下的目录结构:

img

其中,a对应模块中的一个u8类型的变量,b和subdir下面的c都是对应模块里的一个字符数组,只是它们的实现方式不同。

在module_init里,我们首先要建立根目录mydebug:

`my_debugfs_root = debugfs_create_dir(``"mydebug"``, NULL);`

第一个参数是目录的名称,第二个参数用来指定这个目录的上级目录,如果是NULL,则表示是放在debugfs的根目录里。

子目录也是用debugfs_create_dir来实现:

`sub_dir = debugfs_create_dir(``"subdir"``, my_debugfs_root);`

建立文件a的代码非常简单:

`debugfs_create_u8(``"a"``, 0644, my_debugfs_root, &a);`

这表示文件名为“a”,文件属性是0644,父目录是上面建立的“mydebug”,对应的变量是模块中的a。

Linux内核还提供了其他一些创建debugfs文件的API,请参考本文的附录。

b是一个32-bytes的字符数组,在debugfs里,数组可以用blob wrapper来实现。

`char` `hello[32] = ``"Hello world!\n"``;``struct` `debugfs_blob_wrapper b;` `b.data = (``void` `*)hello;``b.size = ``strlen``(hello) + 1;``debugfs_create_blob(``"b"``, 0644, my_debugfs_root, &b);`

这里需要注意的是,blob wrapper定义的数据只能是只读的。在本例中,虽然我们把文件b的权限设定为0644,但实际这个文件还是只读的,如果试图改写这个文件,系统将提示出错。

如果需要对内核数组进行写的动作,blob wrapper就无法满足要求,我们只能通过自己定义文件操作来实现。在这个实作里,可以参考文件c的实现。c和b在模块里对应着同一块字符数组,不同的是,b是只读的,而c通过自定义的文件操作同时实现了读和写。

`static` `int` `c_open(``struct` `inode *inode, ``struct` `file *filp)``{``    ``filp->private_data = inode->i_private;``    ``return` `0;``}` `static` `ssize_t c_read(``struct` `file *filp, ``char` `__user *buffer,``        ``size_t` `count, loff_t *ppos)``{``    ``if` `(*ppos >= 32)``        ``return` `0;``    ``if` `(*ppos + count > 32)``        ``count = 32 - *ppos;` `    ``if` `(copy_to_user(buffer, hello + *ppos, count))``        ``return` `-EFAULT;` `    ``*ppos += count;` `    ``return` `count;``}` `static` `ssize_t c_write(``struct` `file *filp, ``const` `char` `__user *buffer,``        ``size_t` `count, loff_t *ppos)``{``    ``if` `(*ppos >= 32)``        ``return` `0;``    ``if` `(*ppos + count > 32)``        ``count = 32 - *ppos;` `    ``if` `(copy_from_user(hello + *ppos, buffer, count))``        ``return` `-EFAULT;` `    ``*ppos += count;` `    ``return` `count;``}` `struct` `file_operations c_fops = {``    ``.owner = THIS_MODULE,``    ``.open = c_open,``    ``.read = c_read,``    ``.write = c_write,``};`  `debugfs_create_file(``"c"``, 0644, sub_dir, NULL, &c_fops);`

注:代码里,c_open其实并没有任何用处,因为c_read和c_write直接引用了全局变量hello。这里,我们也可以换一种写法,在read/write函数里用filp->private_data来引用字符数组hello。

到这里,三个文件和子目录已经创建完毕。在module_exit中,我们要记得释放创建的数据。

`debugfs_remove_recursive(my_debugfs_root);`

debugfs_remove_recursive可以帮我们逐步移除每个分配的dentry,如果您想一个一个手动的移除,也可以直接调用debugfs_remove。

附录:

创建和撤销目录及文件

`struct` `dentry *debugfs_create_dir(``const` `char` `*name, ``struct` `dentry *parent);``struct` `dentry *debugfs_create_file(``const` `char` `*name, mode_t mode, ``        ``struct` `dentry *parent, ``void` `*data, ``        ``const` `struct` `file_operations *fops);``void` `debugfs_remove(``struct` `dentry *dentry);``void` `debugfs_remove_recursive(``struct` `dentry *dentry);`

创建单值文件

`struct` `dentry *debugfs_create_u8(``const` `char` `*name, mode_t mode, ``        ``struct` `dentry *parent, u8 *value);``struct` `dentry *debugfs_create_u16(``const` `char` `*name, mode_t mode, ``        ``struct` `dentry *parent, u16 *value);``struct` `dentry *debugfs_create_u32(``const` `char` `*name, mode_t mode, ``        ``struct` `dentry *parent, u32 *value);``struct` `dentry *debugfs_create_u64(``const` `char` `*name, mode_t mode, ``        ``struct` `dentry *parent, u64 *value);` `struct` `dentry *debugfs_create_x8(``const` `char` `*name, mode_t mode, ``        ``struct` `dentry *parent, u8 *value);``struct` `dentry *debugfs_create_x16(``const` `char` `*name, mode_t mode, ``        ``struct` `dentry *parent, u16 *value);``struct` `dentry *debugfs_create_x32(``const` `char` `*name, mode_t mode, ``        ``struct` `dentry *parent, u32 *value);` `struct` `dentry *debugfs_create_size_t(``const` `char` `*name, mode_t mode, ``        ``struct` `dentry *parent, ``size_t` `*value);``struct` `dentry *debugfs_create_bool(``const` `char` `*name, mode_t mode, ``        ``struct` `dentry *parent, u32 *value);`

其中,后缀为x8、x16、x32的这三个函数是指debugfs中的数据用十六进制表示。

创建BLOB文件

`struct` `debugfs_blob_wrapper {``    ``void` `*data;``    ``unsigned ``long` `size;``};` `struct` `dentry *debugfs_create_blob(``const` `char` `*name, mode_t mode, ``         ``struct` `dentry *parent, ``struct` `debugfs_blob_wrapper *blob);`

其它

`struct` `dentry *debugfs_rename(``struct` `dentry *old_dir, ``struct` `dentry *old_dentry, ``        ``struct` `dentry *new_dir, ``const` `char` `*new_name);` `struct` `dentry *debugfs_create_symlink(``const` `char` `*name, ``        ``struct` `dentry *parent, ``const` `char` `*target);`

Documentation/dynamic-debug-howto.txt

https://lwn.net/Articles/434856/

ntroduction
============

This document describes how to use the dynamic debug (ddebug) feature.

Dynamic debug is designed to allow you to dynamically enable/disable kernel
code to obtain additional kernel information. Currently, if
CONFIG_DYNAMIC_DEBUG is set, then all pr_debug()/dev_debug() calls can be
dynamically enabled per-callsite.

Dynamic debug has even more useful features:

 * Simple query language allows turning on and off debugging statements by
   matching any combination of:

   - source filename
   - function name
   - line number (including ranges of line numbers)
   - module name
   - format string

 * Provides a debugfs control file: <debugfs>/dynamic_debug/control which can be
   read to display the complete list of known debug statements, to help guide you

Controlling dynamic debug Behaviour
===================================

The behaviour of pr_debug()/dev_debug()s are controlled via writing to a
control file in the 'debugfs' filesystem. Thus, you must first mount the debugfs
filesystem, in order to make use of this feature. Subsequently, we refer to the
control file as: <debugfs>/dynamic_debug/control. For example, if you want to
enable printing from source file 'svcsock.c', line 1603 you simply do:

nullarbor:~ # echo 'file svcsock.c line 1603 +p' >
              <debugfs>/dynamic_debug/control

If you make a mistake with the syntax, the write will fail thus:

nullarbor:~ # echo 'file svcsock.c wtf 1 +p' >
              <debugfs>/dynamic_debug/control
-bash: echo: write error: Invalid argument

Viewing Dynamic Debug Behaviour
===========================

You can view the currently configured behaviour of all the debug statements
via:

nullarbor:~ # cat <debugfs>/dynamic_debug/control
# filename:lineno [module]function flags format
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:323
[svcxprt_rdma]svc_rdma_cleanup - "SVCRDMA Module Removed, deregister RPC RDMA transport\012"
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:341
[svcxprt_rdma]svc_rdma_init - "\011max_inline       : %d\012"
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:340
[svcxprt_rdma]svc_rdma_init - "\011sq_depth         : %d\012"
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:338
[svcxprt_rdma]svc_rdma_init - "\011max_requests     : %d\012"
...


You can also apply standard Unix text manipulation filters to this
data, e.g.

nullarbor:~ # grep -i rdma <debugfs>/dynamic_debug/control  | wc -l
62

nullarbor:~ # grep -i tcp <debugfs>/dynamic_debug/control | wc -l
42

Note in particular that the third column shows the enabled behaviour
flags for each debug statement callsite (see below for definitions of the
flags).  The default value, no extra behaviour enabled, is "-".  So
you can view all the debug statement callsites with any non-default flags:

nullarbor:~ # awk '$3 != "-"' <debugfs>/dynamic_debug/control
# filename:lineno [module]function flags format
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c:1603 [sunrpc]svc_send p
"svc_process: st_sendto returned %d\012"


Command Language Reference
==========================

At the lexical level, a command comprises a sequence of words separated
by whitespace characters.  Note that newlines are treated as word
separators and do *not* end a command or allow multiple commands to
be done together.  So these are all equivalent:

nullarbor:~ # echo -c 'file svcsock.c line 1603 +p' >
              <debugfs>/dynamic_debug/control
nullarbor:~ # echo -c '  file   svcsock.c     line  1603 +p  ' >
              <debugfs>/dynamic_debug/control
nullarbor:~ # echo -c 'file svcsock.c\nline 1603 +p' >
              <debugfs>/dynamic_debug/control
nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' >
              <debugfs>/dynamic_debug/control

Commands are bounded by a write() system call.  If you want to do
multiple commands you need to do a separate "echo" for each, like:

nullarbor:~ # echo 'file svcsock.c line 1603 +p' > /proc/dprintk ;\
> echo 'file svcsock.c line 1563 +p' > /proc/dprintk

or even like:

nullarbor:~ # (
> echo 'file svcsock.c line 1603 +p' ;\
> echo 'file svcsock.c line 1563 +p' ;\
> ) > /proc/dprintk

At the syntactical level, a command comprises a sequence of match
specifications, followed by a flags change specification.

command ::= match-spec* flags-spec

The match-spec's are used to choose a subset of the known dprintk()
callsites to which to apply the flags-spec.  Think of them as a query
with implicit ANDs between each pair.  Note that an empty list of
match-specs is possible, but is not very useful because it will not
match any debug statement callsites.

A match specification comprises a keyword, which controls the attribute
of the callsite to be compared, and a value to compare against.  Possible
keywords are:

match-spec ::= 'func' string |
         'file' string |
         'module' string |
         'format' string |
         'line' line-range

line-range ::= lineno |
         '-'lineno |
         lineno'-' |
         lineno'-'lineno
// Note: line-range cannot contain space, e.g.
// "1-30" is valid range but "1 - 30" is not.

lineno ::= unsigned-int

The meanings of each keyword are:

func
    The given string is compared against the function name
    of each callsite.  Example:

    func svc_tcp_accept

file
    The given string is compared against either the full
    pathname or the basename of the source file of each
    callsite.  Examples:

    file svcsock.c
    file /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c

module
    The given string is compared against the module name
    of each callsite.  The module name is the string as
    seen in "lsmod", i.e. without the directory or the .ko
    suffix and with '-' changed to '_'.  Examples:

    module sunrpc
    module nfsd

format
    The given string is searched for in the dynamic debug format
    string.  Note that the string does not need to match the
    entire format, only some part.  Whitespace and other
    special characters can be escaped using C octal character
    escape \ooo notation, e.g. the space character is \040.
    Alternatively, the string can be enclosed in double quote
    characters (") or single quote characters (').
    Examples:

    format svcrdma:       // many of the NFS/RDMA server dprintks
    format readahead      // some dprintks in the readahead cache
    format nfsd:\040SETATTR // one way to match a format with whitespace
    format "nfsd: SETATTR"  // a neater way to match a format with whitespace
    format 'nfsd: SETATTR'  // yet another way to match a format with whitespace

line
    The given line number or range of line numbers is compared
    against the line number of each dprintk() callsite.  A single
    line number matches the callsite line number exactly.  A
    range of line numbers matches any callsite between the first
    and last line number inclusive.  An empty first number means
    the first line in the file, an empty line number means the
    last number in the file.  Examples:

    line 1603     // exactly line 1603
    line 1600-1605  // the six lines from line 1600 to line 1605
    line -1605        // the 1605 lines from line 1 to line 1605
    line 1600-        // all lines from line 1600 to the end of the file

The flags specification comprises a change operation followed
by one or more flag characters.  The change operation is one
of the characters:

-
    remove the given flags

+
    add the given flags

=
    set the flags to the given flags

The flags are:

f
    Include the function name in the printed message
l
    Include line number in the printed message
m
    Include module name in the printed message
p
    Causes a printk() message to be emitted to dmesg
t
    Include thread ID in messages not generated from interrupt context

Note the regexp ^[-+=][flmpt]+$ matches a flags specification.
Note also that there is no convenient syntax to remove all
the flags at once, you need to use "-flmpt".


Debug messages during boot process
==================================

To be able to activate debug messages during the boot process,
even before userspace and debugfs exists, use the boot parameter:
ddebug_query="QUERY"

QUERY follows the syntax described above, but must not exceed 1023
characters. The enablement of debug messages is done as an arch_initcall.
Thus you can enable debug messages in all code processed after this
arch_initcall via this boot parameter.
On an x86 system for example ACPI enablement is a subsys_initcall and
ddebug_query="file ec.c +p"
will show early Embedded Controller transactions during ACPI setup if
your machine (typically a laptop) has an Embedded Controller.
PCI (or other devices) initialization also is a hot candidate for using
this boot parameter for debugging purposes.


Examples
========

// enable the message at line 1603 of file svcsock.c
nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' >
              <debugfs>/dynamic_debug/control

// enable all the messages in file svcsock.c
nullarbor:~ # echo -n 'file svcsock.c +p' >
              <debugfs>/dynamic_debug/control

// enable all the messages in the NFS server module
nullarbor:~ # echo -n 'module nfsd +p' >
              <debugfs>/dynamic_debug/control

// enable all 12 messages in the function svc_process()
nullarbor:~ # echo -n 'func svc_process +p' >
              <debugfs>/dynamic_debug/control

// disable all 12 messages in the function svc_process()
nullarbor:~ # echo -n 'func svc_process -p' >
              <debugfs>/dynamic_debug/control

// enable messages for NFS calls READ, READLINK, READDIR and READDIR+.
nullarbor:~ # echo -n 'format "nfsd: READ" +p' >
              <debugfs>/dynamic_debug/control

如何打开pr_debug调试信息

https://blog.csdn.net/helloanthea/article/details/25330809

如何打开pr_debug调试信息,先不要着急,我们先静下心来分析一下这个函数的源代码。。。

以DMA的调试为例,先来看看一个pr_debug函数调用

   pr_debug("%s: %s (%s)\n",
             __func__,
             chan ? "success" : "fail",
             chan ? dma_chan_name(chan) : NULL);

在include/linux/printk.h里找到pr_debug的定义,这里暗藏了玄机。 /* If you are writing a driver, please use dev_dbg instead / #if defined(CONFIG_DYNAMIC_DEBUG) / dynamic_pr_debug() uses pr_fmt() internally so we don’t need it here */ #define pr_debug(fmt, …)
dynamic_pr_debug(fmt, ##VA_ARGS) #elif defined(DEBUG) #define pr_debug(fmt, …)
printk(KERN_DEBUG pr_fmt(fmt), ##VA_ARGS) #else #define pr_debug(fmt, …)
no_printk(KERN_DEBUG pr_fmt(fmt), ##VA_ARGS) #endif

原来,三个宏作为判断条件决定了pr_debug到底采用哪种用法: 第一种用法,如果定义了CONFIG_DYNAMIC_DEBUG,就使用动态debug机制dynamic_pr_debug(); 第二种用法,如果定义了DEBUG,就使用printk(KERN_DEBUG…) 第三种用法,默认情况下,不打印。

第三种不打印肯定不是我们想要的,那么要想让kernel乖乖的打印调试信息,就只有前面两条路可选了:要么动态debug,要么定义DEBUG宏。

先说一下如何定义DEBUG宏: 其实在kernel中很多driver已经定义好了这样的DEBUG选项,前人栽树,后人乘凉,我们可以先看看有没有现成的 例如,我们经常可以看到这样的配置选项和宏定义: (1)DMA Engine debugging(CONFIG_DMADEVICES_DEBUG ) (2)Power Management Debug Support(CONFIG_PM_DEBUG) (3) Enable debug for the B2C2 FlexCop drivers(CONFIG_PCI_DEBUG)

以DMA为例,在drivers/dma/Makefile中定义了编译选项 ccflags-$(CONFIG_DMADEVICES_DEBUG) := -DDEBUG 其作用相当于在drivers/dma/所有子文件定义了宏#define DEBUG

小伙伴们赶紧把CONFIG_DEBUG选项选上吧,然后重新编译kernel。先别急,这样还不够, 默认的console级别是7(在kernel/printk/printk.c中定义了#define DEFAULT_CONSOLE_LOGLEVEL 7) 只有那些级别“小于7”的调试信息才能打印出来,而printk(KERN_DEBUG…)的级别是7,那就还需要提高console打印级别 如果要查看dma初始化的debug信息,那就直接改代码 #define DEFAULT_CONSOLE_LOGLEVEL 8 如果是runtime,可以直接通过printk的sys接口调整打印级别 $cat /proc/sys/kernel/printk 7 4 1 7 $echo 8 > /proc/sys/kernel/printk $cat /proc/sys/kernel/printk 8 4 1 7

ok,大功告成!

如果一些driver没有现成的宏可用,那么你可以在你想debug的源文件中直接定义DEBUG宏,例如你想查看driver/video/fsl-diu-fb.c的调试信息,直接在头文件引用后添加宏定义

#define DEBUG(宏的作用范围相信我就不用多说了吧,就是从宏定义开始到源文件的末尾结束),就能如愿以偿了。

开发人员也可以自己仿照上述方法进行定义。

下面再简单说一下kernel的动态调试 打开Enable dynamic printk() support(DYNAMIC_DEBUG),那么所有的 pr_debug()/dev_debug() 之类的函数在runtime就可以动态地使用了。 kernel动态调试提供一个debugfs接口: /dynamic_debug/control 这个文件可以用来获取已完成的调试信息列表 例如你要显示文件’svcsock.c’的1603行内容,你可以这样做:

nullarbor:~ # echo ‘file svcsock.c line 1603 +p’ > /dynamic_debug/control

// 提供文件svcsock.c所有信息 nullarbor:~ # echo -n ‘file svcsock.c +p’ > /dynamic_debug/control

如果你想执行多个命令,你需要为每个加入“echo”分割,像这样:

nullarbor:~ # echo ‘file svcsock.c line 1603 +p’ > /proc/dprintk ;
> echo ‘file svcsock.c line 1563 +p’ > /proc/dprintk

或者甚至是这样:

nullarbor:~ # ( > echo ‘file svcsock.c line 1603 +p’ ;
> echo ‘file svcsock.c line 1563 +p’ ;
> ) > /proc/dprintk

file可以替换成module,format等匹配方式,具体用法请参考Documentation/dynamic-debug-howto.txt 好了,enjoy你的debug之旅吧!