当前位置: 首页 > news >正文

性能剖析工具火焰图介绍与实战demo

火焰图介绍

  1. 什么是火焰图

火焰图(Flame Graph)是由 Linux 性能优化大师 Brendan Gregg 发明的。和所有其他的trace和profiling方法不同的是,它从底部往顶部,列出所有可能导致性能瓶颈的调用栈。由于这里的形状类似一团火焰,因此得名火焰图。其他的呈现方法,一般只能列出单一的调用栈或者非层次化的时间分布。
以典型的分析CPU时间花费到哪个函数的on-cpu火焰图为例来展开。CPU火焰图中的每一个方框是一个函数,方框的长度,代表了采样周期内总的采样数,所以越宽的函数,执行越久。火焰图的楼层每高一层,就是更深一级的函数被调用,最顶层的函数,是叶子函数。
总结而言,火焰图是调用栈分析的一种可视化方法。以常用的on-cpu火焰图为例:

  1. 火焰图工具官方github仓库

https://github.com/brendangregg/FlameGraph
在代码仓库里面,flamegraph.pl是生成最终火焰图的脚本,stackcollapse-xxx.pl是各种不同的工具生成的调用栈统计和处理工具,生成最终flamegraph.pl生成火焰图需要的文本。

  • stackcollapse.pl: for DTrace stacks

  • stackcollapse-perf.pl: for Linux perf_events “perf script” output

  • stackcollapse-pmc.pl: for FreeBSD pmcstat -G stacks

  • stackcollapse-stap.pl: for SystemTap stacks

  • stackcollapse-instruments.pl: for XCode Instruments

  • stackcollapse-vtune.pl: for Intel VTune profiles

  • stackcollapse-ljp.awk: for Lightweight Java Profiler

  • stackcollapse-jstack.pl: for Java jstack(1) output

  • stackcollapse-gdb.pl: for gdb(1) stacks

  • stackcollapse-go.pl: for Golang pprof stacks

  • stackcollapse-vsprof.pl: for Microsoft Visual Studio profiles

  • stackcollapse-wcp.pl: for wallClockProfiler output

  1. 火焰图分类
火焰图****类型横轴含义纵轴含义解决问题采样方式
cpu 火焰图cpu占用时间调用栈找出 cpu 占用高的问题函数;分析代码热路径固定频率采样 cpu 调用栈
off-cpu 火焰图阻塞时间调用栈i/o、网络等阻塞场景导致的性能下降;锁竞争、死锁导致的性能下降问题固定频率采样 阻塞事件调用栈
内存火焰图内存申请/释放函数调用次数调用栈内存泄露问题;内存占用高的对象/申请内存多的函数;虚拟内存或物理内存泄露问题有四种方式:跟踪malloc/free;跟踪brk;跟踪mmap;跟踪页错误
Hot/Cold 火焰图on-CPU 火焰图和 off-CPU 火焰图结合在一起综合展示调用栈需要结合 cpu 占用以及阻塞分析的场景;off-CPU 火焰图无法直观判断问题的场景on-CPU 火焰图和 off-CPU 火焰图结合
  1. 简单一张火焰图
# call.loga;b;c 12
a;d 3
b;c 3
z;d 5
a;c;e 3

前面是调用链,每个调用之间用 ; 隔开,每行后面的数字是调用栈出现的次数。
如上面的数据,用 flamegraph.pl 生成的火焰图如下图:

cat call.log | ./flamegraph.pl > calls-flame.svg

火焰图的交互

  1. 鼠标悬浮

火焰的每一层都会标注函数名,鼠标悬浮时会显示完整的函数名、抽样抽中的次数、占据总抽样次数的百分比。下面是一个例子。

  1. 点击放大

在某一层点击,火焰图会水平放大,该层会占据所有宽度,显示详细信息。
左上角会同时显示**“Reset Zoom”** ,点击该链接,图片就会恢复原样。

  1. 搜索

按下 Ctrl + F 会显示一个搜索框,用户可以输入关键词或正则表达式,所有符合条件的函数名会高亮显示。

如何理解火焰图

y 轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数。
x 轴表示抽样数,如果一个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多,一般与执行的时间长正相关。注意,x 轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的。
火焰图就是看顶层的哪个函数占据的宽度最大。只要有“平顶”plateaus,就表示该函数可能存在性能问题。
颜色没有特殊含义,因为火焰图表示的是 CPU 的繁忙程度,所以一般选择暖色调。

