诊断报告

功能描述

Node.js 性能平台的诊断功能,大都从某一个特定角度,例如针对内存问题的堆快照,针对CPU问题的Profiling,通过一定时间的信息采集来协助定位问题。

诊断报告则从一个全局的视角抓住进程的瞬时状态,采集了堆栈,系统资源,平台信息等以协助故障定位。尤其是其中的JavaScriptStack可以协助精确定位长正则和死循环故障。

各个分支开始支持的版本:

  • 2.x: v2.5.2

  • 3.x: v3.11.8

  • 4.x: v4.3.0

注意:此功能需要您的 agenthub/egg-alinode 依赖的 commandx 版本 >= v1.5.3,否则会失败。

操作指南

在实例页抓取性能数据,或者数据趋势页,点击诊断报告后,以json格式输出当前应用运行时状态,协助定位故障。

ab

下面列举一些对定位问题协助较大的输出项,具体可以实际操作生成诊断报告后查看。

  • JavaScriptStack:JS 栈,当前js代码运行栈

  • NativeStack:当前c++运行栈

  • JsHeapGcInfo:堆上各个空间信息

  • resource:进程资源信息和eventloop资源信息

  • libuvHandleSummary:libuvHandle信息

  • system:系统环境变量,资源限制,动态链接库等

  • FileName:诊断报告全路径

  • NodeJsVersion:对应的Node.js版本

  • alinodeVersion:Node.js 性能平台运行时版本

  • execPath:运行时二进制文件路径

在线分析

文件 页面等待诊断报告生成后,点击 转储 按钮后,可以将您的应用生成的对应诊断报告文件转储至云端,此时点击 分析 按钮可以实现在线的分析:

diag_ananysis

新打开的在线分析页面会给出当前诊断报告对应的 JavaScript 栈Native 栈堆内分布Libuv 句柄系统详情 等信息,具体我们看一看下面的应用示例。

应用示例

  • 复制 demo.js 代码到服务器/本地保存为demo.js后运行(这里假设 agenthub 已经运行)。

$node demos.js

http listen at 8080 pid:  26542
  • 在浏览器打开 http://[您的服务器地址]:8080 。

  • 选择测试 Trigger Long Running Regular Expression 或者 Trigger Dead Loop JS function 进入长正则或者死循环中。

  • 然后根据上面的pid去Node.js性能平台上点击 诊断报告 ,然后到 文件 页面转储后继续点击 分析 进行在线分析定位。

首先可以看到最有用的 JavaScript 栈 信息:

js-stack

这里进程显然是阻塞在 regexpCase() 函数调用中,查看对应的 18 行 23 列可以看到是里面的 replace 操作由于触发了异常的正则回溯导致的进程全局阻塞。

剩余的一些信息的展示可以帮助您更精准地定位到问题,如下图所示

  • Native 栈信息

native-stack
  • 堆内分布

heap
  • Libuv 句柄信息

libuv
  • 系统详情信息(包含了进程当前的 ENV、系统软硬资源限制和共享库等信息)

system

其他说明

  • 诊断报告参考了node-report的功能

  • 可以通过环境变量DIAGNOSTIC_EVENTS = fatalerrorexception或者fatalerror+exception来使能遇到FatalError或者Exception时自动生成诊断报告。

demo.js 代码

'use strict';
const http = require('http');

const regexpCase = function() {
  let str = '<br/>                                             ' +
    '           早餐后自由活动,于指定时间集合自行办理退房手续。';
  str += '<br/>                                      <br/>' +
    '                                        <br/>           ' +
    '                         <br/>';
  str += '                                    <br/>' +
    '                                                        ' +
    '                                                        ' +
    '        <br/>';
  str += '                                                <br/>                                                                                                                <br/>';
  str += '                                                     ' +
    '                                                        ' +
    '       根据船班时间,自行前往暹粒机场,返回中国。<br/>';
  str += '如需送机服务,需增加280/每单。<br/>';

  let r = String(str).replace(/(^(\s*?<br[\s\/]*?>\*?)+|(\s*?<br[\s\/]*?>\s*?)+?$)/igm, '');
};


const findSomething = function(from) {
  // find something from 'from'
  return [];
};

const deadLoopCase = function() {
  let selected = [];
  let curArr = ['item1', 'item2', 'item3'];
  let arr = [];

  while(selected.length < curArr.length) {
    const dp = findSomething(curArr)[0];
    //if (typeof dp === 'undefined') {
    //  break;
    //}
    if (selected.indexOf(dp) === -1) {
      selected.push(dp);
    } else {
      arr = arr.filter(val => {
        return val != dp
      })
    }
  }
};


http.createServer( (req, res) => {
  console.log(req.url);

  switch(req.url) {
    case '/regexp': {
      regexpCase();
      res.end('regexp case');
    } break;
    case '/deadloop': {
      deadLoopCase();
      res.end('deadloop case');
    } break;
    case '/exit': {
      process.exit(0);
    } break;
    default: {
      res.writeHead(200, "OK",{'Content-Type': 'text/html'});
      res.write('<html><head><title>Node.js</title></head><body style="font-family:arial;">');
      res.write('<h2> Regular Expression and Dead Loop Detection Example</h2>');

      res.write('<p>Click on button below to trigger long time running regexp test.');
      res.write('<form enctype="application/x-www-form-urlencoded" action="/regexp" method="post">');
      res.write('<button>Trigger Long Running Regular Expression</button></form>');

      res.write('<p>Click on button below to enter JavaScript dead loop test');
      res.write('<form enctype="application/x-www-form-urlencoded" action="/deadloop" method="post">');
      res.write('<button>Trigger Dead Loop JS function</button></form>');

      res.write('<p>The test can be terminated only before the above cases triggered.');
      res.write('<form enctype="application/x-www-form-urlencoded" action="/exit" method="post">');
      res.write('<button>Exit Test</button></form>');
      res.write('</form></body></html');
      res.end();
    } break;
  }
}).listen(8080);

console.log('\nhttp listen at 8080 pid: ', process.pid);