UNPKG

@oaklean/profiler

Version:

A library to measure the energy consumption of your javascript/typescript code

203 lines 16 kB
"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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