性能分析工具链:Linux perf、ftrace、eBPF、火焰图、Valgrind、gprof、strace 的适用场景与基本用法
做嵌入式性能优化这些年,我最大的体会是:工具不在多,在于用对地方。很多人一上来就上 perf,结果抓了一堆数据看不懂。也有人只用 printf 打点,遇到诡异问题就抓瞎。
今天我把 Linux 生态下最常用的 7 个性能工具串起来讲。每个工具我都会说清楚:它解决什么问题、什么时候用它、怎么快速上手。嗯,这些都是我踩过坑之后总结出来的经验。
核心观点:没有万能工具。性能分析的关键是——先定位问题类型,再选对工具。
1. perf:CPU 热点分析的瑞士军刀
适用场景:CPU 占用高、函数热点定位、缓存命中率分析、上下文切换频繁。
perf 是我用得最多的工具。说白了,它就是靠硬件 PMU(性能监控单元)来采样的。CPU 在跑什么代码,它每隔一段时间就拍一张快照。
基本用法就三板斧:
# 采样 10 秒,记录 CPU 事件
perf record -a -g -- sleep 10
# 查看采样结果
perf report
# 实时查看热点(类似 top)
perf top
我的经验:-g 参数一定要加,它记录调用链。不加的话,你只知道哪个函数耗 CPU,但不知道是谁调了它。我曾经因为这个漏掉了一个第三方库的循环调用问题,排查了两天。
perf 还能做更细粒度的分析:
# 统计缓存 miss
perf stat -e cache-misses,cache-references ./myapp
# 追踪上下文切换
perf stat -e context-switches ./myapp
2. ftrace:内核函数追踪的利器
适用场景:驱动延迟、中断响应时间、内核态函数调用关系、调度延迟。
ftrace 是内核自带的追踪器。我习惯用它来查内核态的性能问题。比如某个中断处理时间过长,或者调度器延迟异常。
它的用法比较特别,通过文件系统操作:
# 挂载 tracefs
mount -t tracefs nodev /sys/kernel/tracing
# 开启函数追踪
echo function > /sys/kernel/tracing/current_tracer
echo do_irq > /sys/kernel/tracing/set_ftrace_filter
echo 1 > /sys/kernel/tracing/tracing_on
# 查看追踪结果
cat /sys/kernel/tracing/trace
注意:ftrace 的开销不可忽视。生产环境慎用 function 追踪,建议用 function_graph 或者只追踪特定函数。我曾经在客户现场开了全量追踪,结果系统直接卡死——教训深刻。
3. eBPF:新时代的动态追踪王者
适用场景:自定义内核探针、网络性能分析、容器环境追踪、安全监控。
eBPF 是近几年最火的性能技术。它允许你在内核中安全地运行沙箱程序。说白了,就是你可以动态插入探针,而不需要修改内核代码。
我推荐用 BCC 工具集,它封装了 eBPF 的复杂性:
# 安装 BCC
apt-get install bpfcc-tools
# 追踪新进程创建
execsnoop-bpfcc
# 查看块设备 I/O 延迟
biolatency-bpfcc
# 追踪 TCP 连接
tcptop-bpfcc
如果你想写自己的 eBPF 程序,Python 接口很友好:
from bcc import BPF
bpf_text = """
int kprobe__sys_clone(void *ctx) {
bpf_trace_printk("clone called\\n");
return 0;
}
"""
BPF(text=bpf_text).trace_print()
核心优势:eBPF 不需要重启系统,不需要加载内核模块,安全性有保障。我在生产环境排查网络延迟问题时,eBPF 几乎是唯一的选择。
4. 火焰图:让性能数据一目了然
适用场景:perf 数据的可视化呈现、快速定位热点路径、对比优化前后效果。
火焰图不是工具,是可视化方法。它把 perf 采样数据变成一张图。X 轴是采样数(越宽越热),Y 轴是调用栈深度。
生成火焰图的流程:
# 1. 用 perf 采样
perf record -a -g -F 99 -- sleep 30
# 2. 生成火焰图
perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > output.svg
怎么看火焰图?记住一句话:找最宽的方块。那个就是最耗 CPU 的函数。如果顶层很宽,说明函数本身耗 CPU;如果底层很宽,说明调用链上有问题。
我的习惯:优化前后各生成一张火焰图,并排对比。肉眼就能看出热点有没有消除。有一次我优化了一个内存拷贝函数,火焰图上那个方块从 30% 缩到了 5%,成就感满满。
5. Valgrind:内存问题的照妖镜
适用场景:内存泄漏、越界访问、使用未初始化内存、double free。
Valgrind 是一个虚拟执行环境。它模拟 CPU 执行你的程序,同时检查每一次内存访问。代价是程序会慢 10-20 倍。
基本用法:
# 检查内存泄漏
valgrind --leak-check=full ./myapp
# 检查越界访问
valgrind --tool=memcheck ./myapp
# 分析缓存使用
valgrind --tool=cachegrind ./myapp
避坑指南:Valgrind 在嵌入式平台上可能跑不起来,因为需要虚拟内存支持。我曾在 ARM Cortex-A 上折腾过,最后改用 AddressSanitizer 编译选项替代。如果你在资源受限的环境,优先考虑 ASan。
6. gprof:老牌调用计数工具
适用场景:函数调用次数统计、调用关系图生成、简单性能分析。
gprof 是 GNU 工具链自带的。它通过编译时插桩来统计每个函数的调用次数和执行时间。
用法很简单:
# 编译时加 -pg 选项
gcc -pg -o myapp myapp.c
# 运行程序(会生成 gmon.out)
./myapp
# 分析结果
gprof myapp gmon.out
输出会告诉你每个函数被调了多少次、自耗时、子调用耗时。我一般用它来快速了解代码的调用结构。
说实话:gprof 现在用得少了。perf 和 eBPF 能覆盖它的功能,而且不需要重新编译。但如果你在维护老项目,gprof 依然是最快上手的选择。
7. strace:系统调用的显微镜
适用场景:文件 I/O 问题、网络连接失败、权限错误、启动慢排查。
strace 拦截并记录所有系统调用。它不分析 CPU 热点,而是看程序跟内核的交互。
基本用法:
# 追踪已有进程
strace -p 1234
# 统计系统调用次数
strace -c ./myapp
# 只追踪特定系统调用
strace -e trace=open,read,write ./myapp
我遇到过最典型的场景:一个程序启动花了 30 秒。用 strace 一看,发现它在循环读取一个不存在的配置文件,每次 read 都返回 ENOENT,重试了 1000 次。这就是 strace 的威力——一眼看穿。
总结一下:
- CPU 热点 → perf + 火焰图
- 内核态问题 → ftrace / eBPF
- 内存泄漏 → Valgrind
- 调用结构 → gprof
- 系统调用 → strace
工具链就这些。但记住,工具只是手段。真正重要的是你分析问题的思路。先问自己:这个性能问题属于哪一类?CPU 瓶颈?I/O 瓶颈?锁竞争?内存分配?想清楚了再选工具,事半功倍。
公众号:蓝海资料掘金营,微信deep3321