生成火焰图的方式

严格来说,指生成火焰图的调用栈关系的工具

perf命令

# 收集特定Pid
perf record -F 99 -p <PID> -g -- sleep 10perf record -F 99 -a -g -- sleep 60##perf record命令,会在当前目录生成perf.data记录采样的数据perf script -i /root/perf.data | ./stackcollapse-perf.pl --all |  ./flamegraph.pl > ksoftirqd.svg

BPF工具

  • profile
$ profile -af 10   >out-profile.txt
$ git clone https://github.com/brendangregg/FlameGraph
$ cd FlameGraph
$ ./flamegraph.pl  < ../out-profile.txt  > out-profile.svg/usr/share/bcc/tools/profile -h
usage: profile [-h] [-p PID | -L TID] [-U | -K] [-F FREQUENCY | -c COUNT] [-d][-a] [-I] [-f] [--stack-storage-size STACK_STORAGE_SIZE][-C CPU][duration]Profile CPU stack traces at a timed intervalpositional arguments:duration              duration of trace, in seconds...
[root@c2-1 FlameGraph]# ./flamegraph.pl <../out-profile.txt >../out-profile.svg
Can't locate open.pm in @INC (you may need to install the open module) (@INC contains: /usr/local/lib64/perl5/5.32 /usr/local/share/perl5/5.32 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5) at ./flamegraph.pl line 97.
BEGIN failed--compilation aborted at ./flamegraph.pl line 97.# 错误解决yum -y install perl-open

火焰图使用常见问题

采样失真

采样失真可认为是收集调用栈信息工具本身的问题,比如perf采样,是周期采样,-F参数指定了采样频率。如果某个任务运行周期与采样周期重叠,会造成采样失真问题。-F参数一般指定频率为99,既1s内采样99次,是因为内核的定时器频率一般是100HZ,为了避免潜在的采样重叠问题。

调用栈缺失

