mirror of
https://github.com/Unitech/pm2.git
synced 2025-12-08 20:35:53 +00:00
feat: add pm2 profile:cpu / pm2 profile:mem
This commit is contained in:
parent
2a01e5c4e3
commit
f57a6aeeed
1
.gitignore
vendored
1
.gitignore
vendored
@ -21,3 +21,4 @@ yarn.lock
|
||||
*.tar.gz
|
||||
e2e_time
|
||||
unit_time
|
||||
*.heapprofile
|
||||
|
||||
16
bin/pm2
16
bin/pm2
@ -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);
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
@ -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
|
||||
|
||||
that.Client.executeRemote('snapshotPM2', {
|
||||
pwd : file
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
111
lib/Daemon.js
111
lib/Daemon.js
@ -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()
|
||||
const inspector = require('inspector')
|
||||
var session = new inspector.Session()
|
||||
|
||||
session.connect()
|
||||
session.post('HeapProfiler.enable')
|
||||
|
||||
const chunks = []
|
||||
session.on('HeapProfiler.addHeapSnapshotChunk', (data) => {
|
||||
chunks.push(data.params.chunk)
|
||||
var timeout = msg.timeout || 5000
|
||||
|
||||
session.post(cmd.enable, (err, data) => {
|
||||
if (err) return cb(null, { error: err.message || err })
|
||||
|
||||
console.log(`Starting ${cmd.start}`)
|
||||
session.post(cmd.start, (err, data) => {
|
||||
if (err) return cb(null, { error: err.message || err })
|
||||
|
||||
setTimeout(() => {
|
||||
session.post(cmd.stop, (err, data) => {
|
||||
if (err) return cb(null, { error: err.message || err })
|
||||
const profile = data.profile
|
||||
|
||||
console.log(`Stopping ${cmd.stop}`)
|
||||
session.post(cmd.disable)
|
||||
|
||||
fs.writeFile(msg.pwd, JSON.stringify(profile), (err) => {
|
||||
if (err) return cb(null, { error: err.message || err })
|
||||
return cb(null, { file : msg.pwd })
|
||||
})
|
||||
})
|
||||
}, timeout)
|
||||
})
|
||||
})
|
||||
|
||||
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()
|
||||
|
||||
return cb(null, {file : msg.pwd});
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function startProfilingPM2(msg, cb) {
|
||||
if (profiler == null) {
|
||||
console.log('v8-profiler is not available');
|
||||
return cb(new Error('v8-profiler is not available'));
|
||||
}
|
||||
|
||||
profiler.startProfiling('cpu');
|
||||
|
||||
process.nextTick(function() {
|
||||
return cb(null, {msg : 'profiling started'});
|
||||
});
|
||||
}
|
||||
|
||||
function stopProfilingPM2(msg, cb) {
|
||||
if (profiler == null) {
|
||||
console.log('v8-profiler is not available');
|
||||
return cb(new Error('v8-profiler is not available'));
|
||||
}
|
||||
|
||||
var profile1 = profiler.stopProfiling('cpu');
|
||||
|
||||
profile1.export()
|
||||
.pipe(fs.createWriteStream(msg.pwd))
|
||||
.on('finish', function() {
|
||||
profile1.delete();
|
||||
return cb(null, {file : msg.pwd});
|
||||
});
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user