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

250 lines 23 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()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResolveFunctionIdentifierHelper = void 0; const path_1 = __importDefault(require("path")); const LoggerHelper_1 = require("./LoggerHelper"); const TypescriptParser_1 = require("./TypescriptParser"); const UrlProtocolHelper_1 = require("./UrlProtocolHelper"); const NodeModuleUtils_1 = require("./NodeModuleUtils"); const constants_1 = require("../constants"); const UnifiedPath_1 = require("../system/UnifiedPath"); /** * This helper resolves a function identifier from a CPU profile's source location. * It does so by requesting the executed code from the Node engine and parsing it. * If the requested code contains a source map, it attempts to resolve the original source location. * * Additionally, it checks whether the executed code is part of a Node module. * If so, it determines the associated Node module. */ class ResolveFunctionIdentifierHelper { constructor(rootDir, externalResourceHelper) { this.hideOriginalSourceFileNotExistErrors = true; this.rootDir = rootDir; this.externalResourceHelper = externalResourceHelper; this.PSTperNodeScript = new Map(); this.PSTperOriginalFile = new Map(); this.functionIdentifierCache = new Map(); this._nodeModulePerFileCache = new Map(); } /** * Adds a caching layer to the NodeModuleUtils.nodeModuleFromFilePath function */ nodeModuleFromFilePath(relativeFilePath) { let cacheEntry = this._nodeModulePerFileCache.get(relativeFilePath.toString()); if (cacheEntry !== undefined) { return cacheEntry; } cacheEntry = NodeModuleUtils_1.NodeModuleUtils.nodeModuleFromFilePath(this.externalResourceHelper, relativeFilePath); this._nodeModulePerFileCache.set(relativeFilePath.toString(), cacheEntry); return cacheEntry; } resolveFunctionIdentifier(sourceLocation) { return __awaiter(this, void 0, void 0, function* () { // check wether the given source location was already resolved by checking the cache const functionIdentifierCacheResult = this.functionIdentifierCache.get(sourceLocation.index); if (functionIdentifierCacheResult !== undefined) { return functionIdentifierCacheResult; } let programStructureTreeNodeScript = this.PSTperNodeScript.get(sourceLocation.scriptID); let programStructureTreeOriginal = undefined; const { lineNumber, columnNumber } = sourceLocation.sourceLocation; let functionIdentifierPresentInOriginalFile = true; let sourceNodeLocation = undefined; let originalSourceFileNotFoundError = undefined; if (programStructureTreeNodeScript === undefined) { // request source code from the node engine // (it is already transformed event if it is the original file path) const sourceCode = yield this.externalResourceHelper.sourceCodeFromScriptID(sourceLocation.scriptID); if (sourceCode === null) { throw new Error('ResolveFunctionIdentifierHelper.resolveFunctionIdentifier: sourceCode should not be null' + `scriptID: ${sourceLocation.scriptID}` + ` (${sourceLocation.absoluteUrl.toPlatformString()})`); } programStructureTreeNodeScript = TypescriptParser_1.TypescriptParser.parseSource(sourceLocation.absoluteUrl, sourceCode); this.PSTperNodeScript.set(sourceLocation.scriptID, programStructureTreeNodeScript); } // function identifier of the executed source code const functionIdentifier = programStructureTreeNodeScript.identifierBySourceLocation({ line: lineNumber, column: columnNumber }); const sourceMap = yield this.externalResourceHelper.sourceMapFromScriptID(sourceLocation.scriptID, sourceLocation.absoluteUrl); const originalPosition = sourceMap !== null ? yield ResolveFunctionIdentifierHelper.resolveMappedLocationFromSourceMap(programStructureTreeNodeScript, sourceMap, lineNumber, columnNumber) : undefined; if (originalPosition && originalPosition.source !== null && originalPosition.line !== null && originalPosition.column !== null) { const { url: originalPositionPath, protocol: urlProtocol } = UrlProtocolHelper_1.UrlProtocolHelper.webpackSourceMapUrlToOriginalUrl(this.rootDir, originalPosition.source); const absoluteOriginalSourcePath = originalPositionPath.isRelative() ? new UnifiedPath_1.UnifiedPath(path_1.default.resolve(path_1.default.join(path_1.default.dirname(sourceLocation.absoluteUrl.toString()), originalPositionPath.toString()))) : originalPositionPath; const relativeOriginalSourcePath = this.rootDir.pathTo(absoluteOriginalSourcePath); programStructureTreeOriginal = this.PSTperOriginalFile.get(relativeOriginalSourcePath.toString()); if (programStructureTreeOriginal === undefined) { try { // found the original source file from the source map but it is not yet parsed programStructureTreeOriginal = this.externalResourceHelper.parseFile(relativeOriginalSourcePath, absoluteOriginalSourcePath); this.PSTperOriginalFile.set(relativeOriginalSourcePath.toString(), programStructureTreeOriginal); } catch (_a) { // could not parse original source file, // sometimes WebFrameworks include sourcemaps that point to e.g. .svg files programStructureTreeOriginal = undefined; } } if (programStructureTreeOriginal !== null) { if (programStructureTreeOriginal !== undefined) { const originalFunctionIdentifier = programStructureTreeOriginal.identifierBySourceLocation({ line: originalPosition.line, column: originalPosition.column }); functionIdentifierPresentInOriginalFile = programStructureTreeOriginal.sourceLocationOfIdentifier(functionIdentifier) !== null; sourceNodeLocation = { relativeFilePath: relativeOriginalSourcePath, functionIdentifier: originalFunctionIdentifier }; } } else { if (urlProtocol === 'webpack' || urlProtocol === 'webpack-internal') { originalSourceFileNotFoundError = { originalPositionSource: originalPosition.source, relativeOriginalSourcePath: relativeOriginalSourcePath.toString(), absoluteOriginalSourcePath: absoluteOriginalSourcePath.toString(), originalPositionPath: originalPositionPath.toString() }; } } } else { if (sourceMap) { // there is a sourcemap but the original position could not be resolved functionIdentifierPresentInOriginalFile = false; } } if (sourceNodeLocation === undefined) { // if the executed source code is original, has no source map // or the source map does not contain the original source location sourceNodeLocation = { relativeFilePath: sourceLocation.relativeUrl, functionIdentifier }; } let { relativeNodeModulePath, nodeModule } = { relativeNodeModulePath: null, nodeModule: null }; if (sourceNodeLocation.relativeFilePath.toString() !== './') { // determine the node module of the source node location if there is one ; ({ relativeNodeModulePath, nodeModule } = this.nodeModuleFromFilePath(sourceNodeLocation.relativeFilePath)); if (relativeNodeModulePath && nodeModule) { // since the source node location is within a node module // adjust the relativeFilePath so its relative to that node module directory sourceNodeLocation.relativeFilePath = relativeNodeModulePath.pathTo(sourceNodeLocation.relativeFilePath); } } else { sourceNodeLocation.relativeFilePath = new UnifiedPath_1.UnifiedPath(constants_1.UNKNOWN_SCRIPTS_FOLDER_NAME).join(sourceLocation.scriptID); functionIdentifierPresentInOriginalFile = false; } if (originalSourceFileNotFoundError !== undefined && (!relativeNodeModulePath || !nodeModule)) { // The original source file does not exist, only print an error if: // - the source file is NOT part of a node module, // since node modules often include source maps that point to non-existing files we ignore them if (this.hideOriginalSourceFileNotExistErrors === false) { LoggerHelper_1.LoggerHelper.error('ResolveFunctionIdentifierHelper.resolveFunctionIdentifier: original source file does not exist', { rootDir: this.rootDir.toString(), sources: sourceMap === null || sourceMap === void 0 ? void 0 : sourceMap.sources, url: sourceLocation.absoluteUrl.toString(), scriptID: sourceLocation.scriptID, lineNumber, columnNumber, triedToParse: originalSourceFileNotFoundError }); } } if (functionIdentifier === '') { LoggerHelper_1.LoggerHelper.error('ResolveFunctionIdentifierHelper.resolveFunctionIdentifier: functionIdentifier should not be empty', { url: sourceLocation.absoluteUrl.toString(), scriptID: sourceLocation.scriptID, lineNumber, columnNumber }); throw new Error('ResolveFunctionIdentifierHelper.resolveFunctionIdentifier: functionIdentifier should not be empty'); } const result = { sourceNodeLocation, functionIdentifierPresentInOriginalFile, relativeNodeModulePath, nodeModule }; // cache result this.functionIdentifierCache.set(sourceLocation.index, result); return result; }); } /** * Why does this function exists? * * Example source code: * 1 methodABC(title, highResolutionStopTime) { * 2 var _a, _b, _c; * 3 return __awaiter(this, void 0, void 0, function* () { * 4 // do something * 5 }); * 6 } * * If a source mapping exists for every line except line 2 and 3 * and the function identifier is requested for line 2 or 3 the source map will return undefined. * * So the ProgramStructureTree node has to be resolved for that location. * This will return the parent function (methodABC) and its corresponding scope for line 2 and 3, * since the ProgramStructureTree treats the __awaiter function as part of the methodABC function. * * Then the sourcemap can be used to resolve the original source location of the function methodABC. * * If the sourcemap still returns undefined, * the requested source code location is not part of the original source code. * */ static resolveMappedLocationFromSourceMap(programStructureTreeNodeScript, sourceMap, lineNumber, columnNumber) { return __awaiter(this, void 0, void 0, function* () { const originalPosition = yield sourceMap.getOriginalSourceLocation(lineNumber, columnNumber); // check if position could be resolved if (originalPosition && originalPosition.source) { return originalPosition; } else { // if position could not be resolved // resolve function via ProgramStructureTree and try to resolve the original position again const identifierNode = programStructureTreeNodeScript.identifierNodeBySourceLocation({ line: lineNumber, column: columnNumber }); if (identifierNode === undefined) { return undefined; } return sourceMap.getOriginalSourceLocation(identifierNode.node.beginLoc.line, identifierNode.node.beginLoc.column); } }); } } exports.ResolveFunctionIdentifierHelper = ResolveFunctionIdentifierHelper; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVzb2x2ZUZ1bmN0aW9uSWRlbnRpZmllckhlbHBlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9oZWxwZXIvUmVzb2x2ZUZ1bmN0aW9uSWRlbnRpZmllckhlbHBlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7QUFBQSxnREFBdUI7QUFJdkIsaURBQTZDO0FBQzdDLHlEQUFxRDtBQUNyRCwyREFBdUQ7QUFDdkQsdURBQW1EO0FBSW5ELDRDQUEwRDtBQUkxRCx1REFBbUQ7QUFrQm5EOzs7Ozs7O0dBT0c7QUFDSCxNQUFhLCtCQUErQjtJQXdCM0MsWUFDQyxPQUFvQixFQUNwQixzQkFBOEM7UUF6QnhDLHlDQUFvQyxHQUFHLElBQUksQ0FBQTtRQTJCakQsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUE7UUFDdEIsSUFBSSxDQUFDLHNCQUFzQixHQUFHLHNCQUFzQixDQUFBO1FBQ3BELElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFBO1FBQ2pDLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFBO1FBQ25DLElBQUksQ0FBQyx1QkFBdUIsR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFBO1FBQ3hDLElBQUksQ0FBQyx1QkFBdUIsR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFBO0lBQ3pDLENBQUM7SUFFRDs7T0FFRztJQUNLLHNCQUFzQixDQUM3QixnQkFBNkI7UUFFN0IsSUFBSSxVQUFVLEdBQUcsSUFBSSxDQUFDLHVCQUF1QixDQUFDLEdBQUcsQ0FDaEQsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLENBQzNCLENBQUE7UUFDRCxJQUFJLFVBQVUsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUM5QixPQUFPLFVBQVUsQ0FBQTtRQUNsQixDQUFDO1FBRUQsVUFBVSxHQUFHLGlDQUFlLENBQUMsc0JBQXNCLENBQ2xELElBQUksQ0FBQyxzQkFBc0IsRUFDM0IsZ0JB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