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

311 lines 28.6 kB
"use strict"; 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.AccountingHelper = void 0; const CallIdentifier_1 = require("./CallIdentifier"); const system_1 = require("../../system"); // Types const types_1 = require("../../types"); const TypescriptParser_1 = require("../TypescriptParser"); class AccountingHelper { // IMPORTANT to change when new measurement type gets added // if a node was already visited, set the aggregated measurements to 0 // to avoid double counting of measurements static sensorValuesForVisitedNode(sensorValues, visited) { const result = Object.assign({}, sensorValues); if (visited) { result.aggregatedCPUTime = 0; result.aggregatedCPUEnergyConsumption = 0; result.aggregatedRAMEnergyConsumption = 0; } return result; } /** * This method creates a new source node (if it does not exist) * in the [lang internal] section of the current report. * And adds the sensor values of the cpu node to the new source node. * * It also handles the linking of the newly created source node to the current source node. * * @param cpuNode the new cpu node (that should be inserted) * @param transition the transition (by inserting the cpu node) * * @returns the new state */ static accountToLangInternal(currentState, cpuNode, transition, callRelationTracker) { return __awaiter(this, void 0, void 0, function* () { const sensorValues = cpuNode.sensorValues; const accountedSourceNode = currentState.callIdentifier.report.addToLangInternal(cpuNode.sourceLocation.rawUrl, cpuNode.sourceLocation .sourceNodeIdentifier); const currentCallIdentifier = new CallIdentifier_1.CallIdentifier(currentState.callIdentifier.report, accountedSourceNode, currentState.compensationLayerDepth); const firstTimeVisited = callRelationTracker.initializeCallNodeIfAbsent(currentCallIdentifier, 'langInternal'); const firstTimeInCurrentCompensationLayer = callRelationTracker.initializeInCompensationLayerIfAbsent(currentCallIdentifier); accountedSourceNode.sensorValues.profilerHits += cpuNode.profilerHits; const accountedSensorValues = AccountingHelper.sensorValuesForVisitedNode(sensorValues, !firstTimeVisited); accountedSourceNode.addToSensorValues(accountedSensorValues); let accountedSourceNodeReference; if (transition.options.createLink) { if (currentState.callIdentifier.sourceNode === null) { throw new Error('InsertCPUProfileStateMachine.accountToLangInternal: Current state has no source node assigned'); } const alreadyLinked = callRelationTracker.linkCallToParent(currentCallIdentifier, currentState.callIdentifier); const accountedSensorValues = AccountingHelper.sensorValuesForVisitedNode(sensorValues, alreadyLinked); const sourceNodeReference = currentState.callIdentifier.sourceNode.addSensorValuesToLangInternal(accountedSourceNode.globalIdentifier(), accountedSensorValues); sourceNodeReference.sensorValues.profilerHits += cpuNode.profilerHits; accountedSourceNodeReference = { firstTimeVisited: !alreadyLinked, reference: sourceNodeReference }; } else { accountedSourceNodeReference = null; } return { nextState: { scope: currentState.scope, type: 'lang_internal', headless: transition.options.headless, callIdentifier: currentCallIdentifier, compensationLayerDepth: currentState.compensationLayerDepth }, accountingInfo: { type: 'accountToLangInternal', accountedSourceNode: { node: accountedSourceNode, firstTimeVisited, firstTimeInCurrentCompensationLayer }, accountedSensorValues, accountedSourceNodeReference } }; }); } /** * This method creates a new source node (if it does not exist) * in the [intern] section of the current report. * And adds the sensor values of the cpu node to the new source node. * * It also handles the linking of the newly created source node to the current source node. * * @param cpuNode the new cpu node (that should be inserted) * @param transition the transition (by inserting the cpu node) * * @returns the new state */ static accountToIntern(currentState, cpuNode, transition, callRelationTracker, awaiterStack) { return __awaiter(this, void 0, void 0, function* () { var _a; const sensorValues = cpuNode.sensorValues; const sourceNodeLocation = transition.options.sourceNodeLocation; let accountedSourceNodeReference; // intern const accountedSourceNode = currentState.callIdentifier.report.addToIntern(sourceNodeLocation.relativeFilePath.toString(), sourceNodeLocation.functionIdentifier); accountedSourceNode.presentInOriginalSourceCode = transition.options.presentInOriginalSourceCode; const currentCallIdentifier = new CallIdentifier_1.CallIdentifier(currentState.callIdentifier.report, accountedSourceNode, currentState.compensationLayerDepth); const firstTimeVisited = callRelationTracker.initializeCallNodeIfAbsent(currentCallIdentifier, 'intern'); const firstTimeInCurrentCompensationLayer = callRelationTracker.initializeInCompensationLayerIfAbsent(currentCallIdentifier); accountedSourceNode.sensorValues.profilerHits += cpuNode.profilerHits; const accountedSensorValues = AccountingHelper.sensorValuesForVisitedNode(sensorValues, !firstTimeVisited); accountedSourceNode.addToSensorValues(accountedSensorValues); if (sourceNodeLocation.functionIdentifier === TypescriptParser_1.TypescriptHelper.awaiterSourceNodeIdentifier()) { currentCallIdentifier.isAwaiterSourceNode = true; // add the awaiter to the stack and the corresponding async function parent // if the parentNodeInfo.sourceNode is null or of type lang internal // the awaiter is the first function in the call tree // this could happen if the was called from node internal functions for example awaiterStack.push({ awaiter: accountedSourceNode, awaiterParent: ((_a = currentState.callIdentifier.sourceNode) === null || _a === void 0 ? void 0 : _a.type) === types_1.SourceNodeMetaDataType.SourceNode ? currentState.callIdentifier .sourceNode : undefined }); } if (transition.options.createLink) { const alreadyLinked = callRelationTracker.linkCallToParent(currentCallIdentifier, currentState.callIdentifier); if (currentState.callIdentifier.sourceNode === null) { throw new Error('InsertCPUProfileStateMachine.accountToIntern: Current state has no source node assigned'); } if (currentState.callIdentifier.sourceNode !== accountedSourceNode) { // only create a reference if its not a recursive call const accountedSensorValues = AccountingHelper.sensorValuesForVisitedNode(sensorValues, alreadyLinked); const sourceNodeReference = currentState.callIdentifier.sourceNode.addSensorValuesToIntern(accountedSourceNode.globalIdentifier(), accountedSensorValues); sourceNodeReference.sensorValues.profilerHits += cpuNode.profilerHits; accountedSourceNodeReference = { firstTimeVisited: !alreadyLinked, reference: sourceNodeReference }; } else { accountedSourceNodeReference = { firstTimeVisited: !alreadyLinked }; } } else { accountedSourceNodeReference = null; } return { nextState: transition.transition === 'toProject' ? { scope: 'project', type: 'intern', headless: false, callIdentifier: currentCallIdentifier, compensationLayerDepth: currentState.compensationLayerDepth } : { scope: 'module', type: 'intern', headless: transition.options.headless, callIdentifier: currentCallIdentifier, compensationLayerDepth: currentState.compensationLayerDepth }, accountingInfo: { type: 'accountToIntern', accountedSourceNode: { node: accountedSourceNode, firstTimeVisited, firstTimeInCurrentCompensationLayer }, accountedSensorValues, accountedSourceNodeReference } }; }); } /** * This method creates a new source node and node module (if it does not exist) in the current report * in the [extern] section of the current report. * And adds the sensor values of the cpu node to the new source node. * * It also handles the linking of the newly created source node to the current source node. * * @param cpuNode the new cpu node (that should be inserted) * @param transition the transition (by inserting the cpu node) * * @returns the new state */ static accountToExtern(currentState, cpuNode, transition, callRelationTracker) { return __awaiter(this, void 0, void 0, function* () { const sensorValues = cpuNode.sensorValues; const sourceNodeLocation = transition.options.sourceNodeLocation; const nodeModule = transition.options.nodeModule; let accountedSourceNodeReference; const globalIdentifier = new system_1.GlobalIdentifier(sourceNodeLocation.relativeFilePath.toString(), sourceNodeLocation.functionIdentifier, nodeModule); // extern const { report, sourceNodeMetaData: accountedSourceNode } = currentState.callIdentifier.report.addToExtern(sourceNodeLocation.relativeFilePath, nodeModule, sourceNodeLocation.functionIdentifier); accountedSourceNode.presentInOriginalSourceCode = transition.options.presentInOriginalSourceCode; const currentCallIdentifier = new CallIdentifier_1.CallIdentifier(report, accountedSourceNode, currentState.compensationLayerDepth); const firstTimeVisited = callRelationTracker.initializeCallNodeIfAbsent(currentCallIdentifier, 'extern'); const firstTimeInCurrentCompensationLayer = callRelationTracker.initializeInCompensationLayerIfAbsent(currentCallIdentifier); accountedSourceNode.sensorValues.profilerHits += cpuNode.profilerHits; const accountedSensorValues = AccountingHelper.sensorValuesForVisitedNode(sensorValues, !firstTimeVisited); accountedSourceNode.addToSensorValues(accountedSensorValues); if (transition.options.createLink) { if (currentState.callIdentifier.sourceNode === null) { throw new Error('InsertCPUProfileStateMachine.accountToIntern: Current state has no source node assigned'); } const alreadyLinked = callRelationTracker.linkCallToParent(currentCallIdentifier, currentState.callIdentifier); const accountedSensorValues = AccountingHelper.sensorValuesForVisitedNode(sensorValues, alreadyLinked); const sourceNodeReference = currentState.callIdentifier.sourceNode.addSensorValuesToExtern(globalIdentifier, accountedSensorValues); sourceNodeReference.sensorValues.profilerHits += cpuNode.profilerHits; accountedSourceNodeReference = { firstTimeVisited: !alreadyLinked, reference: sourceNodeReference }; } else { accountedSourceNodeReference = null; } return { nextState: { scope: 'module', type: 'intern', headless: transition.options.headless, callIdentifier: currentCallIdentifier, compensationLayerDepth: currentState.compensationLayerDepth }, accountingInfo: { type: 'accountToExtern', accountedSourceNode: { node: accountedSourceNode, firstTimeVisited, firstTimeInCurrentCompensationLayer }, accountedSensorValues, accountedSourceNodeReference } }; }); } /** * This method is called when the new cpu node belongs to the project source code * but was executed by an external function (module or wasm). * * It creates a new source node (if it does not exist) in the [intern] section of the current report. * And adds the sensor values of the cpu node to the new source node. * * It does NOT create a link to the parent, since the parent call is from a different report. * Since Wasm code is treated as external code, it has its own report. * * @param originalReport the original report where the cpu profile is inserted * @param cpuNode the new cpu node (that should be inserted) * @param transition the transition (by inserting the cpu node) * * @returns the new state */ static accountOwnCodeGetsExecutedByExternal(currentState, cpuNode, transition, callRelationTracker, originalReport) { return __awaiter(this, void 0, void 0, function* () { const sensorValues = cpuNode.sensorValues; const sourceNodeLocation = transition.options.sourceNodeLocation; const accountedSourceNode = originalReport.addToIntern(sourceNodeLocation.relativeFilePath.toString(), sourceNodeLocation.functionIdentifier); accountedSourceNode.presentInOriginalSourceCode = transition.options.presentInOriginalSourceCode; const currentCallIdentifier = new CallIdentifier_1.CallIdentifier(originalReport, accountedSourceNode, currentState.compensationLayerDepth + 1); const firstTimeVisited = callRelationTracker.initializeCallNodeIfAbsent(currentCallIdentifier, 'intern'); const firstTimeInCurrentCompensationLayer = callRelationTracker.initializeInCompensationLayerIfAbsent(currentCallIdentifier); accountedSourceNode.sensorValues.profilerHits += cpuNode.profilerHits; const accountedSensorValues = AccountingHelper.sensorValuesForVisitedNode(sensorValues, !firstTimeVisited); // add measurements to original source code accountedSourceNode.addToSensorValues(accountedSensorValues); if (transition.options.createLink) { throw new Error('InsertCPUProfileStateMachine.accountOwnCodeGetsExecutedByExternal: Cannot create link to parent, since the parent call is from a different report'); } return { nextState: { scope: 'project', type: 'intern', headless: false, callIdentifier: currentCallIdentifier, compensationLayerDepth: currentState.compensationLayerDepth + 1 }, accountingInfo: { type: 'accountOwnCodeGetsExecutedByExternal', accountedSourceNode: { node: accountedSourceNode, firstTimeVisited, firstTimeInCurrentCompensationLayer }, accountedSensorValues, accountedSourceNodeReference: null } }; }); } } exports.AccountingHelper = AccountingHelper; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWNjb3VudGluZ0hlbHBlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9oZWxwZXIvSW5zZXJ0Q1BVUHJvZmlsZUhlbHBlci9BY2NvdW50aW5nSGVscGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7OztBQUFBLHFEQUFpRDtBQWVqRCx5Q0FBK0M7QUFHL0MsUUFBUTtBQUNSLHVDQU9vQjtBQUNwQiwwREFBc0Q7QUFFdEQsTUFBYSxnQkFBZ0I7SUFDNUIsMkRBQTJEO0lBQzNELHNFQUFzRTtJQUN0RSwyQ0FBMkM7SUFDM0MsTUFBTSxDQUFDLDBCQUEwQixDQUNoQyxZQUEyQixFQUMzQixPQUFnQjtRQUVoQixNQUFNLE1BQU0scUJBQ1IsWUFBWSxDQUNmLENBQUE7UUFFRCxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ2IsTUFBTSxDQUFDLGlCQUFpQixHQUFHLENBQXdCLENBQUE7WUFDbkQsTUFBTSxDQUFDLDhCQUE4QixHQUFHLENBQXNCLENBQUE7WUFDOUQsTUFBTSxDQUFDLDhCQUE4QixHQUFHLENBQXNCLENBQUE7UUFDL0QsQ0FBQztRQUVELE9BQU8sTUFBTSxDQUFBO0lBQ2QsQ0FBQztJQUVEOzs7Ozs7Ozs7OztPQVdHO0lBQ0gsTUFBTSxDQUFPLHFCQUFxQixDQUNqQyxZQUFtQixFQUNuQixPQUFnQixFQUNoQixVQUFvQyxFQUNwQyxtQkFBd0M7O1lBUXhDLE1BQU0sWUFBWSxHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUE7WUFFekMsTUFBTSxtQkFBbUIsR0FDeEIsWUFBWSxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsaUJBQWlCLENBQ25ELE9BQU8sQ0FBQyxjQUFjLENBQUMsTUFBaUMsRUFDeEQsT0FBTyxDQUFDLGNBQWM7aUJBQ3BCLG9CQUErRCxDQUNqRSxDQUFBO1lBRUYsTUFBTSxxQkFBcUIsR0FBRyxJQUFJLCtCQUFjLENBQy9DLFlBQVksQ0FBQyxjQUFjLENBQUMsTUFBTSxFQUNsQyxt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