UNPKG

@oaklean/profiler

Version:

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

297 lines 30.1 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 () { 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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