150 lines
5.9 KiB
JavaScript
150 lines
5.9 KiB
JavaScript
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
const electron_1 = require("electron");
|
||
|
const memoize = require("memoizee");
|
||
|
// @ts-ignore: no declaration file
|
||
|
const pidtree = require("pidtree");
|
||
|
// @ts-ignore: no declaration file
|
||
|
const pidusage = require("pidusage");
|
||
|
const rxjs_1 = require("rxjs");
|
||
|
const extractURLDomain_1 = require("./extractURLDomain");
|
||
|
exports.getAppUsage = (pid) => {
|
||
|
return pidtree(pid, { root: true })
|
||
|
.then(pidusage)
|
||
|
.then((usages) => Object.values(usages).filter(Boolean));
|
||
|
};
|
||
|
let getSharedProcessMetricsPollerByPid = (pid, samplingInterval) => rxjs_1.Observable.timer(0, samplingInterval)
|
||
|
.map(() => rxjs_1.Observable.fromPromise(exports.getAppUsage(pid)))
|
||
|
.mergeAll()
|
||
|
.share();
|
||
|
getSharedProcessMetricsPollerByPid = memoize(getSharedProcessMetricsPollerByPid);
|
||
|
let getSharedProcessMetricsPollerByApp = (app, samplingInterval) => rxjs_1.Observable.timer(0, samplingInterval)
|
||
|
.map(() => app.getAppMetrics())
|
||
|
.share();
|
||
|
getSharedProcessMetricsPollerByApp = memoize(getSharedProcessMetricsPollerByApp);
|
||
|
/**
|
||
|
* Returns an Observable that emits Electron.ProcessMetric[] on a regular interval.
|
||
|
*
|
||
|
* For a given `app` and a given `samplingInterval`, the returned observable is shared
|
||
|
* for performance reasons.
|
||
|
*
|
||
|
* options.samplingInterval = 1000 (1s) by default
|
||
|
* @param app
|
||
|
* @param options
|
||
|
*/
|
||
|
exports.onProcessMetrics = (app, options) => {
|
||
|
options = Object.assign({ samplingInterval: 1000 }, options);
|
||
|
return getSharedProcessMetricsPollerByApp(app, options.samplingInterval);
|
||
|
};
|
||
|
/**
|
||
|
* Returns an Rx.Observable that emits `PidUsage[]` every `options.samplingInterval` ms.
|
||
|
*
|
||
|
* For a given `pid` and a given `samplingInterval`, the returned observable is shared
|
||
|
* for performance reasons.
|
||
|
* `pid` is the root of the process tree.
|
||
|
*
|
||
|
* @param pid
|
||
|
* @param {object} options
|
||
|
* @param {number} options.samplingInterval - 1000 (1s) by default
|
||
|
*
|
||
|
* @example
|
||
|
* - pid: main process
|
||
|
* - rendererPid1: renderer process
|
||
|
* - rendererPid2: renderer process
|
||
|
*/
|
||
|
exports.onProcessTreeMetricsForPid = (pid, options) => {
|
||
|
options = Object.assign({ samplingInterval: 1000 }, options);
|
||
|
return getSharedProcessMetricsPollerByPid(pid, options.samplingInterval);
|
||
|
};
|
||
|
const getExtendedAppMetrics = (appMetrics) => {
|
||
|
const allWebContents = electron_1.webContents.getAllWebContents();
|
||
|
const webContentsInfo = allWebContents.map((wc) => ({
|
||
|
type: wc.getType(),
|
||
|
id: wc.id,
|
||
|
pid: wc.getOSProcessId(),
|
||
|
URL: wc.getURL(),
|
||
|
URLDomain: extractURLDomain_1.default(wc.getURL()),
|
||
|
}));
|
||
|
return appMetrics.map(proc => {
|
||
|
const report = proc;
|
||
|
const wc = webContentsInfo.find(wc => wc.pid === proc.pid);
|
||
|
if (!wc)
|
||
|
return report;
|
||
|
report.webContents = [wc];
|
||
|
return report;
|
||
|
});
|
||
|
};
|
||
|
/**
|
||
|
* Returns an Rx.Observable that emits reports of `ExtendedProcessMetric`
|
||
|
* every `options.samplingInterval` ms.
|
||
|
*
|
||
|
* Default `options.samplingInterval` = 1000ms
|
||
|
*
|
||
|
* Compared to `onProcessMetrics` it adds data on the `webContents` associated
|
||
|
* to the given process.
|
||
|
*
|
||
|
* @param app the electron app instance
|
||
|
* @param options
|
||
|
*/
|
||
|
exports.onExtendedProcessMetrics = (app, options = {}) => exports.onProcessMetrics(app, options).map(getExtendedAppMetrics);
|
||
|
/**
|
||
|
* Will emit an array of `PidUsage` when a process of the tree exceeds the
|
||
|
* `options.percentCPUUsageThreshold` on more than `options.samplesCount`
|
||
|
* samples.
|
||
|
* It monitors the whole tree of pids, starting from `childPid`.
|
||
|
* The reason behind this is that the `process.pid` of the main process is at the same
|
||
|
* level as all renderers.
|
||
|
* So we fetch their common ancestor, which is the `ppid` of the main process.
|
||
|
* The parent pid of `childPid` is not part of the end result
|
||
|
* (that way, we monitor the same processes as `getAppMetrics`).
|
||
|
*
|
||
|
* In opposite to onExcessiveCPUUsage, onExcessiveCPUUsageInProcessTree does not use
|
||
|
* Electron's internal measurement but rather use `pidusage`, a cross-platform
|
||
|
* process cpu % and memory usage of a PID. It is known to have lower pressure on CPU.
|
||
|
* Also, as this leverage `pidusage`, the measures on Windows can be considered
|
||
|
* as not accurate.
|
||
|
*
|
||
|
* Default `options.samplesCount` = 10
|
||
|
* Default `options.percentCPUUsageThreshold` = 80
|
||
|
*
|
||
|
* @param pid - the pid of the main process
|
||
|
* @param options
|
||
|
*/
|
||
|
exports.onExcessiveCPUUsageInProcessTree = (pid, options) => {
|
||
|
options = Object.assign({ samplesCount: 10, percentCPUUsageThreshold: 80 }, options);
|
||
|
return exports.onProcessTreeMetricsForPid(pid, options)
|
||
|
.map(appUsage => rxjs_1.Observable.from(appUsage))
|
||
|
.mergeAll()
|
||
|
.groupBy(appUsage => appUsage.pid)
|
||
|
.map(g => g.bufferCount(options.samplesCount))
|
||
|
.mergeAll()
|
||
|
.filter(processMetricsSamples => {
|
||
|
const excessiveSamplesCount = processMetricsSamples.filter(p => p.cpu >= options.percentCPUUsageThreshold).length;
|
||
|
return excessiveSamplesCount === processMetricsSamples.length;
|
||
|
});
|
||
|
};
|
||
|
/**
|
||
|
* Will emit an array `ExtendedProcessMetric` when a process exceeds the
|
||
|
* `options.percentCPUUsageThreshold` on more than `options.samplesCount`
|
||
|
* samples.
|
||
|
*
|
||
|
* Default `options.samplesCount` = 10
|
||
|
* Default `options.percentCPUUsageThreshold` = 80
|
||
|
*
|
||
|
* @param app the electron app instance
|
||
|
* @param options
|
||
|
*/
|
||
|
exports.onExcessiveCPUUsage = (app, options) => {
|
||
|
options = Object.assign({ samplesCount: 10, percentCPUUsageThreshold: 80 }, options);
|
||
|
return exports.onExtendedProcessMetrics(app, options)
|
||
|
.map(report => rxjs_1.Observable.from(report))
|
||
|
.mergeAll()
|
||
|
.groupBy(processMetric => processMetric.pid)
|
||
|
.map(g => g.bufferCount(options.samplesCount))
|
||
|
.mergeAll()
|
||
|
.filter(processMetricsSamples => {
|
||
|
const excessiveSamplesCount = processMetricsSamples.filter(p => p.cpu.percentCPUUsage >= options.percentCPUUsageThreshold).length;
|
||
|
return excessiveSamplesCount == processMetricsSamples.length;
|
||
|
});
|
||
|
};
|
||
|
//# sourceMappingURL=index.js.map
|