在上一篇文章中介绍了内核加载的方式使用kprobe的方法,现在介绍一下使用debugfs接口使用kprobe的方法。
模块加载的终究不是很方便,尤其对于一些不带gcc的嵌入式系统,需要交叉编译ko,将ko拷贝到单板,然后insmod,不便。debugfs下(确切地说,应该是ftrace)提供了一套注册、使能、注销kprobe的接口,可以很方便地操作kprobe。
需要将debugfs文件系统挂在到/sys/kernel/debug/目录下:
# mount -t debugfs nodev /sys/kernel/debug/
此时/sys/kernel/debug/tracing目录下就出现了若干个文件和目录用于用户设置要跟踪的函数以及过滤条件等等,这里我主要关注以下几个文件:
重要文件接口
配置接口:/sys/kernel/debug/tracing/kprobe_events
读取信息接口:/sys/kernel/debug/tracing/trace
读取信息接口:/sys/kernel/debug/tracing/trace_pipe
开启某个kprobe接口:
/sys/kernel/debug/tracing/events/kprobes/<EVENT>/enabled
过滤接口:
/sys/kernel/debug/tracing/events/kprobes/<EVENT>/filter
其中配置属性文件用于用户配置要探测的函数以及探测的方式与参数,在配置完成后,会在events/kprobes/目录下生成对应的目录;其中会生成enabled、format、filter和id这4个文件,其中的enable属性文件用于控制探测的开启或关闭,filter用于设置过滤条件,format可以查看当前的输出格式,最后id可以查看当前probe event的ID号。然后若被探测函数被执行流程触发调用,用户可以通过trace属性文件进行查看。最后通过kprobe_profile属性文件可以查看探测命中次数和丢失次数(probe hits and probe miss-hits)。
下面来看看各个属性文件的常用操作方式(其中具体格式和参数方面的细节可以查看内核的Documentation/trace/kprobetrace.txt文件,描述非常详细):
1、kprobe_events
该属性文件支持3中格式的输入:
各字段的含义如下:
基本用法如下:
建议echo用‘>>’重定向输出,否则会把前面的冲掉。通过上面两个命令,下发了两个kprobe,“p do_fork”是当有进程调用do_fork函数的时候,会有打印信息。“r do_fork”是有进程调用do_fork完成之后会有打印信息。
>kprobe_events 命令可以将上面两个kprobe删除。
2 .开启某个kprobe
创建kprobe的时候,会在events/kprobes/下为每个probe创建一个目录,目录下有这个kprobe相关的接口。下面是开启kprobe的方式。
3. 查看kprobe消息
要查看哪些进程触发了这些kprobe,可以通过trace、trace_pipe接口查看,输出格式如下,最左边是进程名,如果是<…>,可能是因为cat的时候,那个进程号对应的进程已经不存在了,第二个是进程PID,触发kprobe的时候记录的。FUNCTION就是触发的那个kprobe的名字,后面括号里是触发的时候代码位置,如果是“r”类型的kprobe,会显示返回到了什么代码位置。代码位置中的行号是反汇编对应的行号。
利用“r”方式的kprobe会显示函数返回地址,我们可以追某个函数被什么调用了,例如下面的do_fork被sys_clone调用了,我们就可以将sys_clone加到kprobe(echor sys_clone >> kprobe_events),就可以看到哪个代码调用了sys_clone,直到追到我们想要看到的代码。
如果输出太多了,想要清除就向trace中写0即可
最后补充一点,若此时需要查看函数调用的栈信息(stacktrace),可以使用如下命令激活stacktrace输出: