UNPKG

@oaklean/profiler-core

Version:

Part of the @oaklean suite. It provides all basic functions to work with the `.oak` file format. It allows parsing the `.oak` file format as well as tools for analyzing the measurement values. It also provides all necessary capabilities required for prec

422 lines 35.7 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProfilerConfig = void 0; const fs = __importStar(require("fs")); const jsoncParser = __importStar(require("jsonc-parser")); const config_1 = require("../constants/config"); const UnifiedPath_1 = require("../system/UnifiedPath"); const Crypto_1 = require("../system/Crypto"); const PermissionHelper_1 = require("../helper/PermissionHelper"); const LoggerHelper_1 = require("../helper/LoggerHelper"); const PathUtils_1 = require("../helper/PathUtils"); // Types const types_1 = require("../types"); const JsoncHelper_1 = require("../helper/JsoncHelper"); const ProfilerConfigCommentHelper_1 = require("../helper/ProfilerConfigCommentHelper"); class ProfilerConfig { constructor(filePath, config) { this.filePath = filePath; this.extends = config.extends; this.registryOptions = config.registryOptions; this.exportOptions = config.exportOptions; this.projectOptions = config.projectOptions; this.runtimeOptions = config.runtimeOptions; } static getDefaultConfig() { return new ProfilerConfig(new UnifiedPath_1.UnifiedPath(process.cwd()).join(config_1.STATIC_CONFIG_FILENAME), config_1.DEFAULT_PROFILER_CONFIG); } static defaultConfigAsIntermediate() { return Object.assign({ filePath: new UnifiedPath_1.UnifiedPath(process.cwd()).join(config_1.STATIC_CONFIG_FILENAME) }, config_1.DEFAULT_PROFILER_CONFIG); } static verifyConfig(config) { types_1.IProfilerConfig_schema.parse(config); return true; } static printZodError(err) { LoggerHelper_1.LoggerHelper.error('ProfilerConfig.verifyConfig: Invalid config'); for (const issue of err.issues) { LoggerHelper_1.LoggerHelper.error(`${issue.path.join('.')} - ${issue.message}`); } } getAnonymizedRuntimeOptions() { if (this.runtimeOptions.sensorInterface) { switch (this.runtimeOptions.sensorInterface.type) { case types_1.SensorInterfaceType.windows: return Object.assign(Object.assign({}, this.runtimeOptions), { sensorInterface: { type: types_1.SensorInterfaceType.windows, options: { sampleInterval: this.runtimeOptions.sensorInterface.options.sampleInterval, outputFilePath: '<anonymized>' } } }); case types_1.SensorInterfaceType.perf: return Object.assign(Object.assign({}, this.runtimeOptions), { sensorInterface: { type: types_1.SensorInterfaceType.perf, options: { sampleInterval: this.runtimeOptions.sensorInterface.options.sampleInterval, outputFilePath: '<anonymized>' } } }); case types_1.SensorInterfaceType.powermetrics: return Object.assign(Object.assign({}, this.runtimeOptions), { sensorInterface: { type: types_1.SensorInterfaceType.powermetrics, options: { sampleInterval: this.runtimeOptions.sensorInterface.options.sampleInterval, outputFilePath: '<anonymized>' } } }); } } return this.runtimeOptions; } getV8CPUSamplingInterval() { return this.runtimeOptions.v8.cpu.sampleInterval; } getRegistryUploadUrl() { return `http://${this.registryOptions.url}/upload`; } uploadEnabled() { var _a, _b; return (((_a = this.registryOptions) === null || _a === void 0 ? void 0 : _a.url) !== undefined && ((_b = this.registryOptions) === null || _b === void 0 ? void 0 : _b.url) !== ''); } getProjectIdentifier() { if (!Crypto_1.Crypto.validateUniqueID(this.projectOptions.identifier)) { throw new Error('ProfilerConfig.getProjectIdentifier: identifier should be an uuid4'); } return this.projectOptions.identifier; } getRootDir() { if (PathUtils_1.PathUtils.isAbsolute(this.exportOptions.rootDir)) { return new UnifiedPath_1.UnifiedPath(this.exportOptions.rootDir); } return this.filePath.dirName().join(this.exportOptions.rootDir); } getOutDir() { if (PathUtils_1.PathUtils.isAbsolute(this.exportOptions.outDir)) { return new UnifiedPath_1.UnifiedPath(this.exportOptions.outDir); } return this.filePath.dirName().join(this.exportOptions.outDir); } getOutHistoryDir() { if (PathUtils_1.PathUtils.isAbsolute(this.exportOptions.outHistoryDir)) { return new UnifiedPath_1.UnifiedPath(this.exportOptions.outHistoryDir); } return this.filePath.dirName().join(this.exportOptions.outHistoryDir); } getSensorInterfaceType() { var _a; return (_a = this.runtimeOptions.sensorInterface) === null || _a === void 0 ? void 0 : _a.type; } static getSensorInterfaceType(json) { var _a, _b; return (_b = (_a = json.runtimeOptions) === null || _a === void 0 ? void 0 : _a.sensorInterface) === null || _b === void 0 ? void 0 : _b.type; } getSensorInterfaceOptions() { var _a; return (_a = this.runtimeOptions.sensorInterface) === null || _a === void 0 ? void 0 : _a.options; } shouldExportV8Profile() { return this.exportOptions.exportV8Profile; } shouldExportReport() { return this.exportOptions.exportReport; } shouldExportSensorInterfaceData() { return this.exportOptions.exportSensorInterfaceData; } getSeedForMathRandom() { return this.runtimeOptions.seeds['Math.random']; } static configAsExtended(config, pathDiff) { const exportOptions = config.exportOptions; if (exportOptions) { if (exportOptions.outDir && !PathUtils_1.PathUtils.isAbsolute(exportOptions.outDir)) { exportOptions.outDir = pathDiff.join(exportOptions.outDir).toString(); } if (exportOptions.outHistoryDir && !PathUtils_1.PathUtils.isAbsolute(exportOptions.outHistoryDir)) { exportOptions.outHistoryDir = pathDiff .join(exportOptions.outHistoryDir) .toString(); } if (exportOptions.rootDir && !PathUtils_1.PathUtils.isAbsolute(exportOptions.rootDir)) { exportOptions.rootDir = pathDiff.join(exportOptions.rootDir).toString(); } } return { exportOptions: exportOptions, projectOptions: config.projectOptions, runtimeOptions: config.runtimeOptions, registryOptions: config.registryOptions }; } /** * Fills unspecified values of the config with values of the given config to extend from * * example usage: * - every config that gets resolved inherits values of the default config (baseConfig) * to ensure that unspecified values are filled with the default value. This happens via: * ProfilerConfig.implement(config, baseConfig): * * - if a config contains the extends keyword like this: * { * "extends": "<config that gets extended>" * } * the config gets extended via: * ProfilerConfig.implement(config, <config mentioned in extends>): * * it also adjusts the inherited path values values to make them relative to the config * * * @param config to inherit from */ static implement(config, configToExtend) { var _a, _b, _c, _d, _e, _f; const pathDiff = config.filePath .dirName() .pathTo(configToExtend.filePath.dirName()); const configToExtendAsExtended = ProfilerConfig.configAsExtended(configToExtend, pathDiff); const newExportOptions = Object.assign(Object.assign({}, configToExtendAsExtended.exportOptions), config.exportOptions); const newProjectOptions = Object.assign(Object.assign({}, configToExtendAsExtended.projectOptions), config.projectOptions); const newRuntimeOptions = Object.assign(Object.assign(Object.assign({}, configToExtendAsExtended.runtimeOptions), config.runtimeOptions), { seeds: Object.assign(Object.assign({}, (((_a = configToExtendAsExtended === null || configToExtendAsExtended === void 0 ? void 0 : configToExtendAsExtended.runtimeOptions) === null || _a === void 0 ? void 0 : _a.seeds) || {})), (((_b = config.runtimeOptions) === null || _b === void 0 ? void 0 : _b.seeds) || {})), v8: { cpu: Object.assign(Object.assign({}, (((_d = (_c = configToExtendAsExtended.runtimeOptions) === null || _c === void 0 ? void 0 : _c.v8) === null || _d === void 0 ? void 0 : _d.cpu) || {})), (((_f = (_e = config.runtimeOptions) === null || _e === void 0 ? void 0 : _e.v8) === null || _f === void 0 ? void 0 : _f.cpu) || {})) } }); const newRegistryOptions = Object.assign(Object.assign({}, configToExtendAsExtended.registryOptions), config.registryOptions); config.exportOptions = newExportOptions; config.projectOptions = newProjectOptions; config.runtimeOptions = newRuntimeOptions; config.registryOptions = newRegistryOptions; } static intermediateFromJSON(json) { let data; if (typeof json === 'string') { data = JSON.parse(json); data.filePath = new UnifiedPath_1.UnifiedPath(''); } else { data = Object.assign(Object.assign({}, json), { filePath: new UnifiedPath_1.UnifiedPath('') }); } return data; } toJSON() { return { extends: this.extends, exportOptions: this.exportOptions, projectOptions: this.projectOptions, runtimeOptions: this.runtimeOptions, registryOptions: this.registryOptions }; } static stringifyConfig(config, options) { var _a; const jsonc = new JsoncHelper_1.JsoncHelper((_a = options === null || options === void 0 ? void 0 : options.existingFileContent) !== null && _a !== void 0 ? _a : '{}'); jsonc.updateJsoncContent(config); let output = jsonc.toString(); if (options === null || options === void 0 ? void 0 : options.addDefaultComments) { output = ProfilerConfigCommentHelper_1.ProfilerConfigCommentHelper.addDefaultCommentsToConfigFileContent(output); } return output; } storeToFile(filePath, options) { let content = '{}'; if (fs.existsSync(filePath.toPlatformString())) { content = fs.readFileSync(filePath.toPlatformString(), 'utf8'); } const output = ProfilerConfig.stringifyConfig(this.toJSON(), { existingFileContent: content, addDefaultComments: options === null || options === void 0 ? void 0 : options.addDefaultComments }); PermissionHelper_1.PermissionHelper.writeFileWithUserPermission(filePath, output); } static storeIntermediateToFile(filePath, config, options) { let content = '{}'; if (fs.existsSync(filePath.toPlatformString())) { content = fs.readFileSync(filePath.toPlatformString(), 'utf8'); } const output = ProfilerConfig.stringifyConfig(config, { existingFileContent: content, addDefaultComments: options === null || options === void 0 ? void 0 : options.addDefaultComments }); PermissionHelper_1.PermissionHelper.writeFileWithUserPermission(filePath, output); } // loads a config from a given file path and extends it // this method is NOT used to ensure load a valid (complete) config, use resolveFromFile to achieve that static loadFromFile(filePath) { if (!fs.existsSync(filePath.toPlatformString())) { return undefined; } const content = fs.readFileSync(filePath.toPlatformString()).toString(); const errors = []; const configJSON = jsoncParser.parse(content, errors, { allowTrailingComma: true, allowEmptyContent: true }); if (errors.length > 0) { const errorMessages = errors .map((err) => `Line ${err.offset}: ${jsoncParser.printParseErrorCode(err.error)}`) .join(', '); LoggerHelper_1.LoggerHelper.error(`ProfilerConfig.loadFromFile: JSONC parse errors: ${errorMessages}`); throw new Error(`ProfilerConfig: Invalid ${config_1.STATIC_CONFIG_FILENAME} config file`); } const loadedConfig = ProfilerConfig.intermediateFromJSON(configJSON); loadedConfig.filePath = filePath; if (loadedConfig.extends) { const configToExtendFilePath = filePath .dirName() .join(loadedConfig.extends); const configToExtend = ProfilerConfig.loadFromFile(configToExtendFilePath); if (configToExtend) { ProfilerConfig.implement(loadedConfig, configToExtend); return loadedConfig; } } return loadedConfig; } // loads a config from a given file path and fills all non defined values with the resp. default values static resolveFromFile(filePath) { var _a; if (!filePath) { return ProfilerConfig.getDefaultConfig(); } const baseConfig = ProfilerConfig.defaultConfigAsIntermediate(); const config = this.loadFromFile(filePath); if (config) { baseConfig.filePath = config.filePath; if (((_a = config.projectOptions) === null || _a === void 0 ? void 0 : _a.identifier) === undefined) { throw new Error('ProfilerConfig: the project has no identifier yet'); } ProfilerConfig.implement(config, baseConfig); try { if (ProfilerConfig.verifyConfig(config)) { return new ProfilerConfig(config.filePath, config); } // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err) { if (err.name === 'ZodError') { ProfilerConfig.printZodError(err); throw new Error(`ProfilerConfig: Invalid ${config_1.STATIC_CONFIG_FILENAME} config file`); } throw err; } } return new ProfilerConfig(baseConfig.filePath, baseConfig); } static autoResolveFromPath(startDir) { // Searches from the given path upwards until it finds the config file const configFilePath = PathUtils_1.PathUtils.findUp(config_1.STATIC_CONFIG_FILENAME, startDir.toPlatformString()); if (!configFilePath) { return ProfilerConfig.resolveFromFile(undefined); } return ProfilerConfig.resolveFromFile(new UnifiedPath_1.UnifiedPath(configFilePath)); } static autoResolve() { // Searches from the processes execution path upwards until it finds the config file return this.autoResolveFromPath(new UnifiedPath_1.UnifiedPath(process.cwd())); } static createMainConfig(options) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; const mainConfig = ProfilerConfig.getDefaultConfig(); mainConfig.projectOptions.identifier = (_b = (_a = options === null || options === void 0 ? void 0 : options.projectOptions) === null || _a === void 0 ? void 0 : _a.identifier) !== null && _b !== void 0 ? _b : (yield Crypto_1.Crypto.uniqueID()); mainConfig.registryOptions = undefined; // remove runtime options from main config mainConfig.runtimeOptions.sensorInterface = undefined; return mainConfig; }); } /** * Function to initialize a local config file that extends the main config file * * @param options * @returns */ static createLocalConfig(options) { var _a, _b, _c; const localConfig = {}; localConfig.runtimeOptions = {}; switch (options.selectedSensorInterface) { case undefined: localConfig.runtimeOptions.sensorInterface = undefined; break; case types_1.SensorInterfaceType.perf: localConfig.runtimeOptions.sensorInterface = { type: types_1.SensorInterfaceType.perf, options: { outputFilePath: 'energy-measurements.txt', sampleInterval: (_a = options.sensorInterfaceSampleInterval) !== null && _a !== void 0 ? _a : 100 } }; break; case types_1.SensorInterfaceType.powermetrics: localConfig.runtimeOptions.sensorInterface = { type: types_1.SensorInterfaceType.powermetrics, options: { outputFilePath: 'energy-measurements.plist', sampleInterval: (_b = options.sensorInterfaceSampleInterval) !== null && _b !== void 0 ? _b : 100 } }; break; case types_1.SensorInterfaceType.windows: localConfig.runtimeOptions.sensorInterface = { type: types_1.SensorInterfaceType.windows, options: { outputFilePath: 'energy-measurements.csv', sampleInterval: (_c = options.sensorInterfaceSampleInterval) !== null && _c !== void 0 ? _c : 100 } }; break; default: break; } return localConfig; } } exports.ProfilerConfig = ProfilerConfig; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUHJvZmlsZXJDb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbW9kZWwvUHJvZmlsZXJDb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsdUNBQXdCO0FBRXhCLDBEQUEyQztBQUczQyxnREFHNEI7QUFDNUIsdURBQW1EO0FBQ25ELDZDQUF5QztBQUN6QyxpRUFBNkQ7QUFDN0QseURBQXFEO0FBQ3JELG1EQUErQztBQUMvQyxRQUFRO0FBQ1Isb0NBZWlCO0FBQ2pCLHVEQUFtRDtBQUNuRCx1RkFBbUY7QUFNbkYsTUFBYSxjQUFjO0lBUTFCLFlBQVksUUFBcUIsRUFBRSxNQUF1QjtRQUN6RCxJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQTtRQUN4QixJQUFJLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUE7UUFDN0IsSUFBSSxDQUFDLGVBQWUsR0FBRyxNQUFNLENBQUMsZUFBZSxDQUFBO1FBQzdDLElBQUksQ0FBQyxhQUFhLEdBQUcsTUFBTSxDQUFDLGFBQWEsQ0FBQTtRQUN6QyxJQUFJLENBQUMsY0FBYyxHQUFHLE1BQU0sQ0FBQyxjQUFjLENBQUE7UUFDM0MsSUFBSSxDQUFDLGNBQWMsR0FBRyxNQUFNLENBQUMsY0FBYyxDQUFBO0lBQzVDLENBQUM7SUFFRCxNQUFNLENBQUMsZ0JBQWdCO1FBQ3RCLE9BQU8sSUFBSSxjQUFjLENBQ3hCLElBQUkseUJBQVcsQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsK0JBQXNCLENBQUMsRUFDM0QsZ0NBQXVCLENBQ3ZCLENBQUE7SUFDRixDQUFDO0lBRUQsTUFBTSxDQUFDLDJCQUEyQjtRQUNqQyx1QkFDQyxRQUFRLEVBQUUsSUFBSSx5QkFBVyxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQywr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