@oaklean/profiler
Version:
A library to measure the energy consumption of your javascript/typescript code
297 lines • 30.1 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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__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.Profiler = void 0;
const fs = __importStar(require("fs"));
const seedrandom_1 = __importDefault(require("seedrandom"));
const profiler_core_1 = require("@oaklean/profiler-core");
const NodeInspectorProfiler_1 = require("./helper/NodeInspectorProfiler");
const TraceEventHelper_1 = require("./helper/TraceEventHelper");
const PowerMetricsSensorInterface_1 = require("./interfaces/powermetrics/PowerMetricsSensorInterface");
const PerfSensorInterface_1 = require("./interfaces/perf/PerfSensorInterface");
const WindowsSensorInterface_1 = require("./interfaces/windows/WindowsSensorInterface");
class Profiler {
constructor(subOutputDir) {
this.subOutputDir = subOutputDir;
this.config = profiler_core_1.ProfilerConfig.autoResolve();
this.loadSensorInterface();
this._externalResourceHelper = new profiler_core_1.ExternalResourceHelper(this.config.getRootDir());
this.exportAssetHelper = new profiler_core_1.ExportAssetHelper(this.config.getOutDir().join(this.subOutputDir || ''));
}
static getSensorInterface(config) {
const sensorInterfaceType = config.getSensorInterfaceType();
switch (sensorInterfaceType) {
case profiler_core_1.SensorInterfaceType.powermetrics: {
const options = config.getSensorInterfaceOptions();
if (options === undefined) {
throw new Error('Profiler.getSensorInterface: sensorInterfaceOptions are not defined');
}
options.outputFilePath = config
.getOutDir()
.join(options.outputFilePath)
.toPlatformString();
return new PowerMetricsSensorInterface_1.PowerMetricsSensorInterface(options);
}
case profiler_core_1.SensorInterfaceType.perf: {
const options = config.getSensorInterfaceOptions();
if (options === undefined) {
throw new Error('Profiler.getSensorInterface: sensorInterfaceOptions are not defined');
}
options.outputFilePath = config
.getOutDir()
.join(options.outputFilePath)
.toPlatformString();
return new PerfSensorInterface_1.PerfSensorInterface(options);
}
case profiler_core_1.SensorInterfaceType.windows: {
const options = config.getSensorInterfaceOptions();
if (options === undefined) {
throw new Error('Profiler.getSensorInterface: sensorInterfaceOptions are not defined');
}
options.outputFilePath = config
.getOutDir()
.join(options.outputFilePath)
.toPlatformString();
return new WindowsSensorInterface_1.WindowsSensorInterface(options);
}
case undefined:
return undefined;
}
}
loadSensorInterface() {
this._sensorInterface = Profiler.getSensorInterface(this.config);
}
static inject(subOutputDir) {
return __awaiter(this, void 0, void 0, function* () {
const profiler = new Profiler(subOutputDir);
const title = new Date().getTime().toString();
const exitResolve = () => resolve('exit');
const sigIntResolve = () => resolve('SIGINT');
const sigUsr1Resolve = () => resolve('SIGUSR1');
const sigUsr2Resolve = () => resolve('SIGUSR2');
let stopped = false;
function resolve(origin) {
return __awaiter(this, void 0, void 0, function* () {
if (!stopped) {
stopped = true;
profiler_core_1.LoggerHelper.appPrefix.log('Finish Measurement, please wait...');
yield profiler.finish(title);
process.removeListener('exit', exitResolve);
process.removeListener('SIGINT', sigIntResolve);
process.removeListener('SIGUSR1', sigUsr1Resolve);
process.removeListener('SIGUSR2', sigUsr2Resolve);
if (origin !== 'exit') {
process.exit();
}
}
});
}
profiler_core_1.LoggerHelper.appPrefix.log('Measurement started');
yield profiler.start(title);
process.on('exit', exitResolve);
// //catches ctrl+c event
process.on('SIGINT', sigIntResolve);
// // catches "kill pid" (for example: nodemon restart)
process.on('SIGUSR1', sigUsr1Resolve);
process.on('SIGUSR2', sigUsr2Resolve);
return profiler;
});
}
start(title, executionDetails) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const performance = new profiler_core_1.PerformanceHelper();
performance.start('Profiler.start');
const outFileReport = this.exportAssetHelper.outputReportPath(title);
const outDir = outFileReport.dirName();
performance.start('Profiler.start.createOutDir');
if (!fs.existsSync(outDir.toPlatformString())) {
profiler_core_1.PermissionHelper.mkdirRecursivelyWithUserPermission(outDir);
}
performance.stop('Profiler.start.createOutDir');
performance.start('Profiler.start.seedRandom');
const mathRandomSeed = this.config.getSeedForMathRandom();
if (mathRandomSeed) {
(0, seedrandom_1.default)(mathRandomSeed, { global: true });
}
performance.stop('Profiler.start.seedRandom');
if (executionDetails) {
this.executionDetails = executionDetails;
}
else {
performance.start('Profiler.start.resolveExecutionDetails');
this.executionDetails = yield profiler_core_1.ExecutionDetails.resolveExecutionDetails();
performance.stop('Profiler.start.resolveExecutionDetails');
}
performance.start('Profiler.start.sensorInterface.couldBeExecuted');
if (this._sensorInterface !== undefined &&
!(yield this._sensorInterface.couldBeExecuted())) {
// remove sensor interface from execution details since it cannot be executed
this.executionDetails.runTimeOptions.sensorInterface = undefined;
profiler_core_1.LoggerHelper.appPrefix.warn('Warning: ' +
'Sensor Interface can not be executed, no energy measurements will be collected');
}
performance.stop('Profiler.start.sensorInterface.couldBeExecuted');
performance.start('Profiler.start.sensorInterface.startProfiling');
yield ((_a = this._sensorInterface) === null || _a === void 0 ? void 0 : _a.startProfiling());
performance.stop('Profiler.start.sensorInterface.startProfiling');
performance.start('Profiler.start.getV8CPUSamplingInterval');
yield NodeInspectorProfiler_1.NodeInspectorProfiler.setSamplingInterval(this.config.getV8CPUSamplingInterval()); // sets the sampling interval in microseconds
performance.stop('Profiler.start.getV8CPUSamplingInterval');
performance.start('TraceEventHelper.startCapturingProfilerTracingEvents');
yield TraceEventHelper_1.TraceEventHelper.startCapturingProfilerTracingEvents();
performance.stop('TraceEventHelper.startCapturingProfilerTracingEvents');
performance.start('Profiler.start.externalResourceHelper.connect');
yield this._externalResourceHelper.connect();
yield this._externalResourceHelper.listen();
performance.stop('Profiler.start.externalResourceHelper.connect');
// wait for the first sensor interface measurement
if (this._sensorInterface !== undefined &&
(yield this._sensorInterface.couldBeExecuted())) {
yield ((_b = this._sensorInterface) === null || _b === void 0 ? void 0 : _b.measurementStarted());
}
// title - handle to stop profile again
// recsampels(boolean) - record samples, if false no cpu times will be captured
performance.start('Profiler.start.startProfiling');
yield NodeInspectorProfiler_1.NodeInspectorProfiler.startProfiling();
performance.stop('Profiler.start.startProfiling');
performance.stop('Profiler.start');
performance.printReport('Profiler.start');
performance.exportAndSum(this.exportAssetHelper.outputPerformancePath());
});
}
finish(title, highResolutionStopTime) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const highResolutionStopTimeToUse = highResolutionStopTime !== undefined
? highResolutionStopTime.toString()
: profiler_core_1.TimeHelper.getCurrentHighResolutionTime().toString();
const performance = new profiler_core_1.PerformanceHelper();
performance.start('Profiler.finish');
if (this.executionDetails === undefined) {
throw new Error('Profiler.finish: Profiler was not started yet');
}
performance.start('Profiler.finish.stopProfiling');
const profile = yield NodeInspectorProfiler_1.NodeInspectorProfiler.stopProfiling();
performance.stop('Profiler.finish.stopProfiling');
performance.start('TraceEventHelper.stopTraceEventSession');
yield TraceEventHelper_1.TraceEventHelper.stopTraceEventSession();
performance.stop('TraceEventHelper.stopTraceEventSession');
performance.start('Profiler.finish.sensorInterface.stopProfiling');
yield ((_a = this._sensorInterface) === null || _a === void 0 ? void 0 : _a.stopProfiling());
performance.stop('Profiler.finish.sensorInterface.stopProfiling');
const CPUProfilerBeginTime = (BigInt(yield TraceEventHelper_1.TraceEventHelper.getCPUProfilerBeginTime()) * BigInt(1000));
const highResolutionBeginTimeToUse = CPUProfilerBeginTime.toString();
const cpuProfile = {
nodes: profile.nodes,
startTime: profile.startTime,
endTime: profile.endTime,
samples: profile.samples,
timeDeltas: profile.timeDeltas
};
const outFileCPUProfile = this.exportAssetHelper.outputCPUProfilePath(title);
const outFileExternalResourceHelper = this.exportAssetHelper.outputExternalResourceHelperPath(title);
const outFileReport = this.exportAssetHelper.outputReportPath(title);
const outFileMetricsDataCollection = this.exportAssetHelper.outputMetricsDataCollectionPath(title);
if (this.config.shouldExportV8Profile()) {
performance.start('Profiler.finish.exportV8Profile');
yield profiler_core_1.CPUProfileHelper.storeToFile(cpuProfile, outFileCPUProfile);
performance.stop('Profiler.finish.exportV8Profile');
}
performance.start('Profiler.finish.sensorInterface.readSensorValues');
const metricsDataCollection = yield ((_b = this._sensorInterface) === null || _b === void 0 ? void 0 : _b.readSensorValues(process.pid));
performance.stop('Profiler.finish.sensorInterface.readSensorValues');
const executionDetailsFull = Object.assign(Object.assign({}, this.executionDetails), { highResolutionBeginTime: highResolutionBeginTimeToUse, highResolutionStopTime: highResolutionStopTimeToUse });
const rootDir = this.config.getRootDir();
const report = new profiler_core_1.ProjectReport(executionDetailsFull, profiler_core_1.ReportKind.measurement);
if (this.config.shouldExportSensorInterfaceData()) {
if (metricsDataCollection !== undefined) {
performance.start('Profiler.finish.exportMetricsDataCollection');
metricsDataCollection.storeToFile(outFileMetricsDataCollection);
performance.stop('Profiler.finish.exportMetricsDataCollection');
}
}
// load all script sources from inspector
performance.start('Profiler.finish.externalResourceHelper.fillSourceMapsFromCPUProfile');
yield this._externalResourceHelper.fillSourceMapsFromCPUProfile(profile);
performance.stop('Profiler.finish.externalResourceHelper.fillSourceMapsFromCPUProfile');
performance.start('Profiler.finish.externalResourceHelper.disconnect');
yield this._externalResourceHelper.disconnect();
performance.stop('Profiler.finish.externalResourceHelper.disconnect');
if (this.config.shouldExportV8Profile()) {
performance.start('Profiler.finish.exportExternalResourceHelper');
this._externalResourceHelper.storeToFile(outFileExternalResourceHelper, 'pretty-json');
performance.stop('Profiler.finish.exportExternalResourceHelper');
}
performance.start('Profiler.finish.insertCPUProfile');
yield report.insertCPUProfile(rootDir, cpuProfile, this._externalResourceHelper, metricsDataCollection);
performance.stop('Profiler.finish.insertCPUProfile');
performance.start('Profiler.finish.trackUncommittedFiles');
report.trackUncommittedFiles(rootDir, this._externalResourceHelper);
performance.stop('Profiler.finish.trackUncommittedFiles');
if (this.config.shouldExportV8Profile()) {
performance.start('Profiler.finish.exportExternalResourceHelper');
this._externalResourceHelper.storeToFile(outFileExternalResourceHelper, 'pretty-json');
performance.stop('Profiler.finish.exportExternalResourceHelper');
}
if (this.config.shouldExportReport()) {
performance.start('Profiler.finish.exportReport');
report.storeToFile(outFileReport, 'bin', this.config);
performance.stop('Profiler.finish.exportReport');
}
if (yield report.shouldBeStoredInRegistry()) {
yield profiler_core_1.RegistryHelper.uploadToRegistry(report, this.config);
}
performance.stop('Profiler.finish');
performance.printReport('Profiler.finish');
performance.exportAndSum(this.exportAssetHelper.outputPerformancePath());
return report;
});
}
}
exports.Profiler = Profiler;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUHJvZmlsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvUHJvZmlsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsdUNBQXdCO0FBRXhCLDREQUFtQztBQUNuQywwREFpQitCO0FBRS9CLDBFQUFzRTtBQUN0RSxnRUFBNEQ7QUFFNUQsdUdBQW1HO0FBQ25HLCtFQUEyRTtBQUMzRSx3RkFBb0Y7QUFFcEYsTUFBYSxRQUFRO0lBU3BCLFlBQVksWUFBcUI7UUFDaEMsSUFBSSxDQUFDLFlBQVksR0FBRyxZQUFZLENBQUE7UUFDaEMsSUFBSSxDQUFDLE1BQU0sR0FBRyw4QkFBYyxDQUFDLFdBQVcsRUFBRSxDQUFBO1FBQzFDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFBO1FBRTFCLElBQUksQ0FBQyx1QkFBdUIsR0FBRyxJQUFJLHNDQUFzQixDQUN4RCxJQUFJLENBQUMsTUFBTSxDQUFDLFVBQVUsRUFBRSxDQUN4QixDQUFBO1FBQ0QsSUFBSSxDQUFDLGlCQUFpQixHQUFHLElBQUksaUNBQWlCLENBQzdDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLElBQUksRUFBRSxDQUFDLENBQ3JELENBQUE7SUFDRixDQUFDO0lBRUQsTUFBTSxDQUFDLGtCQUFrQixDQUFDLE1BQXNCO1FBQy9DLE1BQU0sbUJBQW1CLEdBQUcsTUFBTSxDQUFDLHNCQUFzQixFQUFFLENBQUE7UUFDM0QsUUFBUSxtQkFBbUIsRUFBRSxDQUFDO1lBQzdCLEtBQUssbUNBQW1CLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQztnQkFDdkMsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLHlCQUF5QixFQUFFLENBQUE7Z0JBQ2xELElBQUksT0FBTyxLQUFLLFNBQVMsRUFBRSxDQUFDO29CQUMzQixNQUFNLElBQUks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