内容提要
本文介绍如何读懂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火焰图。