2019-08-18 21:45:39 +02:00
|
|
|
import path from 'path'
|
|
|
|
import fs from 'fs'
|
|
|
|
|
|
|
|
let inspector
|
|
|
|
try {
|
|
|
|
inspector = require('inspector')
|
|
|
|
} catch (e) {
|
|
|
|
console.log('Unable to CPU profile in < node 8.0')
|
|
|
|
}
|
|
|
|
|
|
|
|
class Profiler {
|
2019-11-11 04:24:53 +01:00
|
|
|
constructor(inspector) {
|
2019-08-18 21:45:39 +02:00
|
|
|
this.session = undefined
|
|
|
|
this.inspector = inspector
|
|
|
|
}
|
|
|
|
|
2019-11-11 04:24:53 +01:00
|
|
|
hasSession() {
|
2019-08-18 21:45:39 +02:00
|
|
|
return this.session !== undefined
|
|
|
|
}
|
|
|
|
|
2019-11-11 04:24:53 +01:00
|
|
|
startProfiling() {
|
2019-08-18 21:45:39 +02:00
|
|
|
if (this.inspector === undefined) {
|
|
|
|
return Promise.resolve()
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.session = new inspector.Session()
|
|
|
|
this.session.connect()
|
|
|
|
} catch (_) {
|
|
|
|
this.session = undefined
|
|
|
|
return Promise.resolve()
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.all([
|
|
|
|
this.sendCommand('Profiler.setSamplingInterval', {
|
2019-11-11 04:24:53 +01:00
|
|
|
interval: 100,
|
2019-08-18 21:45:39 +02:00
|
|
|
}),
|
|
|
|
this.sendCommand('Profiler.enable'),
|
2019-11-11 04:24:53 +01:00
|
|
|
this.sendCommand('Profiler.start'),
|
2019-08-18 21:45:39 +02:00
|
|
|
])
|
|
|
|
}
|
|
|
|
|
2019-11-11 04:24:53 +01:00
|
|
|
sendCommand(method, params) {
|
2019-08-18 21:45:39 +02:00
|
|
|
if (this.hasSession()) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
return this.session.post(method, params, (err, params) => {
|
|
|
|
if (err !== null) {
|
|
|
|
reject(err)
|
|
|
|
} else {
|
|
|
|
resolve(params)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
return Promise.resolve()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-11 04:24:53 +01:00
|
|
|
destroy() {
|
2019-08-18 21:45:39 +02:00
|
|
|
if (this.hasSession()) {
|
|
|
|
this.session.disconnect()
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.resolve()
|
|
|
|
}
|
|
|
|
|
2019-11-11 04:24:53 +01:00
|
|
|
stopProfiling() {
|
2019-08-18 21:45:39 +02:00
|
|
|
return this.sendCommand('Profiler.stop')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const { Tracer } = require('chrome-trace-event')
|
|
|
|
|
|
|
|
/**
|
|
|
|
* an object that wraps Tracer and Profiler with a counter
|
|
|
|
* @typedef {Object} Trace
|
|
|
|
* @property {Tracer} trace instance of Tracer
|
|
|
|
* @property {number} counter Counter
|
|
|
|
* @property {Profiler} profiler instance of Profiler
|
|
|
|
* @property {Function} end the end function
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string} outputPath The location where to write the log.
|
|
|
|
* @returns {Trace} The trace object
|
|
|
|
*/
|
|
|
|
export const createTrace = outputPath => {
|
|
|
|
const trace = new Tracer({
|
2019-11-11 04:24:53 +01:00
|
|
|
noStream: true,
|
2019-08-18 21:45:39 +02:00
|
|
|
})
|
|
|
|
const profiler = new Profiler(inspector)
|
|
|
|
if (/\/|\\/.test(outputPath)) {
|
|
|
|
const dirPath = path.dirname(outputPath)
|
2020-03-21 17:02:05 +01:00
|
|
|
fs.mkdirSync(dirPath, { recursive: true })
|
2019-08-18 21:45:39 +02:00
|
|
|
}
|
|
|
|
const fsStream = fs.createWriteStream(outputPath)
|
|
|
|
|
|
|
|
let counter = 0
|
|
|
|
|
|
|
|
trace.pipe(fsStream)
|
|
|
|
// These are critical events that need to be inserted so that tools like
|
|
|
|
// chrome dev tools can load the profile.
|
|
|
|
trace.instantEvent({
|
|
|
|
name: 'TracingStartedInPage',
|
|
|
|
id: ++counter,
|
|
|
|
cat: ['disabled-by-default-devtools.timeline'],
|
|
|
|
args: {
|
|
|
|
data: {
|
|
|
|
sessionId: '-1',
|
|
|
|
page: '0xfff',
|
|
|
|
frames: [
|
|
|
|
{
|
|
|
|
frame: '0xfff',
|
|
|
|
url: 'webpack',
|
2019-11-11 04:24:53 +01:00
|
|
|
name: '',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
2019-08-18 21:45:39 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
trace.instantEvent({
|
|
|
|
name: 'TracingStartedInBrowser',
|
|
|
|
id: ++counter,
|
|
|
|
cat: ['disabled-by-default-devtools.timeline'],
|
|
|
|
args: {
|
|
|
|
data: {
|
2019-11-11 04:24:53 +01:00
|
|
|
sessionId: '-1',
|
|
|
|
},
|
|
|
|
},
|
2019-08-18 21:45:39 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
return {
|
|
|
|
trace,
|
|
|
|
counter,
|
|
|
|
profiler,
|
|
|
|
end: callback => {
|
|
|
|
// Wait until the write stream finishes.
|
|
|
|
fsStream.on('finish', () => {
|
|
|
|
callback()
|
|
|
|
})
|
|
|
|
// Tear down the readable trace stream.
|
|
|
|
trace.push(null)
|
2019-11-11 04:24:53 +01:00
|
|
|
},
|
2019-08-18 21:45:39 +02:00
|
|
|
}
|
|
|
|
}
|