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