火焰图详解:如何读懂 CPU 火焰图,找出烧掉 CPU 的代码

火焰图详解:如何读懂 CPU 火焰图,找出烧掉 CPU 的代码

💡 原文中文,约10400字,阅读约需25分钟。
📝

内容提要

本文介绍如何读懂CPU火焰图:宽度代表采样占比而非时间,y轴为调用深度,顶边(或底边)是CPU实际执行位置,x轴按字母序排列。通过PHP、Go、Erlang三个真实案例展示从火焰图定位瓶颈代码行的方法,并给出各技术栈生成火焰图的工具及四步排查流程。

🔎

延伸解读

火焰图不是时间线

许多初学者误以为火焰图的x轴代表时间顺序,但实际是按字母序排列,目的是合并相同调用栈。因此,并排的方框并不表示先后关系。理解这一点是正确读图的关键,否则容易将字母序误读为执行顺序,导致错误判断。

self与total的区分

火焰图中每个方框有self和total两个指标:self是函数自身在栈顶的采样占比,total包含其所有子调用。self高的函数才是真正消耗CPU的,而total高但self低的通常是调度器或框架层。排查时应优先关注self最高的叶函数,而非整体最宽的方框。

跨语言级别验证诊断

三个案例均展示了应用层火焰图与C语言级火焰图指向同一瓶颈时,诊断更为可靠。例如PHP案例中,PHP级显示preg_match占56.2%,C级显示pcre2_match_8占34.7%,两者吻合。这种交叉验证能减少误判,增强对根因的信心。

Q&A

CPU火焰图的四条基本规则是什么?

四条规则是:1. 宽度代表函数在采样中的占比,不是持续时间;2. y轴是调用深度,底部是入口,顶部是CPU实际执行的叶函数;3. 顶边(或底边,取决于渲染方向)是on-CPU的位置;4. x轴按字母序排列,不是时间轴。

如何通过火焰图定位CPU瓶颈?

看火焰图的叶函数一端(经典方向在顶边,根在上的方向在底边),找到self占比最高的方框,那就是直接烧CPU的函数。然后往下读调用链,了解它为什么被调用。

火焰图中的self和total有什么区别?

total是方框的完整宽度,包含它调用的所有函数;self是该函数位于栈顶部的采样占比,即没有更上层被调用者。分析时应该按self排序,self最高的函数才是CPU真正在跑的。

火焰图有哪些常见形态?分别代表什么?

常见形态有:Plateau(宽而平的顶部)表示一个函数独占CPU;Tower(高而窄的尖刺)表示深调用栈但频率低,通常不是问题;Hair(顶边大量细小的毛刺)表示短暂调用,如中断,如果很宽可能是中断风暴;底座平坦、顶部参差表示框架健康,热点在应用层。

火焰图和flame chart有什么区别?

火焰图(flame graph)的x轴按字母序排列,不是时间轴,相同的函数名会合并成一个更宽的方框;而flame chart的x轴是时间轴,水平位置表示先后顺序,相同的函数名可能出现在多个互不合并的方框里。

如何为不同技术栈生成火焰图?

通用步骤是采集调用栈采样、折叠、渲染。工具因技术栈而异:Linux用perf,Go用pprof,Java用async-profiler,Node.js用0x,PHP用Excimer,Perl用Devel::NYTProf,Erlang用eflame,Rust用cargo-flamegraph。

从100% CPU到定位代码行的四步流程是什么?

第1步:用top或htop找到烧CPU的进程,记下PID;第2步:用profiler从该PID采集调用栈采样,生成火焰图;第3步:读叶函数一端,找到最宽的方框(self最高),悬停或点击查看源文件和行号;第4步:打开文件,跳到那一行,结合调用栈理解为什么被调用。

火焰图中的颜色有含义吗?

在Brendan Gregg的原始火焰图中,颜色是随机的暖色调,没有语义含义。一些工具会按模块或语言级别分配颜色,需要查看工具是否有图例。OpenResty XRay的火焰图用橙色表示CPU火焰图,蓝色表示off-CPU火焰图。

🏷️

标签

➡️

继续阅读