# 1 perf工具和火焰图简介
能做什么:
可以分析函数执⾏的频繁程度
可以分析哪些函数经常阻塞
可以分析哪些函数频繁分配内存
Flame Graph 原图路径:https://queue.acm.org/downloads/2016/Gregg4.svg
# 2 通过perf收集函数堆栈信息绘制火焰图
# 2.1 安装perf
perf 命令(performance 的缩写)讲起, 它是 Linux 系统原⽣提供的性能分析⼯具, 会返回 CPU 正在执⾏的函数名以及调⽤栈(stack)
安装perf
apt install linux-tools-common
1
测试perf是否可用 perf.data为产生的文件
┌──(root💀luo)-[~/workspace/flamegraph/src-flamegraph/test]
└─# perf record -F 99 -a -g -- sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.598 MB perf.data (3370 samples) ]
┌──(root💀luo)-[~/workspace/flamegraph/src-flamegraph/test]
└─# ls
perf.data perf.data.old test test.c
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 2.2 编译并运行测试程序
test.c 源码和接受在最下面
┌──(root💀luo)-[~/workspace/flamegraph/src-flamegraph/test]
└─# gcc -o test test.c -g
┌──(root💀luo)-[~/workspace/flamegraph/src-flamegraph/test]
└─# ./test
main into
1
2
3
4
5
2
3
4
5
# 2.3 使用perf采集数据
perf record 表示采集系统事件, 没有使⽤ -e 指定采集事件, 则默认采集 cycles(即 CPU clock 周
期), -F 99 表示每秒 99 次, -p pidof test
是进程号, 即对哪个进程进⾏分析, -g 表示记录调⽤栈,
sleep 30 则是持续 30 秒.
┌──(root💀luo)-[~/workspace/flamegraph/src-flamegraph/test]
└─# perf record -F 99 -p `pidof test` -g -- sleep 30
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.235 MB perf.data (2962 samples) ]
┌──(root💀luo)-[~/workspace/flamegraph/src-flamegraph/test]
└─# ls
perf.data perf.data.old test test.c
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
使用perf report 可以看到堆栈热点函数
┌──(root💀luo)-[~/workspace/flamegraph/src-flamegraph/test]
└─# perf report
Samples: 2K of event 'cpu-clock:pppH', Event count (approx.): 29919191620
Children Self Command Shared Object Symbol
+ 100.00% 0.00% test libc-2.31.so [.] __libc_start_main
+ 100.00% 29.71% test test [.] main
+ 35.58% 35.55% test test [.] func_c
+ 19.85% 19.85% test test [.] func_b
+ 14.85% 9.55% test test [.] func_a
+ 5.30% 5.30% test test [.] func_d
0.03% 0.03% test [kernel.kallsyms] [k] __lock_text_start
0.03% 0.00% test [kernel.kallsyms] [k] apic_timer_interrupt
0.03% 0.00% test [kernel.kallsyms] [k] smp_apic_timer_interrupt
0.03% 0.00% test [kernel.kallsyms] [k] irq_exit
0.03% 0.00% test [kernel.kallsyms] [k] __softirqentry_text_start
0.03% 0.00% test [kernel.kallsyms] [k] run_timer_softirq
0.03% 0.00% test [kernel.kallsyms] [k] __run_timers.part.0
0.03% 0.00% test [kernel.kallsyms] [k] call_timer_fn
0.03% 0.00% test [kernel.kallsyms] [k] rh_timer_func
0.03% 0.00% test [kernel.kallsyms] [k] usb_hcd_poll_rh_status
0.03% 0.00% test [kernel.kallsyms] [k] uhci_hub_status_data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 2.4 下载火焰图脚本
https://github.com/brendangregg/FlameGraph
git clone https://gitee.com/mirrors/FlameGraph.git
┌──(root💀luo)-[~/workspace/flamegraph/src-flamegraph/test]
└─# git clone https://gitee.com/mirrors/FlameGraph.git
Cloning into 'FlameGraph'...
remote: Enumerating objects: 1147, done.
remote: Total 1147 (delta 0), reused 0 (delta 0), pack-reused 1147
Receiving objects: 100% (1147/1147), 1.87 MiB | 806.00 KiB/s, done.
Resolving deltas: 100% (674/674), done.
┌──(root💀luo)-[~/workspace/flamegraph/src-flamegraph/test]
└─# ls
FlameGraph perf.data perf.data.old test test.c
┌──(root💀luo)-[~/workspace/flamegraph/src-flamegraph/test]
└─# ls FlameGraph/
aix-perf.pl stackcollapse-gdb.pl
demos stackcollapse-go.pl
dev stackcollapse-instruments.pl
...
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2.5 生成火焰图
┌──(root💀luo)-[~/workspace/flamegraph/src-flamegraph/test]
└─# perf script | ./FlameGraph/stackcollapse-perf.pl | ./FlameGraph/flamegraph.pl > process.svg
1
2
2
下载到windows桌面用浏览器打开查看效果
https://lzwtty.oss-cn-hangzhou.aliyuncs.com/blogimage/acknowledge/test_flamegraph.svg
可以很清晰的看到热点函数调用占用cpu使用率
# 2.5 ⽕焰图的含义
⽕焰图是基于 stack 信息⽣成的 SVG 图⽚, ⽤来展示 CPU 的调⽤栈。
- y 轴表示调⽤栈, 每⼀层都是⼀个函数. 调⽤栈越深, ⽕焰就越⾼, 顶部就是正在执⾏的函数, 下
⽅都是它的⽗函数.
- x 轴表示抽样数, 如果⼀个函数在 x 轴占据的宽度越宽, 就表示它被抽到的次数多, 即执⾏的时
间⻓. 注意, x 轴不代表时间, ⽽是所有的调⽤栈合并后, 按字⺟顺序排列的.
- ⽕焰图就是看顶层的哪个函数占据的宽度最⼤. 只要有 “平顶”(plateaus), 就表示该函数可能存
在性能问题。
- 颜⾊没有特殊含义, 因为⽕焰图表示的是 CPU 的繁忙程度, 所以⼀般选择暖⾊调.
# 源码test.c
#include <stdio.h>
void func_d() // 5
{
for (int i = 5 * 10000; i--;); //5
}
void func_a() // 10+5= 15
{
for (int i = 10 * 10000; i--;); //10
func_d(); //5
}
void func_b()
{
for (int i = 20 * 10000; i--;); // 20
}
void func_c()
{
for (int i = 35 * 10000; i--;); // 35
}
int main(void)
{
printf("main into\n");
while (1) // 100
{
for (int i = 30 * 10000; i--;); // 30
func_a(); //10+5 =15
func_b(); // 20
func_c(); // 35
}
printf("main end\n");
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32