Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node.js 性能分析之火焰图 #57

Open
xizhibei opened this issue Sep 9, 2017 · 2 comments
Open

Node.js 性能分析之火焰图 #57

xizhibei opened this issue Sep 9, 2017 · 2 comments
Labels

Comments

@xizhibei
Copy link
Owner

xizhibei commented Sep 9, 2017

往往,在我们的开发过程的以下两个场景中,我们会需要去分析应用的性能。

  1. 开发时候优化程序;
  2. 线上排查问题;

往往,如果不是线上出了问题,我们不会想要去进行性能分析 :P,所以其实你只会面对第二个场景。话说回来,其实这两个场景的相关处理方式也非常类似,我接下来会做个总结,当然了,得先从介绍相关的工具开始。

Node.js 性能分析工具

主要的分析对象就是内存以及 CPU,而最著名的几个工具莫过于 v8-profiler 以及 flamegraph,当然了,从 Node.js 4.4.0 开始,Node.js 自带了分析工具。其它的,可以看 这里

对于 Node.js 自带的分析工具:

  1. 启动相关应用的时候,node 需要带上 --prof 参数;
  2. 再做相关的性能测试,结束后,能在 node 运行目录下生成 isolate-0xnnnnnnnnnnnn-v8.log 的文件;
  3. 然后再运行 node --prof-process isolate-0xnnnnnnnnnnnn-v8.log > processed.txt
  4. 最后打开 processed.txt 文件,就能看到相关的结果了;

显然,它只能做 CPU 分析。

而 v8-profiler 两者都能做: CPU profiler 以及分析 heap,heap 的快照跟比较也不在话下。

不过,它需要进行一些编码操作,目前我在开发过程中尝试过以下两种方法:

Linux SIGUSR1 信号

const fs = require('fs');
const profiler = require('v8-profiler');

let profileData;
let startProfile = false;

process.on('SIGUSR1', () => {
    if (startProfile) {
        console.log('Dumping data...');
        profileData = profiler.stopProfiling();
        console.log(profileData.getHeader());
        return profileData.export((err, result) => {
            if (err) {
                console.log(err);
            } else {
                fs.writeFileSync('profileData.cpuprofile', result)
                console.log('Dumping data done');
            }
            profileData.delete();
            profileData = null;
            startProfile = false;
        });
    }
    console.log('Start profile...');
    profiler.startProfiling('p1');
    startProfile = true;
})

使用 kill -s SIGUSR1 PID 开始采集数据,再运行一次结束并生成 cpuprofile 文件。

API 接口

比如,你在调试一个基于 express 的 web 应用:

const profiler = require('v8-profiler');

app.get('/profile', (req, res) => {
  const duration = req.query.duration || 30;
  profiler.startProfiling('profile sample');
  setTimeout(() => {
    const profileData = profiler.stopProfiling();
    console.log(profileData.getHeader());
    return profileData.export((err, result) => {
       if (err) {
           console.log(err);
       } else {
           fs.writeFileSync('profileData.cpuprofile', result)
           console.log('Dumping data done');
       }
       profileData.delete();
       res.send('Done');
   }, duration * 1000);
  })
});

显然,当你在 Windows 下的时候,你只能选择第二种,需要注意的是:假如你的应用是 CPU 密集型,当出现 100% CPU 的时候,有可能你是得不到 cpuprofile 文件的,具体为什么,当做你的思考题 :P。

不管怎么做,v8-profiler 提供的方式还是非常灵活的,生成的文件是一个 json 文件,这个文件可以直接导入 chrome 的 JavaScript profiler 去查看,也可以利用 flamegraph 这个工具来生成火焰图。

火焰图

提到火焰图,就不得不提这个项目:brendangregg/FlameGraph,现在网络上几乎你能看到的火焰图都是用这个工具来生成的。

npm flamegraph

而 node 也有一个 flamegraph 包可以用来帮助你更简单处理(显然,它是基于上面这个项目进一步开发的。):

npm install flamegraph -g
flamegraph -t cpuprofile -f profileData.cpuprofile.cpuprofile -o fg.svg

生成的 svg 文件可以直接在浏览器中查看,这个图的看法简单介绍下:


图片来自上面的 FlameGraph 项目。

  1. X 轴不表示时间刻度,而表示时间长度,也就是占用的 CPU 时间;
  2. Y 轴从上到下,越接近底部表示越接近系统的底层,对于 Node.js 来说,上层是 js 代码,下层是 C 代码;
  3. 每一层上,每个条幅越宽,表示它在整个运行周期中,占用的 CPU 越多;
  4. 点击任何一个条幅,图像会自动将它置于最底部,同时只留下它之上的条幅,并且放大至全图;

一般来说,你只需要注意标示出你程序相关的最宽条幅即可,因为往往它就是程序中存在的瓶颈。

Linux perf

另外,Node.js 其实还有另外一个参数 --perf-basic-prof,可以配合 Linux 上的 perf 或者 systemtap 工具来画火焰图。

限于篇幅,这里只简单说下 perf:

  1. 安装 perf: yum install perf -y
  2. 下载 Flamegraph: git clone https://github.com/brendangregg/FlameGraph
  3. 首先启动应用: node --perf-basic-prof app.js,这步操作会生成一个 /tmp/perf-PID.map 的文件;
  4. 确认应用的 pid,然后运行 perf,采集 60s 的数据:perf record -F 99 -p PID -g -- sleep 60
  5. 修改 map 文件权限:chown root /tmp/perf-PID.map,然后你才能继续下一步 perf script > out.perf,这一步就是会读取 /tmp/perf-PID.map 这个文件,才能生成 out.perf 文件;
  6. 最后就可以使用 Flamegraph 来生成火焰图了:./FlameGraph/stackcollapse-perf.pl --kernel < ./out.perf | ./FlameGraph/flamegraph.pl --color=js --hash> ./fg.svg

这个图与上面生成的图是类似的,其中需要说明下的是 map 文件是用来将 Node.js 程序内的代码映射到内存地址空间。显然, perf 这个工具只能获取 Node.js 进程内部的内存地址,毕竟 js 是个解释性的语言。

顺便提一句,/tmp/perf-PID.map 这个文件会不断增长,如果你是在线上排查在本地无法复现的问题,你可能需要个大点的硬盘了。

Ref:

@warjiang
Copy link

warjiang commented Oct 5, 2018

并不能看到Nodejs执行的函数

@xizhibei
Copy link
Owner Author

xizhibei commented Oct 6, 2018

并不能看到Nodejs执行的函数

这篇文章有些过时了,可以看看这个比较方便的工具:https://github.com/mapbox/flamebearer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants