Skip to content

Commit

Permalink
feat: add pm2 profile:cpu / pm2 profile:mem
Browse files Browse the repository at this point in the history
  • Loading branch information
Unitech committed Feb 18, 2019
1 parent 2a01e5c commit f57a6ae
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 105 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ yarn.lock
*.tar.gz
e2e_time
unit_time
*.heapprofile
16 changes: 8 additions & 8 deletions bin/pm2
Original file line number Diff line number Diff line change
Expand Up @@ -373,19 +373,19 @@ commander.command('scale <app_name> <number>')
//
// snapshot PM2
//
commander.command('snapshot')
.description('snapshot PM2 memory')
.action(function() {
pm2.snapshotPM2();
commander.command('profile:mem [time]')
.description('Sample PM2 heap memory')
.action(function(time) {
pm2.profile('mem', time);
});

//
// snapshot PM2
//
commander.command('profile <command>')
.description('profile CPU')
.action(function(command) {
pm2.profilePM2(command);
commander.command('profile:cpu [time]')
.description('Profile PM2 cpu')
.action(function(time) {
pm2.profile('cpu', time);
});

//
Expand Down
64 changes: 24 additions & 40 deletions lib/API/Extra.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,64 +168,48 @@ module.exports = function(CLI) {
return cb(null, pids);
})
}

/**
* Create PM2 memory snapshot
* @method getVersion
* @callback cb
*/
CLI.prototype.snapshotPM2 = function(cb) {
CLI.prototype.profile = function(type, time, cb) {
var that = this;
var moment = require('moment');
var file = path.join(process.cwd(), moment().format('dd-HH:mm:ss') + '.heapsnapshot');
var cmd

if (type == 'cpu') {
cmd = {
ext: '.cpuprofile',
action: 'profileCPU'
}
}
if (type == 'mem') {
cmd = {
ext: '.heapprofile',
action: 'profileMEM'
}
}

var file = path.join(process.cwd(), moment().format('dd-HH:mm:ss') + cmd.ext);
time = time || 10000

that.Client.executeRemote('snapshotPM2', {
pwd : file
console.log(`Starting ${cmd.action} profiling for ${time}ms...`)
that.Client.executeRemote(cmd.action, {
pwd : file,
timeout: time
}, function(err) {
if (err) {
console.error(err);
return that.exitCli(1);
}
console.log('Heapdump in %s', file);
console.log(`Profile done in ${file}`)
return cb ? cb.apply(null, arguments) : that.exitCli(cst.SUCCESS_EXIT);
});
};


/**
* Create PM2 memory snapshot
* @method getVersion
* @callback cb
*/
CLI.prototype.profilePM2 = function(command, cb) {
var that = this;
var moment = require('moment');
var file = path.join(process.cwd(), moment().format('dd-HH:mm:ss') + '.cpuprofile');

if (command == 'start') {
that.Client.executeRemote('profileStart', {
}, function(err) {
if (err) {
console.error(err);
return that.exitCli(1);
}
console.log('CPU profiling started, type pm2 profile stop once finished');
return cb ? cb.apply(null, arguments) : that.exitCli(cst.SUCCESS_EXIT);
});
}
else if (command == 'stop') {
that.Client.executeRemote('profileStop', {
pwd : file
}, function(err) {
if (err) {
console.error(err);
return that.exitCli(1);
}
console.log('CPU profile in %s', file);
return cb ? cb.apply(null, arguments) : that.exitCli(cst.SUCCESS_EXIT);
});
}
};

/**
* Description
* @method sendLineToStdin
Expand Down
103 changes: 46 additions & 57 deletions lib/Daemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,80 +151,69 @@ Daemon.prototype.innerStart = function(cb) {
that.sendReady(cb);
});

var profiler;

try {
profiler = semver.satisfies(process.version, '>= 10.0.0') ? require('inspector') : null;
} catch(e) {
profiler = null;
}

/**
* Memory Snapshot
*/
function snapshotPM2(msg, cb) {
if (profiler == null) {
console.log('Heap snapshot is not available (node 10+)');
return cb(new Error('Heap snapshot is not available (node 10+)'));
function profile(type, msg, cb) {
if (semver.satisfies(process.version, '< 8'))
return cb(null, { error: 'Node.js is not on right version' })

var cmd

if (type === 'cpu') {
cmd = {
enable: 'Profiler.enable',
start: 'Profiler.start',
stop: 'Profiler.stop',
disable: 'Profiler.disable'
}
}
if (type == 'mem') {
cmd = {
enable: 'HeapProfiler.enable',
start: 'HeapProfiler.startSampling',
stop: 'HeapProfiler.stopSampling',
disable: 'HeapProfiler.disable'
}
}

const session = new profiler.Session()
session.connect()
session.post('HeapProfiler.enable')

const chunks = []
session.on('HeapProfiler.addHeapSnapshotChunk', (data) => {
chunks.push(data.params.chunk)
})

session.post('HeapProfiler.takeHeapSnapshot', {
reportProgress: false
}, (err, data) => {
if (err) return cb(err)

fs.writeFile(msg.pwd, chunks.join(''), function() {
session.post('Profiler.disable')
session.disconnect()
const inspector = require('inspector')
var session = new inspector.Session()

return cb(null, {file : msg.pwd});
});
})
}
session.connect()

function startProfilingPM2(msg, cb) {
if (profiler == null) {
console.log('v8-profiler is not available');
return cb(new Error('v8-profiler is not available'));
}
var timeout = msg.timeout || 5000

profiler.startProfiling('cpu');
session.post(cmd.enable, (err, data) => {
if (err) return cb(null, { error: err.message || err })

process.nextTick(function() {
return cb(null, {msg : 'profiling started'});
});
}
console.log(`Starting ${cmd.start}`)
session.post(cmd.start, (err, data) => {
if (err) return cb(null, { error: err.message || err })

function stopProfilingPM2(msg, cb) {
if (profiler == null) {
console.log('v8-profiler is not available');
return cb(new Error('v8-profiler is not available'));
}
setTimeout(() => {
session.post(cmd.stop, (err, data) => {
if (err) return cb(null, { error: err.message || err })
const profile = data.profile

var profile1 = profiler.stopProfiling('cpu');
console.log(`Stopping ${cmd.stop}`)
session.post(cmd.disable)

profile1.export()
.pipe(fs.createWriteStream(msg.pwd))
.on('finish', function() {
profile1.delete();
return cb(null, {file : msg.pwd});
});
fs.writeFile(msg.pwd, JSON.stringify(profile), (err) => {
if (err) return cb(null, { error: err.message || err })
return cb(null, { file : msg.pwd })
})
})
}, timeout)
})
})
}

server.expose({
killMe : that.close.bind(this),
snapshotPM2 : snapshotPM2,
profileStart : startProfilingPM2,
profileStop : stopProfilingPM2,
profileCPU : profile.bind(this, 'cpu'),
profileMEM : profile.bind(this, 'mem'),
prepare : God.prepare,
getMonitorData : God.getMonitorData,
getSystemData : God.getSystemData,
Expand Down

0 comments on commit f57a6ae

Please sign in to comment.