@oaklean/profiler
Version:
A library to measure the energy consumption of your javascript/typescript code
203 lines • 16 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PowerMetricsSensorInterface = void 0;
const fs = __importStar(require("fs"));
const child_process_1 = require("child_process");
const plist_1 = __importDefault(require("plist"));
const profiler_core_1 = require("@oaklean/profiler-core");
const BaseSensorInterface_1 = require("../BaseSensorInterface");
/**
* This SensorInterface uses the data provided by the powermetrics command line tool.
* This Provider can only be used on Mac OS
*
* Man Page to powermetrics:
* https://www.unix.com/man-page/osx/1/powermetrics/
*/
class PowerMetricsSensorInterface extends BaseSensorInterface_1.BaseSensorInterface {
constructor(options, debugOptions) {
super();
this._executable = 'powermetrics';
this._options = options;
this._commandLineArgs = [
'--sample-rate', this._options.sampleInterval.toString(),
'--buffer-size', '0',
'--show-process-energy',
'-f',
'plist',
'-o',
this._options.outputFilePath
];
if (debugOptions !== undefined) {
this._startTime = debugOptions.startTime;
this._stopTime = debugOptions.stopTime;
this._couldBeExecuted = true;
}
}
type() {
return profiler_core_1.SensorInterfaceType.powermetrics;
}
canBeExecuted() {
return new Promise((resolve) => {
try {
const childProcess = (0, child_process_1.spawn)(this._executable, {
detached: false,
stdio: 'pipe'
});
let isExecutable = false;
childProcess.on('error', () => {
resolve(false);
});
childProcess.stderr.on('data', () => {
childProcess.kill('SIGTERM');
isExecutable = false;
});
childProcess.stdout.on('data', () => {
childProcess.kill('SIGTERM');
isExecutable = true;
});
childProcess.on('exit', () => {
resolve(isExecutable);
});
}
catch (_a) {
resolve(false);
}
});
}
isRunning() {
var _a;
return ((_a = this._childProcess) === null || _a === void 0 ? void 0 : _a.pid) !== undefined && BaseSensorInterface_1.BaseSensorInterface.pidIsRunning(this._childProcess.pid);
}
static runningInstances() {
try {
const result = (0, child_process_1.execSync)('pgrep -ix powermetrics', { encoding: 'utf-8' });
return result.trim().split('\n');
}
catch (error) {
return [];
}
}
readSensorValues(pid) {
return __awaiter(this, void 0, void 0, function* () {
if (!(yield this.couldBeExecuted())) {
return undefined;
}
let tries = 0;
while (this.isRunning() && tries < 10) {
profiler_core_1.LoggerHelper.error(`Cannot read sensor values, wait for process to exit: ${tries + 1}, try again after 1 second`);
tries += 1;
yield profiler_core_1.TimeHelper.sleep(1000);
}
if (this.startTime === undefined || this.stopTime === undefined) {
throw new Error('PowerMetricsSensorInterface.readSensorValues: start or stop time could not be determined');
}
if (!fs.existsSync(this._options.outputFilePath) || !(yield this.canBeExecuted())) {
return new profiler_core_1.MetricsDataCollection(pid, profiler_core_1.MetricsDataCollectionType.PowerMetricsPerProcess, [], {
startTime: this.startTime,
stopTime: this.stopTime
});
}
const content = fs.readFileSync(this._options.outputFilePath).toString();
const contents = content.split('\x00');
const data = contents.map((content) => new profiler_core_1.PowerMetricsData(plist_1.default.parse(content)));
return new profiler_core_1.MetricsDataCollection(pid, profiler_core_1.MetricsDataCollectionType.PowerMetricsPerProcess, data, {
startTime: this.startTime,
stopTime: this.stopTime
});
});
}
get startTime() {
return this._startTime;
}
get stopTime() {
return this._stopTime;
}
startProfiling() {
return __awaiter(this, void 0, void 0, function* () {
if (!(yield this.couldBeExecuted())) {
return;
}
if (fs.existsSync(this._options.outputFilePath)) {
fs.unlinkSync(this._options.outputFilePath); // remove output file to ensure clean measurements
}
if (PowerMetricsSensorInterface.runningInstances().length > 0) {
throw new Error('PowerMetricsSensorInterface.startProfiling: PowerMetrics instance already running, close it before taking any measurements');
}
this._startTime = profiler_core_1.TimeHelper.getCurrentHighResolutionTime();
this._childProcess = (0, child_process_1.spawn)(this._executable, [...this._commandLineArgs], {
detached: true
});
this.cleanExit = () => {
if (this._childProcess) {
this._childProcess.kill('SIGTERM');
}
};
process.on('exit', this.cleanExit); // add event listener to close powermetrics if the parent process exits
// detach from current node.js process
this._childProcess.unref();
yield profiler_core_1.TimeHelper.sleep(1000 + this._options.sampleInterval); // wait to ensure measurements started, since the measurements only starts at full seconds
});
}
stopProfiling() {
return __awaiter(this, void 0, void 0, function* () {
if (!(yield this.couldBeExecuted())) {
return;
}
if (this._childProcess === undefined) {
return;
}
yield profiler_core_1.TimeHelper.sleep(1000 + this._options.sampleInterval); // wait to capture last measurement
this._childProcess.kill('SIGIO'); // flush all buffered output
this._stopTime = profiler_core_1.TimeHelper.getCurrentHighResolutionTime();
this._childProcess.kill('SIGTERM');
let seconds = 0;
while (this.isRunning()) {
if (seconds > 10) {
throw new Error('Waited 10 seconds for powermetrics to shut down, it is still running');
}
yield profiler_core_1.TimeHelper.sleep(1000);
seconds++;
}
if (this.cleanExit !== undefined) {
process.removeListener('exit', this.cleanExit); // clean up event listener
}
});
}
}
exports.PowerMetricsSensorInterface = PowerMetricsSensorInterface;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUG93ZXJNZXRyaWNzU2Vuc29ySW50ZXJmYWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2ludGVyZmFjZXMvcG93ZXJtZXRyaWNzL1Bvd2VyTWV0cmljc1NlbnNvckludGVyZmFjZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLHVDQUF3QjtBQUN4QixpREFBNkQ7QUFFN0Qsa0RBQXlCO0FBQ3pCLDBEQVUrQjtBQUUvQixnRUFBNEQ7QUFFNUQ7Ozs7OztHQU1HO0FBR0gsTUFBYSwyQkFBNEIsU0FBUSx5Q0FBbUI7SUFXbkUsWUFBWSxPQUE0QyxFQUFFLFlBR3pEO1FBQ0EsS0FBSyxFQUFFLENBQUE7UUFDUCxJQUFJLENBQUMsV0FBVyxHQUFHLGNBQWMsQ0FBQTtRQUNqQyxJQUFJLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQTtRQUN2QixJQUFJLENBQUMsZ0JBQWdCLEdBQUc7WUFDdkIsZUFBZSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsY0FBYyxDQUFDLFFBQVEsRUFBRTtZQUN4RCxlQUFlLEVBQUUsR0FBRztZQUNwQix1QkFBdUI7WUFDdkIsSUFBSTtZQUNKLE9BQU87WUFDUCxJQUFJO1lBQ0osSUFBSSxDQUFDLFFBQVEsQ0FBQyxjQUFjO1NBQzVCLENBQUE7UUFDRCxJQUFJLFlBQVksS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUNoQyxJQUFJLENBQUMsVUFBVSxHQUFHLFlBQVksQ0FBQyxTQUFTLENBQUE7WUFDeEMsSUFBSSxDQUFDLFNBQVMsR0FBRyxZQUFZLENBQUMsUUFBUSxDQUFBO1lBQ3RDLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxJQUFJLENBQUE7UUFDN0IsQ0FBQztJQUNGLENBQUM7SUFFRCxJQUFJO1FBQ0gsT0FBTyxtQ0FBbUIsQ0FBQyxZQUFZLENBQUE7SUFDeEMsQ0FBQztJQUVELGFBQWE7UUFDWixPQUFPLElBQUksT0FB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