1 | 'use strict'
|
2 |
|
3 | const async = require('async')
|
4 | const endpoint = require('endpoint')
|
5 | const stream = require('../lib/destroyable-stream')
|
6 | const guessInterval = require('./guess-interval.js')
|
7 | const analyseCPU = require('./analyse-cpu.js')
|
8 | const analyseDelay = require('./analyse-delay.js')
|
9 | const analyseMemory = require('./analyse-memory.js')
|
10 | const analyseHandles = require('./analyse-handles.js')
|
11 | const issueCategory = require('./issue-category.js')
|
12 |
|
13 | class Analysis extends stream.Readable {
|
14 | constructor (systemInfoReader, traceEventReader, processStatReader) {
|
15 | super({ objectMode: true })
|
16 |
|
17 | async.waterfall([
|
18 | collectData.bind(null, systemInfoReader, traceEventReader, processStatReader),
|
19 | analyseData
|
20 | ], this._done.bind(this))
|
21 | }
|
22 |
|
23 | _done (err, result) {
|
24 | if (err) this.destroy(err)
|
25 | this.push(result)
|
26 | this.push(null)
|
27 | }
|
28 |
|
29 | _read () {
|
30 |
|
31 | }
|
32 | }
|
33 |
|
34 | function collectData (systemInfoReader, traceEventReader, processStatReader, callback) {
|
35 | async.parallel({
|
36 | systemInfo (done) {
|
37 | systemInfoReader.pipe(endpoint({ objectMode: true }, function (err, data) {
|
38 | if (err) return done(err)
|
39 | done(null, data[0])
|
40 | }))
|
41 | },
|
42 | traceEvent (done) {
|
43 | traceEventReader.pipe(endpoint({ objectMode: true }, done))
|
44 | },
|
45 | processStat (done) {
|
46 | processStatReader.pipe(endpoint({ objectMode: true }, done))
|
47 | }
|
48 | }, callback)
|
49 | }
|
50 |
|
51 | function analyseData ({ systemInfo, traceEvent, processStat }, callback) {
|
52 |
|
53 | const intervalIndex = guessInterval(processStat)
|
54 |
|
55 | if (processStat.length < 2) {
|
56 | return callback(null, {
|
57 | interval: [-Infinity, Infinity],
|
58 | issues: {
|
59 | delay: 'data',
|
60 | cpu: 'data',
|
61 | memory: {
|
62 | external: 'data',
|
63 | rss: 'data',
|
64 | heapTotal: 'data',
|
65 | heapUsed: 'data'
|
66 | },
|
67 | handles: 'data'
|
68 | },
|
69 | issueCategory: 'data'
|
70 | })
|
71 | }
|
72 |
|
73 | const intervalTime = [
|
74 | processStat[intervalIndex[0]].timestamp,
|
75 | processStat[intervalIndex[1] - 1].timestamp
|
76 | ]
|
77 |
|
78 | const { processStatSubset, traceEventSubset } = subsetData(
|
79 | traceEvent, processStat, intervalIndex, intervalTime
|
80 | )
|
81 |
|
82 |
|
83 | analyseCPU(systemInfo, processStatSubset, traceEventSubset, function (err, cpuIssue) {
|
84 |
|
85 | if (err) return callback(err)
|
86 |
|
87 | const issues = {
|
88 | delay: analyseDelay(systemInfo, processStatSubset, traceEventSubset),
|
89 | cpu: cpuIssue,
|
90 | memory: analyseMemory(systemInfo, processStatSubset, traceEventSubset),
|
91 | handles: analyseHandles(systemInfo, processStatSubset, traceEventSubset)
|
92 | }
|
93 |
|
94 | const category = issueCategory(issues)
|
95 |
|
96 | callback(null, {
|
97 | interval: intervalTime,
|
98 | issues: issues,
|
99 | issueCategory: category
|
100 | })
|
101 | })
|
102 | }
|
103 |
|
104 | function subsetData (traceEvent, processStat, intervalIndex, intervalTime) {
|
105 | const processStatSubset = processStat.slice(...intervalIndex)
|
106 | const traceEventSubset = []
|
107 | for (const datum of traceEvent) {
|
108 | if (datum.args.startTimestamp >= intervalTime[0] &&
|
109 | datum.args.endTimestamp <= intervalTime[1]) {
|
110 | traceEventSubset.push(datum)
|
111 | }
|
112 | }
|
113 |
|
114 | return { processStatSubset, traceEventSubset }
|
115 | }
|
116 |
|
117 | module.exports = Analysis
|