function_graph是Linux内核内置的ftrace追踪器之一,能够以图形化方式展示函数调用链和耗时,是性能调优和问题定位的利器。
什么是function_graph?为什么它值得关注
function_graph是ftrace框架下的一个tracer,专门用于记录内核函数的进入和退出事件,它通过缩进和耗时标注,直观呈现函数调用层次和每层执行时间,相比传统的printk或手动打点,function_graph可以在不修改内核代码的情况下,快速摸清代码路径和性能瓶颈。
核心工作原理
function_graph在内核函数入口处插入钩子,记录调用深度和时间戳;在出口处再次记录,计算实际耗时,输出结果中,每一层缩进代表一个函数调用,右侧的数字是该函数执行时间(微秒级),这种机制让开发者一眼看清“时间花在了哪里”。
典型应用场景
- 内核驱动开发:验证驱动程序的行为是否符合预期,例如检查中断处理函数是否过度占用CPU。
- 启动优化:分析内核启动阶段各子系统的初始化顺序和耗时,定位拖慢启动的模块。
- 性能抖动排查:当系统出现偶发卡顿,利用function_graph追踪特定进程的内核调用,寻找异常延迟点。
function_graph怎么用?从配置到实战
前提条件:内核配置
function_graph依赖ftrace支持,大部分发行版默认启用,若需确认,可检查内核配置是否包含CONFIG_FUNCTION_GRAPH_TRACER=y,在/boot/config-$(uname -r)中搜索该选项即可。
启用与基本操作
通过tracefs虚拟文件系统控制,挂载点通常在/sys/kernel/tracing或/sys/kernel/debug/tracing,以下命令序列是典型流程:
# 挂载tracefs(如未挂载) mount -t tracefs nodev /sys/kernel/tracing # 选择function_graph tracer echo function_graph > /sys/kernel/tracing/current_tracer # 开始追踪 echo 1 > /sys/kernel/tracing/tracing_on # 等待一段时间或复现问题,然后停止 echo 0 > /sys/kernel/tracing/tracing_on # 查看结果 cat /sys/kernel/tracing/trace
输出示例:
1) | do_sys_open() {
1) | getname() {
1) 0.910 us | kmem_cache_alloc();
1) 0.210 us | __check_object_size();
1) 2.500 us | }
1) 0.130 us | file_open();
1) 5.200 us | }
缩进代表调用深度,右侧数字是函数执行时间,后的空格数表示层级。
进阶技巧:过滤和追踪特定进程
按函数名称过滤
只追踪某个函数及其子调用,避免输出过多:
echo do_sys_open > /sys/kernel/tracing/set_graph_function
按进程过滤
追踪特定PID的调用:
echo 1234 > /sys/kernel/tracing/set_ftrace_pid
排除不关心的函数
使用set_graph_notrace排除一些高频且无价值的函数(如printk、spin_lock等),减少噪音。
使用trace-cmd简化操作
trace-cmd是ftrace的前端工具,封装了上述文件操作,更直观:
# 记录function_graph追踪,持续10秒 trace-cmd record -p function_graph -O graph-time -l do_sys_open sleep 10 # 查看结果 trace-cmd report
-l指定只追踪的函数,-O graph-time输出时间信息,这种方式适合自动化脚本和长时间抓取。
function_graph性能分析最佳实践
识别热点函数
在高负载场景下,开启function_graph后,观察哪些函数耗时占比最大,较深的缩进和较大的时间值意味着潜在的优化点,在网络驱动中,若hard_start_xmit函数耗时异常,多数情况下是锁竞争或硬件响应慢。
分析内核态延迟
当用户态程序响应变慢,但CPU使用率不高,问题可能在等待内核资源,可以通过function_graph追踪该进程的内核调用,检查mutex_lock、wait_event等函数的等待时间,如果schedule()函数频繁出现且耗时较长,说明进程被频繁切换,需要调整调度策略或减少锁持有时间。
结合trace_hwlat检测硬件干扰
function_graph可以与硬件延迟检测器配合使用,先通过echo hwlat > current_tracer确认硬件是否存在异常中断,再切换回function_graph追踪具体受影响的内核路径,行业共识认为,这种组合排查方式是解决系统抖动问题的高效套路。
function_graph与其他追踪工具有何不同?
与perf对比
- perf:基于硬件计数器和采样,适合宏观性能统计(如CPU周期、缓存命中率),但函数级调用链信息不如function_graph精细。
- function_graph:提供完整的调用树和精确时间,适合微观行为分析,但开销较大(所有函数入口出口都插桩),不适合长时间全量开启。
与strace对比
- strace:追踪用户态系统调用,不深入内核内部。
- function_graph:追踪内核函数,包括系统调用内部实现,例如
do_sys_open内部的路径,两者互补,排查用户态问题用strace,内核态问题用function_graph。
与systemtap对比
- systemtap:需要编写脚本,动态插桩,灵活性高但学习曲线陡峭。
- function_graph:无需脚本,简单配置即可使用,适合快速验证和初步定位。
选择建议
- 快速定位内核函数调用顺序:function_graph
- 分析CPU热点和硬件事件:perf
- 追踪用户态系统调用:strace
- 复杂自定义探针:systemtap
function_graph使用注意事项
性能开销
function_graph在每个内核函数入口和出口插入探测点,会显著增加系统开销,根据内核版本和硬件,整体性能可能下降10%-30%。生产环境建议仅在短时间、低负载下使用,或通过set_graph_notrace排除高频函数。
缓冲区溢出
默认的trace buffer较小,长时间追踪可能导致输出被截断,可通过
echo 8192 > /sys/kernel/tracing/buffer_size_kb调整缓冲区大小,8192表示8MB,可根据内存容量适当增大。
内核版本差异
- 内核版本<3.0:function_graph可能不支持某些架构(如ARM的早期版本)。
- 内核版本>=4.0:支持更完善的过滤机制和
trace-cmd集成。 - 内核版本>=5.10:引入
osnoise追踪器,与function_graph配合可进一步增强实时性分析。
读trace输出时注意时间单位
默认时间单位为微秒(us),但部分内核配置下可能显示为纳秒(ns),可通过echo 0 > /sys/kernel/tracing/trace_options关闭latency-format,确保输出一致。
function_graph是内核开发者手中最直观的调用链分析工具,能快速缩小问题范围,避免盲目加日志,搭配过滤器和trace-cmd,即可在复杂场景中高效定位性能瓶颈。
function_graph常见问题解答
function_graph怎么使用?
在/sys/kernel/tracing目录下,先设置current_tracer为function_graph,然后开启tracing_on,停止后读取trace文件,也可以使用trace-cmd record -p function_graph一键操作,推荐先用set_graph_function指定目标函数,避免输出过多。
function_graph和perf的区别是什么?
function_graph追踪函数调用树和精确时间,适合微观路径分析;perf基于采样,适合宏观性能统计,两者定位不同,function_graph更侧重“谁调用了谁,花了多久”,而perf侧重“什么代码消耗了最多CPU”,排查具体内核函数行为时首选function_graph,分析系统整体性能分布时选perf。
function_graph能追踪用户态吗?
不能,function_graph只追踪内核态函数,包括系统调用内部实现,若需用户态函数调用链,应使用gprof、perf的用户态采样或动态插桩工具(如uprobes),function_graph可以配合set_ftrace_pid追踪特定进程的内核调用,但无法进入用户态代码。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/511629.html


