如何使用Linux perf分析内核模块并获取指定函数及其子函数的进出时间
实现方案
首先明确:可以实现你要的需求。你遇到的部分函数不在--funcs列表的核心原因是:perf probe -m <模块路径> --funcs默认仅展示模块的导出符号,模块内部用static修饰的静态函数默认不会出现在该列表中,这类函数只要保留了模块的调试信息,依然可以添加探测点。
方案1:使用ftrace function_graph(更适配你的需求,操作更简单)
function_graph tracer是内核原生专门用于统计函数调用链、进出时间、执行耗时的工具,无需批量添加探针就能直接输出你要的全部信息,操作步骤如下:
- 挂载tracefs(默认多数发行版已经挂载,未挂载则执行)
sudo mount -t tracefs nodev /sys/kernel/tracing - 配置仅跟踪目标内核模块的函数:
echo <你的模块名> > /sys/kernel/tracing/set_ftrace_filter - 配置跟踪器为函数调用图模式:
echo function_graph > /sys/kernel/tracing/current_tracer - 设置跟踪的根函数,也就是你指定的目标函数:
echo <你的目标函数名> > /sys/kernel/tracing/set_graph_function - 开启跟踪:
echo 1 > /sys/kernel/tracing/tracing_on - 复现你要测试的业务场景,完成后关闭跟踪:
echo 0 > /sys/kernel/tracing/tracing_on - 查看结果:
cat /sys/kernel/tracing/trace
输出结果里会直接展示目标函数调用链下所有同模块子函数的进入时间、退出时间、调用深度、单次执行耗时,默认自带纳秒级时间精度,不需要额外解析。
测试完成后重置ftrace配置,避免占用系统资源:echo nop > /sys/kernel/tracing/current_tracer
方案2:使用perf probe全量打点
如果你需要和perf其他功能配合使用,可以用perf批量添加探测点实现:
前置准备
确保你的内核模块带调试信息(自己编译的话请加-g编译参数,不要执行strip操作),发行版提供的模块请安装对应的debuginfo包。
操作步骤
- 列出模块所有可探测函数(含静态函数)
sudo perf probe -m <你的模块路径> --funcs --no-inlines
筛选出你需要的目标函数及其所有子函数,存到func_list.txt中,每行一个函数名。 - 批量添加入口和返回探针
执行shell循环批量添加:while read func; do sudo perf probe -m <你的模块路径> -a ${func} # 入口探针 sudo perf probe -m <你的模块路径> -a ${func}%return # 返回探针 done < func_list.txt - 录制性能数据
录制过程中保留时间戳和调用栈,方便后续过滤目标函数调用链下的事件:sudo perf record -e 'probe_<你的模块名>:*' -a -g -- <你要执行的测试命令/或者加sleep N等待测试完成> - 解析结果
用perf script直接输出带纳秒级时间戳的所有事件:sudo perf script --ns
过滤调用栈中包含目标函数的事件,就能得到目标函数及其调用的所有子函数的进出时间。
测试完成后删除所有添加的探针:sudo perf probe --del '*'
注意事项
- 你的内核需要开启
CONFIG_FUNCTION_TRACER、CONFIG_FUNCTION_GRAPH_TRACER、CONFIG_KPROBE_EVENTS配置项,可通过cat /boot/config-$(uname -r) | grep 上述配置名确认是否开启,多数发行版默认内核均已开启这些配置。 - 如果静态函数依然无法探测,请确认你的模块没有被strip,调试信息是否完整。
内容的提问来源于stack exchange,提问作者Future Fisherman
相关产品推荐
相关产品推荐

