参考perf工具安装和使用
perf安装命令:
1 2 3 4 |
sudo apt-get install linux-tools-$(uname -r) linux-tools-generic -y sudo apt-get install linux-tools-$(uname -r) linux-tools-generic -y --fix-missing
perf -v # 查看perf版本 |
No permission to enable cycles: u event
解决方法:直接修改 /proc/sys/kernel/perf_event_paranoid,按照报错提示修改为 -1
如果是docker容器环境提示 read only file system, 新建容器的时候应该添加 privileged=true 赋予权限
新建并添加测试程序如下,输入代码
1 2 |
touch main.cpp vim main.cpp |
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 |
#include <iostream> using namespace std;
void delay() { int i,j; for(i = 0; i < 1000000; i++) j=i; }
void function_1() { int i; for(i=0 ; i < 10; i++) delay(); }
void function_2() { int i; for(i = 0; i< 30; i++) delay(); }
int main(void) { std::cout << "begin: " << std::endl; function_1(); function_2(); std::cout << "end!" << std::endl; } |
编译测试程序
1 |
g++ main.cpp -o main |
生成perf.data
1 |
sudo perf record ./main |
添加-g选项查看调用信息
1 |
sudo perf record -g ./main |
生成热点函数占用CPU的比例
1 |
perf report |
可以看到, 程序中main函数总体CPU资源占比(CPU时间片)97.85%, 其中delay函数占比97.59%, function_1和function_2的占比占比近7:3
Flame Graph工程实现了一套生成火焰图的脚本, 直接clone下来使用
1 |
git clone https://github.com/brendangregg/FlameGraph.git |
该文件夹下有很多perf语言脚本, 可完成不同堆栈分析
捕获堆栈: perf script -i perf.data &> perf.unfold
折叠堆栈: ./FlameGraph/FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded
生成火焰图: ./FlameGraph/FlameGraph/flamegraph.pl perf.folded > perf.svg
各个步骤的含义:
y轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数
x轴表示抽样数,如果一个函数在x轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间占比越大。注意,x轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的
颜色无特殊含义, 因为火焰图表示CPU的繁忙程度, 所以一般选择暖色调
火焰图就是看顶层的哪个函数占据的宽度最大。只要有"平顶",就表示该函数可能存在性能问题
鼠标悬浮: (浏览器打开.svg图片)可显示函数名, 抽样抽中的次数, 占总抽样次数的百分比
点击放大: 点击某一层,该层会占据所有宽度,显示详细信息,再点击左上角ResetZoom可恢复
查找: Ctrl + F, 高亮显示查找的函数
火焰图的缺陷: 调用栈可能不完整, 当调用栈过深时,某些系统只返回前面的一部分(比如前10层); 函数名可能缺失, 有些函数没有名字,编译器只用内存地址来表示(比如匿名函数), 有可能是编译器优化等级过高
FlameGraph我们习惯称之为"CPU火焰图", 还有"浏览器火焰图"(x轴是任务时间轴), “红/蓝差分火焰图”, 红蓝差分火焰图可以用来对比分析两次修改的性能差异, 红色表示上升, 蓝色表示下降, 由此来寻找根因; 此外还有"Memory Flame Graphs"(检测内存占用量增加的原因), “Off-CPU Flame Graphs”(不在CPU上运行的时间,如I/O时间等), “Hot/Cold Flame Graphs”(CPU和非CPU结合,显示所有线程的运行时间)
修改源程序
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 |
#include <iostream> using namespace std;
void delay() { int i,j; for(i = 0; i < 1000000; i++) j=i; }
void function_1() { int i; for(i=0 ; i < 20; i++) delay(); }
void function_2() { int i; for(i = 0; i< 20; i++) delay(); }
int main(void) { std::cout << "begin: " << std::endl; function_1(); function_2(); std::cout << "end!" << std::endl; } |
重新编译执行
1 2 |
g++ main.cpp -o main sudo perf record -g ./main # 生成新的perf.data |
折叠堆栈
1 2 |
perf script -i perf.data &> perf.unfold # 生成新的perf.unfold ./FlameGraph/FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded1 |
生成火焰图
1 |
./FlameGraph/FlameGraph/flamegraph.pl perf.folded1 > perf2.svg |
以perf.folded为基准,生成差分火焰图
1 |
./FlameGraph/FlameGraph/difffolded.pl perf.folded perf.folded1 | ./FlameGraph/FlameGraph/flamegraph.pl > diff.svg |
浏览器打开diff.svg
红色部分表示function_1中delay耗时增加了, 蓝色部分表示function_2中delay耗时减少了, 和我们修改的预期是一样的
执行报错1:sudo perf record -g ./可执行文件, 执行失败。节点编译完成后报错找不到
error while loading shared libraries: liblibstatistics_collector.so
解决方法:搜索发现liblibstatistics_collector.so存在, 但通过export添加路径后不生效
执行报错2:上一步生成的perf.data执行perf report提示文件size field is 0
data size field is 0 which is unexpected
原因分析:看下来是perf record -g ./可执行文件的形式没有正确生成perf.data, 单独执行perf record进行采样
1 2 3 4 5 |
top # 查看程序PID = 228
perf record -p 228 -g # 生成perf.data, Ctrl + C结束录制
perf report # 可正常读取 |
生成火焰图
1 2 3 4 5 |
perf script -i perf.data &> perf.unfold # 捕获堆栈
./FlameGraph/FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded # 折叠堆栈
./FlameGraph/FlameGraph/flamegraph.pl perf.folded > perf.svg # 生成火焰图 |
浏览器打开.svg图片
ros2的CPU占用,播包5min
ros1的CPU占用,播包5min
对比结论: