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

323 lines 29.4 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.InsertCPUProfileStateMachine = void 0; const CallIdentifier_1 = require("./CallIdentifier"); const CallRelationTracker_1 = require("./CallRelationTracker"); const StateMachineLogger_1 = require("./StateMachineLogger"); const CompensationHelper_1 = require("./CompensationHelper"); const AccountingHelper_1 = require("./AccountingHelper"); const Switch_1 = require("../../system/Switch"); const CPUModel_1 = require("../CPUProfile/CPUModel"); const system_1 = require("../../system"); const ModuleReport_1 = require("../../model/ModuleReport"); const NodeModule_1 = require("../../model/NodeModule"); /** * This state machine is responsible to track the current state of the CPU profile insertion. * It handles transitions between different states based on the source location of the CPU nodes. * The states include: * - Project: when the current function belongs to the project source code. * - Module: when the current function belongs to an external module. * - LangInternal: when the current function is a language internal function (e.g., V8 internals). * - Wasm: when the current function is a WebAssembly function. * * The state machine uses the ResolveFunctionIdentifierHelper to determine the appropriate state * based on the source location of the CPU nodes. * It also tracks call relations to avoid double counting of sensor values for already recorded calls. * * The state machine maintains an awaiter stack to correctly account for async function calls and their awaiters. * This ensures that sensor values are accurately attributed to the correct source nodes in async scenarios. * */ class InsertCPUProfileStateMachine { constructor(reportToApply) { this.projectReport = reportToApply; this.callRelationTracker = new CallRelationTracker_1.CallRelationTracker(); this.awaiterStack = []; } /** * Inserts a CPU profile into the state machine, updating the project report accordingly. * * @param resolveFunctionIdentifierHelper the helper to resolve function identifiers * @param profile the raw cpu profile to insert * @param metricsDataCollection optional metrics data collection to enrich the cpu profile with energy values */ insertCPUProfile(rootDir, resolveFunctionIdentifierHelper, profile, metricsDataCollection) { return __awaiter(this, void 0, void 0, function* () { if (this.projectReport.executionDetails.highResolutionBeginTime === undefined) { throw new Error('InsertCPUProfileHelper.insertCPUProfile: executionDetails.highResolutionBeginTime is undefined'); } const cpuModel = new CPUModel_1.CPUModel(rootDir, profile, BigInt(this.projectReport.executionDetails.highResolutionBeginTime)); if (metricsDataCollection && metricsDataCollection.items.length > 0) { // fill the cpu model with energy values cpuModel.energyValuesPerNode = cpuModel.energyValuesPerNodeByMetricsData(metricsDataCollection); } yield this.insertCPUNodes(cpuModel.getNode(0), resolveFunctionIdentifierHelper); }); } /** * Inserts CPU nodes into the state machine, updating the project report accordingly. * * @param rootNode the root node of the cpu model * @param resolveFunctionIdentifierHelper the helper to resolve function identifiers */ insertCPUNodes(rootNode, resolveFunctionIdentifierHelper) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g; const stack = [ { // begin state parent: null, state: { scope: 'project', type: 'lang_internal', headless: true, compensationLayerDepth: 0, callIdentifier: new CallIdentifier_1.CallIdentifier(this.projectReport, null, 0) }, node: rootNode, depth: 0 } ]; // traverse the cpu nodes depth first while (stack.length > 0) { const currentStackFrame = stack[stack.length - 1]; // POSTPROCESSING OF THE NODE (second visit) if (currentStackFrame.result !== undefined) { // second visit of the node, do post processing stack.pop(); // state that is about to get left: currentStackFrame.result.nextState // so the parent state is: currentStackFrame.state: const parentState = currentStackFrame.state; // this state is about to get left (all children have been processed): const currentState = currentStackFrame.result.nextState; const accountingInfo = currentStackFrame.result.accountingInfo; // check wether a link was created if (accountingInfo.accountedSourceNodeReference !== null) { // remove the last child call from the current state if (!this.callRelationTracker.removeLastChildRecord(parentState.callIdentifier)) { throw new Error('InsertCPUProfileHelper.insertCPUProfile.traverse: expected childCalls to be present'); } } if (currentStackFrame.result.accountingInfo.accountedSourceNode .firstTimeVisited) { // last occurrence of the callIdentifier in the call stack // remove it from the call relation tracker this.callRelationTracker.removeCallRecord(currentState.callIdentifier); } if (currentStackFrame.result.accountingInfo.accountedSourceNode .firstTimeInCurrentCompensationLayer) { // last occurrence of the callIdentifier in the current compensation layer // remove it from the compensation layer tracking this.callRelationTracker.removeCompensationLayerRecord(currentState.callIdentifier); } if ((_a = this.debug) === null || _a === void 0 ? void 0 : _a.states) { StateMachineLogger_1.StateMachineLogger.logState(currentStackFrame.depth + 1, currentStackFrame.node, currentState); } const compensation = currentStackFrame.result.compensation; // Compensation handling: if (compensation !== undefined) { CompensationHelper_1.CompensationHelper.applyCompensation(currentStackFrame.node, currentState, parentState, compensation, accountingInfo, ((_b = this.debug) === null || _b === void 0 ? void 0 : _b.compensations) ? { depth: currentStackFrame.depth } : undefined); if (currentStackFrame.parent !== null) { // propagate the compensation to all parents CompensationHelper_1.CompensationHelper.propagateCompensation(currentStackFrame.parent, compensation, ((_c = this.debug) === null || _c === void 0 ? void 0 : _c.compensations) ? { depth: currentStackFrame.depth, node: currentStackFrame.node, currentState } : undefined); } } if ((_d = this.debug) === null || _d === void 0 ? void 0 : _d.transitions) { StateMachineLogger_1.StateMachineLogger.logLeaveTransition(currentStackFrame.result.nextState, currentStackFrame.state); } if (currentState.callIdentifier.isAwaiterSourceNode) { this.awaiterStack.pop(); } continue; } // PROCESSING OF THE NODE (first visit) if ((_e = this.debug) === null || _e === void 0 ? void 0 : _e.states) { StateMachineLogger_1.StateMachineLogger.logState(currentStackFrame.depth, currentStackFrame.node, currentStackFrame.state); } // determine the transition const transition = yield InsertCPUProfileStateMachine.getTransition(currentStackFrame.state, currentStackFrame.node.sourceLocation, resolveFunctionIdentifierHelper); // apply the transition currentStackFrame.result = yield this.applyTransition(currentStackFrame, transition); if ((_f = this.debug) === null || _f === void 0 ? void 0 : _f.transitions) { StateMachineLogger_1.StateMachineLogger.logTransition(currentStackFrame.node, transition, currentStackFrame.result.accountingInfo, currentStackFrame.state, currentStackFrame.result.nextState); } // create compensation if necessary currentStackFrame.result.compensation = CompensationHelper_1.CompensationHelper.createCompensationIfNecessary(currentStackFrame.node, currentStackFrame.state, currentStackFrame.result, ((_g = this.debug) === null || _g === void 0 ? void 0 : _g.compensations) ? { depth: currentStackFrame.depth, node: currentStackFrame.node } : undefined); // add children to stack for (const child of currentStackFrame.node.reversedChildren()) { stack.push({ parent: currentStackFrame, state: currentStackFrame.result.nextState, node: child, depth: currentStackFrame.depth + 1 }); } } }); } /** * determine the transition based on the current state and the cpu node's source location * * @param currentState the current state of the state machine * * @param sourceLocation the source location of the incoming cpu node * @returns the transition to the next state */ static getTransition(currentState, sourceLocation, resolveFunctionIdentifierHelper) { return __awaiter(this, void 0, void 0, function* () { if (sourceLocation.isLangInternal) { return { transition: 'toLangInternal', options: { createLink: currentState.type !== 'lang_internal', headless: currentState.headless } }; } if (sourceLocation.isWASM) { const wasmPath = new system_1.UnifiedPath(sourceLocation.rawUrl.substring(7)); // remove the 'wasm://' prefix return { transition: 'toModule', options: { createLink: currentState.type !== 'lang_internal', headless: currentState.headless, nodeModule: NodeModule_1.WASM_NODE_MODULE, sourceNodeLocation: { relativeFilePath: wasmPath, functionIdentifier: // needs to be wrapped in {} to be a valid source node identifier `{${sourceLocation.rawFunctionName}}` }, presentInOriginalSourceCode: false } }; } const { sourceNodeLocation, functionIdentifierPresentInOriginalFile, nodeModule, relativeNodeModulePath } = yield resolveFunctionIdentifierHelper.resolveFunctionIdentifier(sourceLocation); if (!(relativeNodeModulePath && nodeModule)) { // is project return { transition: 'toProject', options: { createLink: currentState.scope === 'project' && currentState.type === 'intern', headless: false, sourceNodeLocation: sourceNodeLocation, presentInOriginalSourceCode: functionIdentifierPresentInOriginalFile } }; } else { // is module return { transition: 'toModule', options: { createLink: currentState.type !== 'lang_internal', headless: currentState.headless, nodeModule: nodeModule, sourceNodeLocation: sourceNodeLocation, presentInOriginalSourceCode: functionIdentifierPresentInOriginalFile } }; } }); } /** * performs the state transition and accounting based on the current stack frame and the transition * * @param currentStackFrame * @param transition * @returns the result of the transition including the next state and accounting info */ applyTransition(currentStackFrame, transition) { return __awaiter(this, void 0, void 0, function* () { this.applyHeadless(currentStackFrame, transition); switch (transition.transition) { case 'toLangInternal': return yield AccountingHelper_1.AccountingHelper.accountToLangInternal(currentStackFrame.state, currentStackFrame.node, transition, this.callRelationTracker); case 'toProject': { const scope = currentStackFrame.state.scope; switch (scope) { case 'project': // transition stays in project return yield AccountingHelper_1.AccountingHelper.accountToIntern(currentStackFrame.state, currentStackFrame.node, transition, this.callRelationTracker, this.awaiterStack); case 'module': // transition from module to project return yield AccountingHelper_1.AccountingHelper.accountOwnCodeGetsExecutedByExternal(currentStackFrame.state, currentStackFrame.node, transition, this.callRelationTracker, this.projectReport); } } break; case 'toModule': { const scope = currentStackFrame.state.scope; switch (scope) { case 'project': // transition from project to module return yield AccountingHelper_1.AccountingHelper.accountToExtern(currentStackFrame.state, currentStackFrame.node, transition, this.callRelationTracker); case 'module': if (currentStackFrame.state.callIdentifier.report instanceof ModuleReport_1.ModuleReport && currentStackFrame.state.callIdentifier.report.nodeModule .identifier === transition.options.nodeModule.identifier) { // transition stays in the same module return yield AccountingHelper_1.AccountingHelper.accountToIntern(currentStackFrame.state, currentStackFrame.node, transition, this.callRelationTracker, this.awaiterStack); } else { // transition from module to different module return yield AccountingHelper_1.AccountingHelper.accountToExtern(currentStackFrame.state, currentStackFrame.node, transition, this.callRelationTracker); } } } break; default: (0, Switch_1.assertUnreachable)(transition); } }); } /** * applies headless accounting if necessary * * @param currentStackFrame * @param transition */ applyHeadless(currentStackFrame, transition) { // if no intern calls were tracked yet, add the time to the headless cpu time if (currentStackFrame.state.headless && transition.transition !== 'toProject') { switch (transition.transition) { case 'toModule': this.projectReport.headlessSensorValues.addSelfToExtern(currentStackFrame.node.sensorValues); break; case 'toLangInternal': this.projectReport.headlessSensorValues.addSelfToLangInternal(currentStackFrame.node.sensorValues); break; } } } } exports.InsertCPUProfileStateMachine = InsertCPUProfileStateMachine; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSW5zZXJ0Q1BVUHJvZmlsZVN0YXRlTWFjaGluZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9oZWxwZXIvSW5zZXJ0Q1BVUHJvZmlsZUhlbHBlci9JbnNlcnRDUFVQcm9maWxlU3RhdGVNYWNoaW5lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7OztBQUFBLHFEQUFpRDtBQUNqRCwrREFBMkQ7QUFJM0QsNkRBQXlEO0FBQ3pELDZEQUF5RDtBQUN6RCx5REFBcUQ7QUFFckQsZ0RBQXVEO0FBQ3ZELHFEQUFpRDtBQUdqRCx5Q0FBMEM7QUFFMUMsMkRBQXVEO0FBQ3ZELHVEQUF5RDtBQU96RDs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILE1BQWEsNEJBQTRCO0lBY3hDLFlBQVksYUFBNEI7UUFDdkMsSUFBSSxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUE7UUFDbEMsSUFBSSxDQUFDLG1CQUFtQixHQUFHLElBQUkseUNBQW1CLEVBQUUsQ0FBQTtRQUNwRCxJQUFJLENBQUMsWUFBWSxHQUFHLEVBQUUsQ0FBQTtJQUN2QixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0csZ0JBQWdCLENBQ3JCLE9BQW9CLEVBQ3BCLCtCQUFnRSxFQUNoRSxPQUF1QixFQUN2QixxQkFBNkM7O1lBRTdDLElBQ0MsSUFBSSxDQUFDLGFBQWEsQ0FBQyxnQkFBZ0IsQ0FBQyx1QkFBdUIsS0FBSyxTQUFTLEVBQ3hFLENBQUM7Z0JBQ0YsTUFBTSxJQUFJLEtBQUssQ0FDZCxnR0FBZ0csQ0FDaEcsQ0FBQTtZQUNGLENBQUM7WUFDRCxNQUFNLFFBQVEsR0FBRyxJQUFJLG1CQUFRLENBQzVCLE9BQU8sRUFDUCxPQUFPLEVBQ1AsTUFBTSxDQUNMLElBQUksQ0FBQyxhQUFhLENBQUMsZ0JBQWdCLENBQUMsdUJBQXVCLENBQ3JDLENBQ3ZCLENBQUE7WUFFRCxJQUFJLHFCQUFxQixJQUFJLHFCQUFxQixDQUFDLEtBQUssQ0FBQyxNQUFNLEdBQUcs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