unix-mem-monitor
Version:
Provides tools to monitor memory usage on UNIX systems without ps for Lambda
144 lines (118 loc) • 4.63 kB
JavaScript
const EventEmitter = require('events');
const { getPageSize, getChildProcesses, getPIDstatm } = require('./util');
const { DEFAULT_POLLRATE } = require('./constants');
/**
* Validates the given params for MemMonitor
* @param {object} params
*/
const validateParams = (params) => {
if (typeof params !== 'object') {
throw 'Argument must be of type object';
}
if (!params.PID && !params.PIDs) {
throw 'Argument must have property PID or PIDs'
}
if (params.PID && typeof params.PID !== 'string' && typeof params.PID !== 'number') {
throw 'PID must be a string or number';
}
if (params.PIDs && !Array.isArray(params.PIDs)) {
throw 'PIDs must be an array'
}
if (params.pollrate && typeof params.pollrate !== 'number') {
throw 'pollrate must be a number';
}
};
class MemMonitor extends EventEmitter {
/**
* Construct a new instance of MemMonitor
* @param {object} params
* @property {number|string} [params.PID] PID to monitor, at least one of PID or PIDs must be specified
* @property {Array} [params.PIDs] PIDs to monitor, at least one of PID or PIDs must be specified
* @property {number} [params.pollrate] How often MemMonitor should scan processes (in ms)
* @property {boolean} [params.monitorChildProcesses] Whether MemMonitor should automatically monitor child processes or not
*/
constructor(params) {
super();
validateParams(params);
this.params = params;
this.PIDs = (params.PIDs) ? params.PIDs : [];
if (params.PID) this.PIDs.push(params.PID);
this.intervals = []; // Holds the polling tasks that go over /proc to get info on memory
this.childProcesses = []; // Holds the current list of child processes
this.pollrate = (params.pollrate) ? params.pollrate : DEFAULT_POLLRATE; // How often MemMonitor should get new info from /proc
this.memoryData = {};
}
/**
* Sets up MemMonitor and starts polling data
*/
async init () {
this.pageSize = await getPageSize();
if (this.params.monitorChildProcesses && this.params.monitorChildProcesses === true) {
this.childProcesses = await getChildProcesses();
this.intervals.push(setInterval(this.updateChildProcesses, this.pollrate, this));
}
this.intervals.push(setInterval(this.updateMemoryData, this.pollrate, this));
}
/**
* Add new PIDs or a single PID for MemMonitor to watch
* @param {Array|number|string} PIDsToAdd
*/
addPIDs (PIDsToAdd) {
const PIDsToAddIsArray = Array.isArray(PIDsToAdd);
if (PIDsToAddIsArray || typeof PIDsToAdd === 'string' || typeof PIDsToAdd === 'number') {
if (PIDsToAddIsArray) this.PIDs.concat(PIDsToAdd);
if (!PIDsToAddIsArray) {
const PIDasInt = parseInt(PIDsToAdd);
if (!isNaN(PIDasInt)) {
this.PIDs.push(PIDasInt);
}
}
} else {
throw "Argument must be of type array, string or number"
}
}
/**
* Go over the PIDs set to be monitored and get their memory info
*/
async updateMemoryData(_this) {
_this = _this || this;
const newMemoryData = {};
for (const PID of _this.getPIDsToMonitor()) {
newMemoryData[PID] = await getPIDstatm(PID, _this.pageSize);
}
_this.memoryData = newMemoryData;
_this.emit('data', _this.memoryData);
}
/**
* Check which child processes exist for this process
*/
async updateChildProcesses(_this) {
_this = _this || this;
_this.childProcesses = await getChildProcesses();
}
/**
* Generate a reduced version of the memory data
*/
getMergedMemoryData() {
return Object.values(this.memoryData).reduce((previous, current) => {
for (const property in current) {
previous[property] += current[property];
}
return previous;
});
}
/**
* Get the PIDs to monitor
*/
getPIDsToMonitor() {
return this.PIDs.concat(this.childProcesses).filter((v, i, a) => a.indexOf(v) === i);
}
/**
* Destroy MemMonitor
*/
kill() {
this.removeAllListeners();
this.intervals.forEach((interval) => clearInterval(interval));
}
}
module.exports = MemMonitor;