@nearform/doctor
Version:
Programmable interface to clinic doctor
99 lines (81 loc) • 2.83 kB
JavaScript
const async = require('async')
const endpoint = require('endpoint')
const stream = require('../lib/destroyable-stream')
const guessInterval = require('./guess-interval.js')
const analyseCPU = require('./analyse-cpu.js')
const analyseDelay = require('./analyse-delay.js')
const analyseMemory = require('./analyse-memory.js')
const analyseHandles = require('./analyse-handles.js')
const issueCategory = require('./issue-category.js')
class Analysis extends stream.Readable {
constructor (traceEventReader, processStatReader) {
super({ objectMode: true })
async.waterfall([
collectData.bind(null, traceEventReader, processStatReader),
analyseData
], this._done.bind(this))
}
_done (err, result) {
if (err) this.destroy(err)
this.push(result)
this.push(null)
}
_read () {
// will call push when all data is collected
}
}
function collectData (traceEventReader, processStatReader, callback) {
async.parallel({
traceEvent (done) {
traceEventReader.pipe(endpoint({ objectMode: true }, done))
},
processStat (done) {
processStatReader.pipe(endpoint({ objectMode: true }, done))
}
}, callback)
}
function analyseData ({ traceEvent, processStat }, callback) {
// guess the interval for where the benchmarker ran
const intervalIndex = guessInterval(processStat)
if (processStat.length < 2) {
const msg = 'Not enough data, try running a longer benchmark'
return callback(new Error(msg))
}
const intervalTime = [
processStat[intervalIndex[0]].timestamp,
processStat[intervalIndex[1] - 1].timestamp
]
const { processStatSubset, traceEventSubset } = subsetData(
traceEvent, processStat, intervalIndex, intervalTime
)
// Check for issues, the CPU analysis is async
analyseCPU(processStatSubset, traceEventSubset, function (err, cpuIssue) {
/* istanbul ignore if: it is very rare that HMM doesn't converge */
if (err) return callback(err)
const issues = {
delay: analyseDelay(processStatSubset, traceEventSubset),
cpu: cpuIssue,
memory: analyseMemory(processStatSubset, traceEventSubset),
handles: analyseHandles(processStatSubset, traceEventSubset)
}
const category = issueCategory(issues)
callback(null, {
'interval': intervalTime,
'issues': issues,
'issueCategory': category
})
})
}
function subsetData (traceEvent, processStat, intervalIndex, intervalTime) {
const processStatSubset = processStat.slice(...intervalIndex)
const traceEventSubset = []
for (const datum of traceEvent) {
if (datum.args.startTimestamp >= intervalTime[0] &&
datum.args.endTimestamp <= intervalTime[1]) {
traceEventSubset.push(datum)
}
}
return { processStatSubset, traceEventSubset }
}
module.exports = Analysis