@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
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());
});
};
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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