基于帧指针的调用栈回溯,一般RBP作为帧指针寄存器使用是惯常做法(x86_64体系)。gcc编译器默认不启用函数帧指针,而将RBP作为通用寄存器使用,这样就无法基于帧指针进行栈回溯。

  1. 过程调用示例
  • Caller 准备

    • 开辟 Callee 的栈帧空间

      • 帧指针*%ebp* 压栈,指向栈指针*%esp* 指向的位置

      • 栈指针向下移动一段距离

    • Caller 栈帧变量保存寄存器

    • 调用函数参数放入栈中

  • Callee 调用

    • 返回值通过*%eax* 传递
    • 调用结束回收空间(开辟Callee 栈帧的逆过程)
      在这里插入图片描述
      栈指针&& 帧指针详解(https://www.cnblogs.com/samo/articles/3092895.html)
  1. 为何调用栈缺失
man  gcc-fomit-frame-pointerDon't keep the frame pointer in a register for functions that don't need one.  This avoids the instructions tosave, set up and restore frame pointers; it also makes an extra register available in many functions.  It alsomakes debugging impossible on some machines.On some machines, such as the VAX, this flag has no effect, because the standard calling sequence automaticallyhandles the frame pointer and nothing is saved by pretending it doesn't exist.  The machine-description macro"FRAME_POINTER_REQUIRED" controls whether a target machine supports this flag.Starting with GCC version 4.6, the default setting (when not optimizing for size) for 32-bit GNU/Linux x86 and32-bit Darwin x86 targets has been changed to -fomit-frame-pointer.  The default can be reverted to-fno-omit-frame-pointer by configuring GCC with the --enable-frame-pointer configure option.Enabled at levels -O, -O2, -O3, -Os.
  1. 解决办法
    1. 对于C/C++软件和其他公共GCC编译软件,重新编译二进制,编译参数用 -fno-omit-frame-pointer选项。

    2. 对于Java,使用-XX:+PreserveFramePointer运行java。

函数名不显示

有些函数没有名字,编译器只用内存地址来表示(比如匿名函数)。
对于内核功能,perf 使用 /proc/kallsyms 文件中的信息将示例映射到其相应的功能名称或符号。对于用户空间中执行的功能,您可能会看到原始功能地址,因为二进制文件被剥离。

  • 解决办法

必须安装可执行文件的 debuginfo 软件包;如果可执行文件是本地开发的应用程序,则必须通过打开的调试信息(GCC 中的 -g 选项)编译,以显示这样的情形中的功能名称或符号。debuginfo中包含二进制文件的DWARF调试格式 。
DWARF全称Debugging With Attributed Record Formats,即带格式的调试信息属性。DWARF是ELF最常用的调试信息格式,它不一定与ELF相关,但两者是一起发展的,因此在开发中常一起使用。
DWARF格式中,高级语言的源文件、函数、变量、类型等调试信息在.debug_info节区中存储。.debug_info节区中,调试信息以节点的形式存在。节点可以存储一个源文件的调试信息、一个变量的调试信息、一个函数的调试信息等等。节点之间存在兄弟或父子的关系,一个源文件的调试信息节点形成一个调试信息树。

Demo

demo为sysbench进行cpu压测,将cpu负载跑满,然后通过perf找到cpu使用率百分百时运行的函数,并绘制对应的火焰图

  1. yum安装sysbench,运行sysbench,对cpu进行压测
sysbench cpu run --threads=4  --cpu-max-prime=100000000
  1. top显示cpu负载400%

在这里插入图片描述

  1. 获取一个压测线程,通过perf top -g -t <TID>
    在这里插入图片描述

  2. 默认情况下,Symbol显示的为地址,无法显示执行的函数。
    在这里插入图片描述

  3. 安装sysbench的debuginfo包

    用debuginfo-install命令安装sysbench的debuginfo包,debuginfo-install为yum-utils里面的一个工具

 yum -y install yum-utils
debuginfo-install sysbench[root@c1master1 ~]# rpm -qa |grep sysbench
sysbench-debuginfo-1.0.20-5.el8.x86_64
sysbench-1.0.20-5.el8.x86_64
sysbench-debugsource-1.0.20-5.el8.x86_64


检查sysbench-debuginfo的包,为一个ELF文件。

[root@c1master1 ~]# rpm -ql sysbench-debuginfo
/usr/lib/debug
/usr/lib/debug/.build-id
/usr/lib/debug/.build-id/32
/usr/lib/debug/.build-id/32/4d96c15e4392dfa5ae2b434139f541a12a88d6
/usr/lib/debug/.build-id/32/4d96c15e4392dfa5ae2b434139f541a12a88d6.debug
/usr/lib/debug/usr
/usr/lib/debug/usr/bin
/usr/lib/debug/usr/bin/sysbench-1.0.20-5.el8.x86_64.debug
[root@c1master1 ~]#
[root@c1master1 ~]# file /usr/lib/debug/usr/bin/sysbench-1.0.20-5.el8.x86_64.debug
/usr/lib/debug/usr/bin/sysbench-1.0.20-5.el8.x86_64.debug: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter \004, for GNU/Linux 3.2.0, BuildID[sha1]=324d96c15e4392dfa5ae2b434139f541a12a88d6, with debug_info, not stripped
  1. 安装完sysbench的debuginfo包之后,perf top已经显示函数名了,还可以对该函数进行进一步的注解。


perf top显示大量的时间运行的是cpu_execute_event函数。

  1. 通过readelf -s解析debuginfo这个elf文件的符号表,过滤到cpu_execute_event函数。
[root@c1master1 ~]# readelf  -s /usr/lib/debug/usr/bin/sysbench-1.0.20-5.el8.x86_64.debug |grep  cpu_execute_event953: 000000000001b7b0   200 FUNC    LOCAL  DEFAULT   15 cpu_execute_event
  1. 生成火焰图
# perf的-t参数指定线程的ID
[root@c2-1 ~]# perf record -F 99 -a -g -t 232036 -- sleep 30
Warning:
PID/TID switch overriding SYSTEM
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.196 MB perf.data (2970 samples) ]# 生成火焰图
perf  script -i /root/perf.data | ./stackcollapse-perf.pl --all |  ./flamegraph.pl >/root/sysbench-perf.svg

  1. 在sysbench的源码包中,搜索cpu_execute_event的信息。

    • 查看源码包内容
    [root@c1master1 ~]# rpm -ql sysbench-debugsource-1.0.20-5.el8.x86_64
    /usr/src/debug/sysbench-1.0.20-5.el8.x86_64
    /usr/src/debug/sysbench-1.0.20-5.el8.x86_64/config
    /usr/src/debug/sysbench-1.0.20-5.el8.x86_64/config/config.h
    /usr/src/debug/sysbench-1.0.20-5.el8.x86_64/src
    
    • 进入源码包目录,grep过滤cpu_execute_event关键字
    cd /usr/src/debug/sysbench-1.0.20-5.el8.x86_64grep -H -r -i -n "cpu_execute_event"  .
    ./src/tests/cpu/sb_cpu.c:46:static int cpu_execute_event(sb_event_t *, int);
    ./src/tests/cpu/sb_cpu.c:58:    .execute_event = cpu_execute_event,
    ./src/tests/cpu/sb_cpu.c:100:int cpu_execute_event(sb_event_t *r, int thread_id)
    

    在这里插入图片描述

    • 查看cpu_execute_event函数的源码,为一个求给定整数的素数的函数。

