使用perf_events在nodejs/v8火焰图中出现未知事件

11
我尝试使用Linux perf_events来进行一些Node.js的性能分析,如Brendan Gregg在这里所述。工作流程如下:
  1. 使用--perf-basic-prof运行版本大于0.11.13的Node.js,它会创建/tmp/perf-(PID).map文件,其中写入了JavaScript符号映射。
  2. 使用perf record -F 99 -p `pgrep -n node` -g -- sleep 30命令捕获堆栈信息。
  3. 使用存储库中的stackcollapse-perf.pl脚本来折叠堆栈信息。
  4. 使用flamegraph.pl脚本生成svg火焰图。
我得到了以下结果(开始看起来非常好): enter image description here 问题是有很多[unknown]元素,我认为它们应该是我的Node.js函数调用。我假设整个过程在第3步失败了,在那里应该使用由带有--perf-basic-prof标志的node/v8执行生成的映射来折叠perf数据。在Node.js执行期间,会创建/tmp/perf-PID.map文件,并将其中一些映射写入其中。
如何解决这个问题?
我正在使用CentOS 6.5 x64,并已经尝试了Node.js 0.11.13、0.11.14(预构建版本和编译版本),但都没有成功。

这里提供了一个解决方案:链接 - user14717
2个回答

20
首先,"[unknown]"的意思是采样器无法确定函数的名称,因为它是系统或库函数。如果是这样,那就没关系 - 你不用在意,因为你要找的是与时间有关的代码,而不是系统代码。
实际上,我建议这是XY问题之一。即使你得到了直接回答你所问的问题,也很可能没有什么用处。以下是原因:
1. CPU分析对于I/O绑定程序几乎没有用处
你火焰图左侧的两座塔正在进行I/O操作,因此它们的墙上时间可能比右侧的大堆要长得多。如果这个火焰图是从墙上时间采样中得出的,而不是CPU时间采样中得出的,它可能看起来更像下面的第二张图,告诉你时间实际上去了哪里:

enter image description here

右边看起来很重要的一堆东西现在缩小了,不再那么重要。另一方面,I/O塔非常宽。如果你的代码中有任何一个宽橙色条纹,那就是节省大量时间的机会,如果可以避免一些I/O。
2. 无论程序是CPU密集型还是I/O密集型,加速机会都可能隐藏在火焰图中。
假设有一些函数Foo真的在浪费资源,如果你知道了这个问题,就可以解决它。假设在火焰图中,它是深红色的。假设它从代码中的许多地方被调用,因此它不会在火焰图中的一个位置上集中显示。相反,它出现在多个小地方,如黑色轮廓所示:

enter image description here

注意,如果收集所有这些矩形,你会发现它占据了11%的时间,这意味着值得关注。 如果能将其时间缩短一半,你可以总体节省5.5%的时间。 如果它所做的实际上可以完全避免,你可以总体节省11%的时间。 每个小矩形都会缩小到零,并将其右侧的图形一起拉动。
现在我将展示我使用的方法。我随机取样适量的堆栈样本,并检查每个样本以查找可能加速的例程。 这相当于在火焰图中进行如下采样:

enter image description here

细长的垂直线代表20个随机时间堆栈样本。 您可以看到,其中三个标有X。 这些是通过Foo的内容。 这大约是正确的数字,因为11%乘以20是2.2。
(感到困惑?好吧,这里有一点概率知识。如果你抛硬币20次,并且它有11%的机会出现正面,你会得到多少个正面?从技术上讲,这是一个二项式分布。您最有可能得到的数字是2,下一个最有可能的数字是1和3。(如果您只得到1,则会继续进行,直到获得2个)。这是分布图:)

enter image description here

你需要取样18.2次才能看到两个Foo

看那20个样本可能有些令人生畏,因为它们运行在20到50个级别之间。 然而,你基本上可以忽略所有不是你的代码的代码。 只检查它们是否包含你的代码。 你将精确地看到你花费时间的方式, 并且你会有一个非常粗略的测量结果。 深度堆栈既是坏消息又是好消息 - 它们意味着代码很可能有很多加速的空间,并且它们向你展示了这些空间。

如果你看到任何可以加速的东西,如果你在超过一个样本中看到它,它将为你提供健康的加速,保证。 你需要在超过一个样本中看到它的原因是,如果你只在一个样本中看到它,你只知道它的时间不为零。如果你在超过一个样本中看到它,你仍然不知道它需要多少时间,但你知道它不小。 以下是统计数据。


如果任务是I/O绑定的,Kamil Z可以尝试关闭CPU分析。Gregg刚刚发布了脚本- https://dev59.com/0WAg5IYBdhLWcg3w1uDy#28784580 此外,获取“随机堆栈样本”(例如使用gdb,甚至使用perf record -g,然后使用perf script来抓取原始堆栈样本,然后获取几个随机堆栈进行手动检查)可能无法帮助解决问题,因为1)很难附加到短暂的程序(在这种情况下,使用LTTng跟踪可能有所帮助...)2)gdb(或perf record)仍然不会报告符号名称,仍然是Kamil提出的Y问题。内部node.js/v8调试器可能有助于解决这些问题。 - osgx
@MikeDunlavey 第一点对我来说不太清楚。这个问题难道不仅发生在正在进行性能分析的线程与产生样本的线程相同的情况下吗?换句话说,第一个图形未显示I/O等待时间的原因是它正在等待,无法记录任何样本。另一方面,如果另一个线程正在产生样本,它将捕获目标线程的所有等待和处理时间,并生成第二张图形。 - Hadi Brais
@Hadi:你说的很有道理(虽然采样线程几乎一直被阻塞,直到下一次采样时间)。然而,性能分析工具制造商仍然对此感到困惑。他们认为必须区分在-CPU和离-CPU时间,因此在采样时,只有当被采样的线程处于在-CPU或离-CPU状态时才进行采样。我怀疑这种混淆是因为他们将自己的主要任务视为精确测量,而不是寻找加速。 - Mike Dunlavey
@ceeaspb:你说得对。我只是习惯了人们问“XY问题”。也就是说,他们真正想要的是获得最大的性能,但他们并没有这么说 - 他们问一些周边问题。 - Mike Dunlavey
“1. CPU Profiling is of little use in an I/O bound program” 可以通过 off-cpu profiling 来解决,对吗? - skytree
显示剩余4条评论

11

一般来说,与主题专家意见不同是一个坏主意,但(最尊敬的)我们开始吧!

SO建议回答以下问题:

"请确保回答问题。提供详细信息并分享您的研究!"

所以问题是,至少我对它的理解是,为什么perf脚本输出中有[未知]帧(如何将这些[未知]帧转换为有意义的名称)?这个问题可能是关于“如何改善我的系统性能?”但在这种情况下,我不这样看。这里真正存在一个问题,即perf记录数据已被后处理。

答案是,虽然先决条件设置正确:正确的节点版本,存在正确的参数以生成函数名称(--perf-basic-prof),但生成的perf映射文件必须由root拥有 ,perf script才能产生预期的输出。

就是这样!

今天写一些新脚本时,我发现了这个指向SO问题的链接。

这里还有一些额外的参考资料:

https://yunong.io/2015/11/23/generating-node-js-flame-graphs/

https://github.com/jrudolph/perf-map-agent/blob/d8bb58676d3d15eeaaf3ab3f201067e321c77560/bin/create-java-perf-map.sh#L22

[非root文件有时可以被强制执行] http://www.spinics.net/lists/linux-perf-users/msg02588.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接