@acromedia/sloth
Version:
Resource profiler for node, utilizing child processes
97 lines (96 loc) • 3.4 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const child_process_1 = __importDefault(require("child_process"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const ProfileResults_1 = __importDefault(require("./ProfileResults"));
class Profiler {
/**
* Class for profiling a process, provided a PID.
*
* @param {Number} pid
* The process PID.
*
* @param {Object} opts
* Profiler options.
*
* @param {Boolean=} opts.toFile
* Whether to spit out the output into a JSON file.
*
* @param {Number=} opts.timestep
* The amount of time in milliseconds before each memory check.
*
* @param {Number=} opts.waitAfterEnd
* The amount of time to wait after the profiler has been stopped.
*
* @param {Boolean=} opts.trimNodeProcessUsage
* Trim base node process usage from tracked usage.
*/
constructor(pid, opts = {}) {
this.toWatch = pid;
this.results = null;
this.process = null;
if (opts && typeof opts !== 'object')
throw new Error('Provided options was not an object.');
// Optional params
this.toFile = opts.toFile || false;
this.timestep = opts.timestep || 100;
this.wait = opts.waitAfterEnd || 0;
this.trimNodeProcessUsage = opts.trimNodeProcessUsage || true;
}
/**
* Starts the watching process by spawning a fork of the monitoring file.
*/
async start() {
let execArgv = [];
let procPath = path_1.default.join(__dirname, '../helpers/watch.js');
if (!fs_1.default.existsSync(procPath)) {
procPath = procPath.replace('.js', '.ts');
execArgv = ['-r', 'ts-node/register'];
}
this.process = child_process_1.default.fork(procPath, [
this.toWatch,
this.timestep,
this.wait,
this.toFile,
this.trimNodeProcessUsage,
], {
execArgv,
});
// Setup our message handler for when the process sends the data.
this.process.on('message', (message) => {
this.results = message;
// Kill the watcher process (if it hasn't already).
if (this.process)
this.process.kill();
this.process = null;
});
// Ensure process can get an accurate baseline by waiting a few cycles before beginning.
return new Promise((resolve) => {
setTimeout(() => {
resolve(this);
}, this.timestep * 3);
});
}
/**
* Kills the monitoring fork process, returns data.
*/
end() {
// Send 'stop' message which will give us our data.
// Once we have the data, we can safely kill the process.
if (this.process)
this.process.send('stop');
return new Promise((resolve) => {
const intr = setInterval(() => {
if (!this.process) {
clearInterval(intr);
resolve(new ProfileResults_1.default(this.results));
}
}, 100);
});
}
}
exports.default = Profiler;