参考网站

https://www.brendangregg.com/flamegraphs.html


文章转载自:

http://kuYojl1L.tsLwz.cn
http://VImZBKL6.tsLwz.cn
http://pSMvykYl.tsLwz.cn
http://OylwNemp.tsLwz.cn
http://b7ZUK49u.tsLwz.cn
http://O1aWys0I.tsLwz.cn
http://NM63k9rk.tsLwz.cn
http://QFgJJMEd.tsLwz.cn
http://qSuinuG0.tsLwz.cn
http://mJYG7yML.tsLwz.cn
http://kw55niBN.tsLwz.cn
http://KzWQXHJi.tsLwz.cn
http://4xelEez6.tsLwz.cn
http://msGJUrpI.tsLwz.cn
http://8vhgTUyq.tsLwz.cn
http://U1siCbsT.tsLwz.cn
http://FcwnpZmS.tsLwz.cn
http://LhRNt9dg.tsLwz.cn
http://8AUDS3N5.tsLwz.cn
http://ZIaQ9fUG.tsLwz.cn
http://gopm4jJR.tsLwz.cn
http://h21kxmmc.tsLwz.cn
http://29GhZDxz.tsLwz.cn
http://5m47d724.tsLwz.cn
http://QDaVtxhd.tsLwz.cn
http://b3Gq1Ja4.tsLwz.cn
http://JYXelbaI.tsLwz.cn
http://SSPmBSqH.tsLwz.cn
http://steRlZCA.tsLwz.cn
http://7c3y0q01.tsLwz.cn
http://www.dtcms.com/a/371895.html

相关文章:

  • Linux:malloc背后的实现细节
  • Windows HDR 和 Nvidia HDR 关系
  • HarmonyOS应用开发:三层工程架构
  • Python学习——安装配置python环境+入门
  • IP校验和算法:从网络协议到SIMD深度优化
  • CentOS7 Hive2.3.8 安装图文教程
  • 如何利用 ChatGPT 辅助写作
  • 《从iptables到ipvs:云原生网络转发的性能拐点突破》
  • centos系统apache支持php配置
  • PyQt数字转大写金额GUI工具开发及财务规范实现
  • 家长沉迷游戏刷剧对儿童学习体验的影响:儿童教育心理学视角分析
  • 环状肽药物发现新路径:DNA 编码文库技术(DELT)的突破与挑战
  • 基于蚁群算法的量子电路调度研究(Matlab平台)
  • Photoshop图层间的关系
  • Axure RP 9 最新版安装包+安装步骤Win系统适用(附安装包)
  • 【PS2025全网最新版】稳定版PS2025保姆级下载安装详细图文教程(附安装包)(Adobe Photoshop)
  • FLINK:水位线的介绍
  • MySQL高级功能:窗口函数
  • 换手率及使用Python获取换手率数据
  • 炉米Lumi:字节跳动推出的AI图像模型分享社区
  • 计算机网络学习(六、应用层)
  • JavaSE 数组从入门到面试全解析
  • 游戏中的设计模式——第二篇 单例模式
  • 【论文阅读】自我进化的AI智能体综述
  • 系统分析师考试备考全面解析
  • 现代C++:C++和现代C++
  • 开始 ComfyUI 的 AI 绘图之旅-图生图之局部重绘(三)
  • 函数合集(1)
  • MySQL高可用方案解析:从复制到云原生
  • Python自学12 — 函数和模块