linux中打开内核的动态调试

1. 简介

在调试linux的驱动的时候,会遇到 dev_dbg (“xxx”); ,如何让他显示出来,是本片文章的重点目的。

 

2. 分析

dev_dbg 的源码

#if defined(CONFIG_DYNAMIC_DEBUG)
#define dev_dbg(dev, format, ...)		     \
do {						     \
	dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \
} while (0)
#elif defined(DEBUG)
#define dev_dbg(dev, format, arg...)		\
	dev_printk(KERN_DEBUG, dev, format, ##arg)
#else
#define dev_dbg(dev, format, arg...)				\
({								\
	if (0)							\
		dev_printk(KERN_DEBUG, dev, format, ##arg);	\
})
#endif

看来想要显示,需要配置上 CONFIG_DYNAMIC_DEBUG 这个宏,打开配置的图形界面,make menuconfig,在其中搜索 CONFIG_DYNAMIC_DEBUG,结果如下
        Location:
        -> Kernel hacking
        (1)   -> printk and dmesg options
按照提示,配置上,重新编译内核,并烧写
> Kernel hacking > printk and dmesg options
[*] Enable dynamic printk() support
内核配置中选中 serial 模块
> Device Drivers > USB support > USB Gadget Support 下的
<M>     Serial Gadget (with CDC ACM and CDC OBEX support)
编译内核
make -j8

 

3. 使用

当重新烧写了内核后,先用 mount 命令查看有没有挂载 debugfs,如果没有,按如下方式挂载
mount -t debugfs none /sys/kernel/debug

查看系统默认打印级别
源码中的值小于下边配置的阈值时会打印,打印级别从 0~7
cat /proc/sys/kernel/printk
7       4       1       7
4个数字分别对应 
[控制台日志级别  默认的消息日志级别  最低的控制台日志级别  默认的控制台日志级别]

我们关注的时第一个,当源文件中打印级别数字小于 控制台日志级别 时,才会输出到dmesg中。

看来就是最高,不需要改动


需要查看以下文件进行详细了解

kernel/Documentation/dynamic-debug-howto.txt

我们打开想要查看的文件中的相关输出
echo -n 'file xxx.c [line xxx] +p' > /sys/kernel/debug/dynamic_debug/control
echo -n 'file drivers/usb/gadget/function/f_serial.c +flp' > /sys/kernel/debug/dynamic_debug/control
echo -n 'file drivers/usb/gadget/function/f_acm.c +flp' > /sys/kernel/debug/dynamic_debug/control

可以先查看能显示的输出
cat /sys/kernel/debug/dynamic_debug/control

当驱动加载,或者有打印时,dmesg就能显示了。

 

4. dynamic-debug-howto

翻译 kernel/Documentation/dynamic-debug-howto.txt 文件。

介绍

===

这个文档描述如何使用动态调试(ddebug)特性。

 

动态调试设计的目的是允许你动态的打开或关闭内核代码的额外信息。 现在, 假如
CONFIG_DYNAMIC_DEBUG 已经设置好了, 那么所有 pr_debug()/dev_debug() 之类的函数调用都可以动态的在代码里使能。

 

动态调试有很多有用的特性:

* 简单的查询语句允许通过下边和配合0和1开启或者关闭调试状态:

        - 源文件名

        - 函数名

        - 行号(可以是一个范围)

        - module 名

        - 格式化字符串

 

* 提供一个 debugfs 控制文件: <debugfs>/dynamic_debug/control,可以读这个文件显示能打开的调试列表,来帮助指导你。<debugfs> 一般是 /sys/kernel/debug

 

控制动态调试的行为

===============

pr_debug()/dev_dbg() 的行为是通过控制'debugfs'文件系统上写一个控制文件。因此,你需要先挂载 debugfs 文件系统(mount -t debugfs none /sys/kernel/debug)。然后,我们看控制文件: <debugfs>/dynamic_debug/control。比如,如果你想打开 'svcsock.c'的1603行 的打印信息:

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

如果语法错误,写会失败,如下:

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

-bash: echo: write error: Invalid argument

 

查看动态调试的行为

===============

你可以查看当前可以配置的输出:

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"

...

 

你也可以应用标准的Unix文本过滤命令来过滤这些数据, 例如。

 

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

 

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

 

在第三列显示了调试状态下的目前使能的标志(下边有标志位的定义)。如果想使用默认值,不想使能任何的标志,使用"-"。你可以查看任何不是默认标志的状态位通过下面的命令:

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"

 

命令语言参考

==========

 

在词汇层面上,一个命令由一系列由空格分割的单词组成。所以这些是等价的:

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 -n 'file svcsock.c line 1603 +p' >   <debugfs>/dynamic_debug/control

 

命令是通过write()提交的。多个命令可以写在一起,需要使用';'或'\n'隔开。

 

~# echo "func pnpacpi_get_resources +p; func pnp_assign_mem +p" \

                                     > <debugfs>/dynamic_debug/control

如果你的命令太多,也可以把这些命令放在文件中

 

~# cat query-batch-file > <debugfs>/dynamic_debug/control

 

在语法层面上,一个命令有一系列的规格匹配组成,随后由一个标记来改变这风格。

 

command ::= match-spec* flags-spec

 

match-spec被用来选择一个 pr_debug() 的子集来套用flags-spec。把他们当做彼此之间的每对做隐式 与 查询。注意一个空的match_specs列表是有可能的,但不是非常有用,因为它不会匹配任何调用点的调试子句。

 

一个匹配规范由一个关键字组成,关键字控制被比较的调用点的属性和要比较的值。可能关键字是:

 

match-spec ::= 'func' string |

                        'file' string |

                        'module' string |

                       'format' string |

                       'line' line-range

 

line-range ::= lineno |

                    '-'lineno |

                    lineno'-' |

                    lineno'-'lineno

// 注意: line-range 不能包含空格, 例如

// "1-30" 是有效的范围,但是"1 - 30"无效。

 

lineno ::= unsigned-int

 

每个关键字的含义:

func

        给定的字符串和每个调用点的函数名进行比较。例如:

        func svc_tcp_accept

 

file

        给定的字符串跟每个调用点的源文件的全路径名或相对路径名比较。例如:

 

        file svcsock.c

        file kernel/freezer.c

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

 

module

        给定的字符串跟每个调用点的模块名进行比较。模块名是“lsmod”里显示的字符串,举例来说,没有路径,没有.ko后缀,名字中的'-'改为'_'。

 

        module sunrpc

        module nfsd

format

给定的字符串在动态调试字符串中查找。注意字符串不必完全匹配format,只需要一部分。空格和其他的特殊字符用c的八进制语法转义,例如空字符是 \040。作为选择,这个字符串可以用双引号或单引号包含起来。

例子:

 

        format svcrdma: // 大多数 NFS/RDMA 服务器的 dprintks

        format readahead // 一些在预加载缓存里的 dprintks

        format nfsd:\040SETATTR // 一个使用空格来匹配格式的方式

        format "nfsd: SETATTR" // 一个整洁的方法来用空格匹配格式

        format 'nfsd: SETATTR' // 和上边相同

 

line

给定的行号,或者行的范围和每个dprintk()调用点的行号进行比较。单独的行号会精确的对比,范围的会包含范围的边界,一个空的开头,表示从第一行开始。一个空在范围的末尾,表示到结尾。例如:

        line 1603 // 1603 行

        line 1600-1605 // 1600 到 1605 行

        line -1605 // 1 到 1605 行

        line 1600- // 1600 到 文件的结尾

 

指定的标志通过以下的一个或多个标志进行改变操作,变化操作如下:

-         移除给定的标记

+         加入给定的标记

=         设置标记到给定的标记上

标记如下:

f         在打印信息中包含函数名

l         在打印信息中包含行号

m         ...模块名

p         产生一个printk()消息到显示系统启动日志

t         ...包含不是从中断上下文差生的线程ID

 

注意正则表达式 ^[-+=][flmpt]+$ 匹配一个标记规范。

同样也要注意,没有便捷的语法来一次性移除所有的标记,你需要使用"-flmpt"。

 

引导进程中的调试信息

=================

在用户空间和debugfs存在前,使能引导进程中的调试信息,使用boot参数:ddebug_query="QUERY"

QUERY遵循上述语法,但是一定不能超过1023字节。调试信息的使能是arch_initcall做的。这样你可以使能arch_initcall之后的所有消息经过这个boot的参数。

在一个x86系统中,例如ACPI启用是一个subsys_initcall并且如果你的机器(通常是一台笔记本)有一个嵌入式的控制器,ddebug_query=“fileec.c+p”将会在ACPI装备期间显示早期的嵌入式控制器事务。PCI(或其他设备)的初始化也是使用这个引导参数来调试的热门候选。

 

实例

===

 

// 使能文件 svcsock.c 1603 行信息

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

 

// 使能文件 svcsock.c 所有信息

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

 

// 使能 NFS服务模块 所有的信息

nullarbor:~ # echo -n 'module nfsd +p' >  <debugfs>/dynamic_debug/control

 

// 使能函数 svc_process() 中的所有12条信息

nullarbor:~ # echo -n 'func svc_process +p' >  <debugfs>/dynamic_debug/control

 

// 不再显示 函数svc_process()的所有12条信息

nullarbor:~ # echo -n 'func svc_process -p' >  <debugfs>/dynamic_debug/control

 

// 使能 NFS 调用的所有以READ开始的信息.

nullarbor:~ # echo -n 'format "nfsd: READ" +p' >  <debugfs>/dynamic_debug/control

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
打开Linux内核某个驱动文件的调试信息,你可以使用printk函数在内核日志输出信息。以下是一些常用的方法: 1. 在驱动程序添加printk语句:在驱动程序代码添加printk语句,以输出调试信息。例如,你可以在某个函数添加以下语句: ``` printk(KERN_DEBUG "mydriver: myfunction called\n"); ``` 这将输出一个调试信息,包括驱动程序名称和函数名称。 2. 使用dmesg命令查看内核日志:打开终端窗口,并输入以下命令: ``` $ dmesg -wH ``` 这个命令将打开内核日志,并将其输出到终端窗口。接着,你可以在终端窗口执行驱动程序,以捕获输出的调试信息。 3. 使用syslog工具:你可以使用syslog工具来捕获内核日志,并将其保存在一个文件。要使用syslog工具,请执行以下步骤: - 安装syslog工具:在终端窗口输入以下命令: ``` $ sudo apt-get install syslog-ng ``` - 配置syslog-ng:打开syslog-ng配置文件,并添加以下内容: ``` source s_mydriver {file("/var/log/mydriver.log");}; filter f_mydriver {facility(kern) and match("mydriver:");}; destination d_mydriver {file("/var/log/mydriver.log");}; log {source(s_mydriver); filter(f_mydriver); destination(d_mydriver);}; ``` 这将配置syslog-ng来捕获内核日志包含“mydriver:”关键字的信息,并将其保存在“/var/log/mydriver.log”文件。 - 在驱动程序添加printk语句:在驱动程序代码添加printk语句,以输出调试信息。 - 重新加载syslog-ng:在终端窗口,输入以下命令以重新加载syslog-ng配置文件: ``` $ sudo service syslog-ng reload ``` 现在,你可以在驱动程序执行操作,并查看“/var/log/mydriver.log”文件,以捕获输出的调试信息。 请注意,打开驱动程序的调试信息可能会影响系统性能,并且需要具备一定的调试经验。建议在测试环境进行操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值