如何动态调试 (ddebug)
动态调试的主要功能是允许你动态的打开或关闭内核代码的各种提示信息. 现在, 假如
CONFIG_DYNAMIC_DEBUG 已经设置好了, 那么所有的 pr_debug()/dev_debug() 之类的函数可以动态的在代码里所使用.
动态调试有很多有用的特性:
* 简洁的查询语言允许打开或关闭调试的状态通过匹配以下的任意组合:
- 资源文件名
- 函数名
- 行号 (包括一定范围的行号)
- 模块名
- 格式化字符串
* 提供一个debugfs 控制文件: <debugfs>/dynamic_debug/control 这个文件被读取用来显示已完成的调试信息列表, 来帮助指导你
控制动态调试的行为
===================================
pr_debug()/dev_debug()的行为控制通过在'debugfs'文件系统上写一个控制文件 .因此,为了这个特性,你首先必须挂载这个debugfs文件系统. 然后, 我们再像这样使用控制文件: <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
在第三列(在Uninx下执行这个命令的结果是一列一列的)显示了调试状态位的激活标志(后边的是标志位的定义). 默认值, 无额外行为被激话, 是这个 "-". 因此你可以查看任何不是默认标志的状态位通过下面的命令:
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 -c 'file svcsock.c\nline 1603 +p' >
<debugfs>/dynamic_debug/control
nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' >
<debugfs>/dynamic_debug/control
命令由wirte()的系统调用界定。如果你想执行多个命令,你需要为每个加入“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
在语法层面上,一个命令由一系列的规格匹配组成,随后由一个标记来改变这规格。
command ::= match-spec* flags-spec
match-spec常用来选择一个已知的dprintk()调用点的子集来套用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 /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c
module
给定的字符串会和每个调用点的模块名进行比较。模块名是和在“lsmod”里看到的字符串一样,举例来说,没有路径或者是.ko后缀同时带有'-'的改变为'_'。
module sunrpc
module nfsd
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行之间的6行
line -1605 // 从第一行到1605行之间的1605行
line 1600- // 从1600行到结尾的全部行
标记规范包含了一个由一个或多个标记字符跟随的变化操作。这变化操作是下面的字符之一:
-
移除给定的标记
+
加入给定的标记
=
设置标记到给定的标记上
这些标记是:
f
包含已打印消息的函数名
l
包含已在打印消息的行号
m
包含已打印消息的模块名
p
产生一个printk()消息到显示系统启动日志
t
包含了不在中断上下文中产生的消息里的线程ID
注意正则表达式^[-+=][flmpt]+$匹配一个标记规范。
同样也要注意,没有便捷的语法来一次性移除所有的标记,你需要使用"-flmpt"。
引导进程中的调试信息
即使在用户空间和debugfs存在之前,也可以使用引导参数:ddebug_query=“QUERY”来激活引导进程中的调试信息。
QUERY遵循上述语法,但一定不能超过1023个字符。调试信息的启动随着arch_initcall而完成。因此通过引导参数arch_initcall之后,你可以在所有的代码处理中启用调试信息。
例如,在一个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
Linux内核动态调试信息的打开,如pr_debug
(2012-08-23 13:38:54)【1】配置Kconfig,添加要调试驱动模块的DDEBUG选项,如
kernel_imx\drivers\mxc\pmic\mc13892\Kconfig
config MXC_PMIC_DEBUG
【2】配置Makefile,添加编译支持驱动模块
kernel_imx\drivers\mxc\pmic\core\Makefile
ifeq ($(CONFIG_MXC_PMIC_DEBUG),y)
endif
【3】配置内核,使支持动态调试
make menuconfig
【4】重烧内核启动后,改变控制台debug消息显示级别,可以打印printk(DEBUG ...)信息
echo > /proc/sys/kernel/printk "8"