Linux cpu 性能分析(3):火焰图(Flame Graph)—— 从 CPU 到“万物皆可火焰图”
在 Linux 性能分析领域,Flame Graph(火焰图) 已经成为定位性能瓶颈最重要的可视化工具之一。无论是 CPU 使用率过高、线程阻塞、锁竞争、磁盘 IO,还是 Page Fault、网络延迟,都可以通过火焰图直观地展示。
很多初学者认为火焰图只能分析 CPU,其实这是一个误区。火焰图本质上是一种可视化方式,而不是一种采样方法。 任何能够采集到调用栈(Stack Trace)的事件,都可以生成对应的火焰图,因此业内也流传着一句话:
Anything can be a Flame Graph(万物皆可火焰图)。
本文介绍几种最常见的火焰图类型,以及它们适用的分析场景。
什么是火焰图?
火焰图由 Brendan Gregg 提出,它将大量调用栈按照相同路径进行聚合,并以矩形的形式展示。
一张典型的 CPU 火焰图如下:
main
│
┌───────┴────────┐
nginxworker
││
process()handle_req()
││
parse()mysql_query()
││
memcpy()send()
火焰图有两个重要特点:
横轴(Width)
表示资源消耗。
对于 CPU 火焰图:
矩形越宽,表示 CPU 时间越多。
注意:
横轴不是时间轴!
很多人第一次看火焰图都会误认为:
左边
──────►
右边
表示程序执行时间,这是错误的。
横轴只是把相同调用栈合并后的统计结果。

纵轴(Height)
表示调用关系。
例如:
main()
└── foo()
└──bar()
└──memcpy()
最底层是入口函数。
越往上调用越深。
On-CPU Flame Graph(CPU 火焰图)
这是最经典的火焰图, 也是人们最常用的。可以通过perf/profile生成
采样对象:
CPU 正在执行什么?
采样来源:
●perf
●profile(BCC)
●eBPF profile
例如:
main
├── parser
│└── json_parse
│
└── worker
└──memcpy
如果:
memcpy
特别宽。
说明:
CPU 大部分时间花在 memcpy。
适用于:
●CPU 使用率高
●查找热点函数
●性能 Profiling
Off-CPU Flame Graph(阻塞火焰图)
CPU 火焰图只能回答:
CPU 在忙什么?
但是:
CPU 利用率很低,
程序为什么还是很慢?
这时候就需要:
Off-CPU Flame Graph (bcc工具offcputime)
采样:
线程什么时候睡眠?
例如:
read()
futex()
poll()
epoll_wait()
这些函数越宽,
说明等待时间越长。
典型问题:
●IO 等待
●锁竞争
●数据库等待
●网络等待
很多线上问题:
CPU 只有 20%
但响应时间很长。
最后发现:
全部卡在:
futex()
就是 Off-CPU 找出来的。
Wakeup Flame Graph(唤醒火焰图)
线程为什么被唤醒?可以使用bcc工具wakeuptime
例如:
Thread A
│
▼
pthread_cond_signal()
唤醒
Thread B
Wakeup Flame Graph
统计:
是谁唤醒了别人?
适用于:
●调度分析
●锁竞争
●Producer / Consumer
例如:
Java、
Kafka、
Redis、
数据库
大量线程唤醒。
Off-Wakeup Flame Graph
Wakeup 看:
谁唤醒别人。
Off-Wakeup:通过bcc工具offwaketime
看:
谁让别人睡觉。
例如:
Thread A
↓
pthread_mutex_lock()
↓
Thread B
↓
pthread_mutex_unlock()
Off-Wakeup 可以分析:
阻塞是谁造成的。
它非常适合:
分析锁竞争。
Page Fault Flame Graph
可以使用工具perf指定event(-e ), 以及关联的调用函数栈(-g)
sudoperf record \
-epage-faults \
-g\
-ppid
Page Fault
不仅影响内存。
也影响 CPU。
例如:
malloc()
↓
缺页
↓
Page Fault
↓
Kernel
↓
分配物理页
Page Fault Flame Graph
可以统计:
哪些函数
最容易触发:
●Minor Fault
●Major Fault
例如:
数据库
Redis
JVM
内存映射文件。
Memory Flame Graph
可以使用bpftrace关联uprobe以及对应的调用栈
bpftrace -e '
uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc
{
@[ustack]= count();
}'
统计:
malloc
free
new
delete
例如:
operator new()
↓
std::vector
↓
push_back()
可以找到:
内存分配热点。
为什么说“万物皆可火焰图”?
火焰图并不是 CPU 专属工具,而是一种调用栈聚合与可视化技术。
只要能够获取事件发生时的调用栈(Call Stack),就可以按照相同的方法生成火焰图。因此,火焰图不仅适用于 CPU,还可以用于内存、I/O、锁、中断、调度等各种场景。
例如:
可以看到,它们唯一的区别只是采样事件不同(perf/bcc/bpftrace)
●CPU Samples → On-CPU Flame Graph
●Sleep Event → Off-CPU Flame Graph
●Wakeup Event → Wakeup Flame Graph
●Page Fault Event → Page Fault Flame Graph
而最终都采用相同的火焰图形式进行展示
总结
火焰图不是一种性能分析工具,而是一种调用栈聚合与可视化方法。它的核心思想是:把海量事件按照调用路径进行聚合,并用矩形宽度表示资源消耗或事件发生频率。
在现代 Linux 性能分析中,CPU 火焰图只是最常见的一种形式。随着 eBPF、perf、BCC、bpftrace 等技术的发展,几乎所有能够获取调用栈的事件都可以生成火焰图。因此,从 CPU、内存、调度、锁竞争,到网络、文件系统、中断甚至系统调用,都可以用同一种方式进行分析,这也是性能分析领域广为流传的理念——“万物皆可火焰图(Anything can be a Flame Graph)”。