能做什么:
可以分析函数执⾏的频繁程度
可以分析哪些函数经常阻塞
可以分析哪些函数频繁分配内存
Flame Graph 原图路径:https://queue.acm.org/downloads/2016/Gregg4.svg
perf 命令(performance 的缩写)讲起, 它是 Linux 系统原⽣提供的性能分析⼯具, 会返回 CPU 正在执⾏的函数名以及调⽤栈(stack)
安装perf
apt install linux-tools-common
测试perf是否可用 perf.data为产生的文件
┌──(rootluo)-[~/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) ] ┌──(rootluo)-[~/workspace/flamegraph/src-flamegraph/test] └─# ls perf.data perf.data.old test test.c2.2 编译并运行测试程序
test.c 源码和接受在最下面
┌──(rootluo)-[~/workspace/flamegraph/src-flamegraph/test] └─# gcc -o test test.c -g ┌──(rootluo)-[~/workspace/flamegraph/src-flamegraph/test] └─# ./test main into2.3 使用perf采集数据
perf record 表示采集系统事件, 没有使⽤ -e 指定采集事件, 则默认采集 cycles(即 CPU clock 周
期), -F 99 表示每秒 99 次, -p pidof test是进程号, 即对哪个进程进⾏分析, -g 表示记录调⽤栈,
sleep 30 则是持续 30 秒.
┌──(rootluo)-[~/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) ] ┌──(rootluo)-[~/workspace/flamegraph/src-flamegraph/test] └─# ls perf.data perf.data.old test test.c
使用perf report 可以看到堆栈热点函数
┌──(rootluo)-[~/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
2.4 下载火焰图脚本
https://github.com/brendangregg/FlameGraph
git clone https://gitee.com/mirrors/FlameGraph.git
┌──(rootluo)-[~/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. ┌──(rootluo)-[~/workspace/flamegraph/src-flamegraph/test] └─# ls FlameGraph perf.data perf.data.old test test.c ┌──(rootluo)-[~/workspace/flamegraph/src-flamegraph/test] └─# ls FlameGraph/ aix-perf.pl stackcollapse-gdb.pl demos stackcollapse-go.pl dev stackcollapse-instruments.pl ... ...2.5 生成火焰图
┌──(rootluo)-[~/workspace/flamegraph/src-flamegraph/test] └─# perf script | ./FlameGraph/stackcollapse-perf.pl | ./FlameGraph/flamegraph.pl > process.svg
下载到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 的繁忙程度, 所以⼀般选择暖⾊调.
#includevoid 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 inton"); while (1) // 100 { for (int i = 30 * 10000; i--;); // 30 func_a(); //10+5 =15 func_b(); // 20 func_c(); // 35 } printf("main endn"); return 0; }



