@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
JavaScript
"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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