11cb0ef41Sopenharmony_ci// Copyright 2009 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Redistribution and use in source and binary forms, with or without 31cb0ef41Sopenharmony_ci// modification, are permitted provided that the following conditions are 41cb0ef41Sopenharmony_ci// met: 51cb0ef41Sopenharmony_ci// 61cb0ef41Sopenharmony_ci// * Redistributions of source code must retain the above copyright 71cb0ef41Sopenharmony_ci// notice, this list of conditions and the following disclaimer. 81cb0ef41Sopenharmony_ci// * Redistributions in binary form must reproduce the above 91cb0ef41Sopenharmony_ci// copyright notice, this list of conditions and the following 101cb0ef41Sopenharmony_ci// disclaimer in the documentation and/or other materials provided 111cb0ef41Sopenharmony_ci// with the distribution. 121cb0ef41Sopenharmony_ci// * Neither the name of Google Inc. nor the names of its 131cb0ef41Sopenharmony_ci// contributors may be used to endorse or promote products derived 141cb0ef41Sopenharmony_ci// from this software without specific prior written permission. 151cb0ef41Sopenharmony_ci// 161cb0ef41Sopenharmony_ci// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 171cb0ef41Sopenharmony_ci// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 181cb0ef41Sopenharmony_ci// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 191cb0ef41Sopenharmony_ci// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 201cb0ef41Sopenharmony_ci// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 211cb0ef41Sopenharmony_ci// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 221cb0ef41Sopenharmony_ci// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 231cb0ef41Sopenharmony_ci// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 241cb0ef41Sopenharmony_ci// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 251cb0ef41Sopenharmony_ci// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 261cb0ef41Sopenharmony_ci// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 271cb0ef41Sopenharmony_ci 281cb0ef41Sopenharmony_ciimport { CodeMap, CodeEntry } from "./codemap.mjs"; 291cb0ef41Sopenharmony_ciimport { ConsArray } from "./consarray.mjs"; 301cb0ef41Sopenharmony_ciimport { WebInspector } from "./sourcemap.mjs"; 311cb0ef41Sopenharmony_ci 321cb0ef41Sopenharmony_ci// Used to associate log entries with source positions in scripts. 331cb0ef41Sopenharmony_ci// TODO: move to separate modules 341cb0ef41Sopenharmony_ciexport class SourcePosition { 351cb0ef41Sopenharmony_ci script = null; 361cb0ef41Sopenharmony_ci line = -1; 371cb0ef41Sopenharmony_ci column = -1; 381cb0ef41Sopenharmony_ci entries = []; 391cb0ef41Sopenharmony_ci isFunction = false; 401cb0ef41Sopenharmony_ci originalPosition = undefined; 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ci constructor(script, line, column) { 431cb0ef41Sopenharmony_ci this.script = script; 441cb0ef41Sopenharmony_ci this.line = line; 451cb0ef41Sopenharmony_ci this.column = column; 461cb0ef41Sopenharmony_ci } 471cb0ef41Sopenharmony_ci 481cb0ef41Sopenharmony_ci addEntry(entry) { 491cb0ef41Sopenharmony_ci this.entries.push(entry); 501cb0ef41Sopenharmony_ci } 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ci toString() { 531cb0ef41Sopenharmony_ci return `${this.script.name}:${this.line}:${this.column}`; 541cb0ef41Sopenharmony_ci } 551cb0ef41Sopenharmony_ci 561cb0ef41Sopenharmony_ci get functionPosition() { 571cb0ef41Sopenharmony_ci // TODO(cbruni) 581cb0ef41Sopenharmony_ci return undefined; 591cb0ef41Sopenharmony_ci } 601cb0ef41Sopenharmony_ci 611cb0ef41Sopenharmony_ci get toolTipDict() { 621cb0ef41Sopenharmony_ci return { 631cb0ef41Sopenharmony_ci title: this.toString(), 641cb0ef41Sopenharmony_ci __this__: this, 651cb0ef41Sopenharmony_ci script: this.script, 661cb0ef41Sopenharmony_ci entries: this.entries, 671cb0ef41Sopenharmony_ci } 681cb0ef41Sopenharmony_ci } 691cb0ef41Sopenharmony_ci} 701cb0ef41Sopenharmony_ci 711cb0ef41Sopenharmony_ciexport class Script { 721cb0ef41Sopenharmony_ci url; 731cb0ef41Sopenharmony_ci source = ""; 741cb0ef41Sopenharmony_ci name; 751cb0ef41Sopenharmony_ci sourcePosition = undefined; 761cb0ef41Sopenharmony_ci // Map<line, Map<column, SourcePosition>> 771cb0ef41Sopenharmony_ci lineToColumn = new Map(); 781cb0ef41Sopenharmony_ci _entries = []; 791cb0ef41Sopenharmony_ci _sourceMapState = "unknown"; 801cb0ef41Sopenharmony_ci 811cb0ef41Sopenharmony_ci constructor(id) { 821cb0ef41Sopenharmony_ci this.id = id; 831cb0ef41Sopenharmony_ci this.sourcePositions = []; 841cb0ef41Sopenharmony_ci } 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_ci update(url, source) { 871cb0ef41Sopenharmony_ci this.url = url; 881cb0ef41Sopenharmony_ci this.name = Script.getShortestUniqueName(url, this); 891cb0ef41Sopenharmony_ci this.source = source; 901cb0ef41Sopenharmony_ci } 911cb0ef41Sopenharmony_ci 921cb0ef41Sopenharmony_ci get length() { 931cb0ef41Sopenharmony_ci return this.source.length; 941cb0ef41Sopenharmony_ci } 951cb0ef41Sopenharmony_ci 961cb0ef41Sopenharmony_ci get entries() { 971cb0ef41Sopenharmony_ci return this._entries; 981cb0ef41Sopenharmony_ci } 991cb0ef41Sopenharmony_ci 1001cb0ef41Sopenharmony_ci get startLine() { 1011cb0ef41Sopenharmony_ci return this.sourcePosition?.line ?? 1; 1021cb0ef41Sopenharmony_ci } 1031cb0ef41Sopenharmony_ci 1041cb0ef41Sopenharmony_ci get sourceMapState() { 1051cb0ef41Sopenharmony_ci return this._sourceMapState; 1061cb0ef41Sopenharmony_ci } 1071cb0ef41Sopenharmony_ci 1081cb0ef41Sopenharmony_ci findFunctionSourcePosition(sourcePosition) { 1091cb0ef41Sopenharmony_ci // TODO(cbruni): implement 1101cb0ef41Sopenharmony_ci return undefined; 1111cb0ef41Sopenharmony_ci } 1121cb0ef41Sopenharmony_ci 1131cb0ef41Sopenharmony_ci addSourcePosition(line, column, entry) { 1141cb0ef41Sopenharmony_ci let sourcePosition = this.lineToColumn.get(line)?.get(column); 1151cb0ef41Sopenharmony_ci if (sourcePosition === undefined) { 1161cb0ef41Sopenharmony_ci sourcePosition = new SourcePosition(this, line, column,) 1171cb0ef41Sopenharmony_ci this._addSourcePosition(line, column, sourcePosition); 1181cb0ef41Sopenharmony_ci } 1191cb0ef41Sopenharmony_ci if (this.sourcePosition === undefined && entry.entry?.type === "Script") { 1201cb0ef41Sopenharmony_ci // Mark the source position of scripts, for inline scripts which don't 1211cb0ef41Sopenharmony_ci // start at line 1. 1221cb0ef41Sopenharmony_ci this.sourcePosition = sourcePosition; 1231cb0ef41Sopenharmony_ci } 1241cb0ef41Sopenharmony_ci sourcePosition.addEntry(entry); 1251cb0ef41Sopenharmony_ci this._entries.push(entry); 1261cb0ef41Sopenharmony_ci return sourcePosition; 1271cb0ef41Sopenharmony_ci } 1281cb0ef41Sopenharmony_ci 1291cb0ef41Sopenharmony_ci _addSourcePosition(line, column, sourcePosition) { 1301cb0ef41Sopenharmony_ci let columnToSourcePosition; 1311cb0ef41Sopenharmony_ci if (this.lineToColumn.has(line)) { 1321cb0ef41Sopenharmony_ci columnToSourcePosition = this.lineToColumn.get(line); 1331cb0ef41Sopenharmony_ci } else { 1341cb0ef41Sopenharmony_ci columnToSourcePosition = new Map(); 1351cb0ef41Sopenharmony_ci this.lineToColumn.set(line, columnToSourcePosition); 1361cb0ef41Sopenharmony_ci } 1371cb0ef41Sopenharmony_ci this.sourcePositions.push(sourcePosition); 1381cb0ef41Sopenharmony_ci columnToSourcePosition.set(column, sourcePosition); 1391cb0ef41Sopenharmony_ci } 1401cb0ef41Sopenharmony_ci 1411cb0ef41Sopenharmony_ci toString() { 1421cb0ef41Sopenharmony_ci return `Script(${this.id}): ${this.name}`; 1431cb0ef41Sopenharmony_ci } 1441cb0ef41Sopenharmony_ci 1451cb0ef41Sopenharmony_ci get toolTipDict() { 1461cb0ef41Sopenharmony_ci return { 1471cb0ef41Sopenharmony_ci title: this.toString(), 1481cb0ef41Sopenharmony_ci __this__: this, 1491cb0ef41Sopenharmony_ci id: this.id, 1501cb0ef41Sopenharmony_ci url: this.url, 1511cb0ef41Sopenharmony_ci source: this.source, 1521cb0ef41Sopenharmony_ci sourcePositions: this.sourcePositions 1531cb0ef41Sopenharmony_ci } 1541cb0ef41Sopenharmony_ci } 1551cb0ef41Sopenharmony_ci 1561cb0ef41Sopenharmony_ci static getShortestUniqueName(url, script) { 1571cb0ef41Sopenharmony_ci const parts = url.split('/'); 1581cb0ef41Sopenharmony_ci const filename = parts[parts.length -1]; 1591cb0ef41Sopenharmony_ci const dict = this._dict ?? (this._dict = new Map()); 1601cb0ef41Sopenharmony_ci const matchingScripts = dict.get(filename); 1611cb0ef41Sopenharmony_ci if (matchingScripts == undefined) { 1621cb0ef41Sopenharmony_ci dict.set(filename, [script]); 1631cb0ef41Sopenharmony_ci return filename; 1641cb0ef41Sopenharmony_ci } 1651cb0ef41Sopenharmony_ci // TODO: find shortest unique substring 1661cb0ef41Sopenharmony_ci // Update all matching scripts to have a unique filename again. 1671cb0ef41Sopenharmony_ci for (let matchingScript of matchingScripts) { 1681cb0ef41Sopenharmony_ci matchingScript.name = script.url 1691cb0ef41Sopenharmony_ci } 1701cb0ef41Sopenharmony_ci matchingScripts.push(script); 1711cb0ef41Sopenharmony_ci return url; 1721cb0ef41Sopenharmony_ci } 1731cb0ef41Sopenharmony_ci 1741cb0ef41Sopenharmony_ci ensureSourceMapCalculated(sourceMapFetchPrefix=undefined) { 1751cb0ef41Sopenharmony_ci if (this._sourceMapState !== "unknown") return; 1761cb0ef41Sopenharmony_ci 1771cb0ef41Sopenharmony_ci const sourceMapURLMatch = 1781cb0ef41Sopenharmony_ci this.source.match(/\/\/# sourceMappingURL=(.*)\n/); 1791cb0ef41Sopenharmony_ci if (!sourceMapURLMatch) { 1801cb0ef41Sopenharmony_ci this._sourceMapState = "none"; 1811cb0ef41Sopenharmony_ci return; 1821cb0ef41Sopenharmony_ci } 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ci this._sourceMapState = "loading"; 1851cb0ef41Sopenharmony_ci let sourceMapURL = sourceMapURLMatch[1]; 1861cb0ef41Sopenharmony_ci (async () => { 1871cb0ef41Sopenharmony_ci try { 1881cb0ef41Sopenharmony_ci let sourceMapPayload; 1891cb0ef41Sopenharmony_ci try { 1901cb0ef41Sopenharmony_ci sourceMapPayload = await fetch(sourceMapURL); 1911cb0ef41Sopenharmony_ci } catch (e) { 1921cb0ef41Sopenharmony_ci if (e instanceof TypeError && sourceMapFetchPrefix) { 1931cb0ef41Sopenharmony_ci // Try again with fetch prefix. 1941cb0ef41Sopenharmony_ci // TODO(leszeks): Remove the retry once the prefix is 1951cb0ef41Sopenharmony_ci // configurable. 1961cb0ef41Sopenharmony_ci sourceMapPayload = 1971cb0ef41Sopenharmony_ci await fetch(sourceMapFetchPrefix + sourceMapURL); 1981cb0ef41Sopenharmony_ci } else { 1991cb0ef41Sopenharmony_ci throw e; 2001cb0ef41Sopenharmony_ci } 2011cb0ef41Sopenharmony_ci } 2021cb0ef41Sopenharmony_ci sourceMapPayload = await sourceMapPayload.text(); 2031cb0ef41Sopenharmony_ci 2041cb0ef41Sopenharmony_ci if (sourceMapPayload.startsWith(')]}')) { 2051cb0ef41Sopenharmony_ci sourceMapPayload = 2061cb0ef41Sopenharmony_ci sourceMapPayload.substring(sourceMapPayload.indexOf('\n')); 2071cb0ef41Sopenharmony_ci } 2081cb0ef41Sopenharmony_ci sourceMapPayload = JSON.parse(sourceMapPayload); 2091cb0ef41Sopenharmony_ci const sourceMap = 2101cb0ef41Sopenharmony_ci new WebInspector.SourceMap(sourceMapURL, sourceMapPayload); 2111cb0ef41Sopenharmony_ci 2121cb0ef41Sopenharmony_ci const startLine = this.startLine; 2131cb0ef41Sopenharmony_ci for (const sourcePosition of this.sourcePositions) { 2141cb0ef41Sopenharmony_ci const line = sourcePosition.line - startLine; 2151cb0ef41Sopenharmony_ci const column = sourcePosition.column - 1; 2161cb0ef41Sopenharmony_ci const mapping = sourceMap.findEntry(line, column); 2171cb0ef41Sopenharmony_ci if (mapping) { 2181cb0ef41Sopenharmony_ci sourcePosition.originalPosition = { 2191cb0ef41Sopenharmony_ci source: new URL(mapping[2], sourceMapURL).href, 2201cb0ef41Sopenharmony_ci line: mapping[3] + 1, 2211cb0ef41Sopenharmony_ci column: mapping[4] + 1 2221cb0ef41Sopenharmony_ci }; 2231cb0ef41Sopenharmony_ci } else { 2241cb0ef41Sopenharmony_ci sourcePosition.originalPosition = {source: null, line:0, column:0}; 2251cb0ef41Sopenharmony_ci } 2261cb0ef41Sopenharmony_ci } 2271cb0ef41Sopenharmony_ci this._sourceMapState = "loaded"; 2281cb0ef41Sopenharmony_ci } catch (e) { 2291cb0ef41Sopenharmony_ci console.error(e); 2301cb0ef41Sopenharmony_ci this._sourceMapState = "failed"; 2311cb0ef41Sopenharmony_ci } 2321cb0ef41Sopenharmony_ci })(); 2331cb0ef41Sopenharmony_ci } 2341cb0ef41Sopenharmony_ci} 2351cb0ef41Sopenharmony_ci 2361cb0ef41Sopenharmony_ci 2371cb0ef41Sopenharmony_ciconst kOffsetPairRegex = /C([0-9]+)O([0-9]+)/g; 2381cb0ef41Sopenharmony_ciclass SourcePositionTable { 2391cb0ef41Sopenharmony_ci constructor(encodedTable) { 2401cb0ef41Sopenharmony_ci this._offsets = []; 2411cb0ef41Sopenharmony_ci while (true) { 2421cb0ef41Sopenharmony_ci const regexResult = kOffsetPairRegex.exec(encodedTable); 2431cb0ef41Sopenharmony_ci if (!regexResult) break; 2441cb0ef41Sopenharmony_ci const codeOffset = parseInt(regexResult[1]); 2451cb0ef41Sopenharmony_ci const scriptOffset = parseInt(regexResult[2]); 2461cb0ef41Sopenharmony_ci if (isNaN(codeOffset) || isNaN(scriptOffset)) continue; 2471cb0ef41Sopenharmony_ci this._offsets.push({code: codeOffset, script: scriptOffset}); 2481cb0ef41Sopenharmony_ci } 2491cb0ef41Sopenharmony_ci } 2501cb0ef41Sopenharmony_ci 2511cb0ef41Sopenharmony_ci getScriptOffset(codeOffset) { 2521cb0ef41Sopenharmony_ci if (codeOffset < 0) { 2531cb0ef41Sopenharmony_ci throw new Exception(`Invalid codeOffset=${codeOffset}, should be >= 0`); 2541cb0ef41Sopenharmony_ci } 2551cb0ef41Sopenharmony_ci for (let i = this.offsetTable.length - 1; i >= 0; i--) { 2561cb0ef41Sopenharmony_ci const offset = this._offsets[i]; 2571cb0ef41Sopenharmony_ci if (offset.code <= codeOffset) { 2581cb0ef41Sopenharmony_ci return offset.script; 2591cb0ef41Sopenharmony_ci } 2601cb0ef41Sopenharmony_ci } 2611cb0ef41Sopenharmony_ci return this._offsets[0].script; 2621cb0ef41Sopenharmony_ci } 2631cb0ef41Sopenharmony_ci} 2641cb0ef41Sopenharmony_ci 2651cb0ef41Sopenharmony_ci 2661cb0ef41Sopenharmony_ciclass SourceInfo { 2671cb0ef41Sopenharmony_ci script; 2681cb0ef41Sopenharmony_ci start; 2691cb0ef41Sopenharmony_ci end; 2701cb0ef41Sopenharmony_ci positions; 2711cb0ef41Sopenharmony_ci inlined; 2721cb0ef41Sopenharmony_ci fns; 2731cb0ef41Sopenharmony_ci disassemble; 2741cb0ef41Sopenharmony_ci 2751cb0ef41Sopenharmony_ci setSourcePositionInfo( 2761cb0ef41Sopenharmony_ci script, startPos, endPos, sourcePositionTableData, inliningPositions, 2771cb0ef41Sopenharmony_ci inlinedFunctions) { 2781cb0ef41Sopenharmony_ci this.script = script; 2791cb0ef41Sopenharmony_ci this.start = startPos; 2801cb0ef41Sopenharmony_ci this.end = endPos; 2811cb0ef41Sopenharmony_ci this.positions = sourcePositionTableData; 2821cb0ef41Sopenharmony_ci this.inlined = inliningPositions; 2831cb0ef41Sopenharmony_ci this.fns = inlinedFunctions; 2841cb0ef41Sopenharmony_ci this.sourcePositionTable = new SourcePositionTable(sourcePositionTableData); 2851cb0ef41Sopenharmony_ci } 2861cb0ef41Sopenharmony_ci 2871cb0ef41Sopenharmony_ci setDisassemble(code) { 2881cb0ef41Sopenharmony_ci this.disassemble = code; 2891cb0ef41Sopenharmony_ci } 2901cb0ef41Sopenharmony_ci 2911cb0ef41Sopenharmony_ci getSourceCode() { 2921cb0ef41Sopenharmony_ci return this.script.source?.substring(this.start, this.end); 2931cb0ef41Sopenharmony_ci } 2941cb0ef41Sopenharmony_ci} 2951cb0ef41Sopenharmony_ci 2961cb0ef41Sopenharmony_ciconst kProfileOperationMove = 0; 2971cb0ef41Sopenharmony_ciconst kProfileOperationDelete = 1; 2981cb0ef41Sopenharmony_ciconst kProfileOperationTick = 2; 2991cb0ef41Sopenharmony_ci 3001cb0ef41Sopenharmony_ci/** 3011cb0ef41Sopenharmony_ci * Creates a profile object for processing profiling-related events 3021cb0ef41Sopenharmony_ci * and calculating function execution times. 3031cb0ef41Sopenharmony_ci * 3041cb0ef41Sopenharmony_ci * @constructor 3051cb0ef41Sopenharmony_ci */ 3061cb0ef41Sopenharmony_ciexport class Profile { 3071cb0ef41Sopenharmony_ci codeMap_ = new CodeMap(); 3081cb0ef41Sopenharmony_ci topDownTree_ = new CallTree(); 3091cb0ef41Sopenharmony_ci bottomUpTree_ = new CallTree(); 3101cb0ef41Sopenharmony_ci c_entries_ = {__proto__:null}; 3111cb0ef41Sopenharmony_ci scripts_ = []; 3121cb0ef41Sopenharmony_ci urlToScript_ = new Map(); 3131cb0ef41Sopenharmony_ci warnings = new Set(); 3141cb0ef41Sopenharmony_ci 3151cb0ef41Sopenharmony_ci serializeVMSymbols() { 3161cb0ef41Sopenharmony_ci let result = this.codeMap_.getAllStaticEntriesWithAddresses(); 3171cb0ef41Sopenharmony_ci result.concat(this.codeMap_.getAllLibraryEntriesWithAddresses()) 3181cb0ef41Sopenharmony_ci return result.map(([startAddress, codeEntry]) => { 3191cb0ef41Sopenharmony_ci return [codeEntry.getName(), startAddress, startAddress + codeEntry.size] 3201cb0ef41Sopenharmony_ci }); 3211cb0ef41Sopenharmony_ci } 3221cb0ef41Sopenharmony_ci 3231cb0ef41Sopenharmony_ci /** 3241cb0ef41Sopenharmony_ci * Returns whether a function with the specified name must be skipped. 3251cb0ef41Sopenharmony_ci * Should be overridden by subclasses. 3261cb0ef41Sopenharmony_ci * 3271cb0ef41Sopenharmony_ci * @param {string} name Function name. 3281cb0ef41Sopenharmony_ci */ 3291cb0ef41Sopenharmony_ci skipThisFunction(name) { 3301cb0ef41Sopenharmony_ci return false; 3311cb0ef41Sopenharmony_ci } 3321cb0ef41Sopenharmony_ci 3331cb0ef41Sopenharmony_ci /** 3341cb0ef41Sopenharmony_ci * Enum for profiler operations that involve looking up existing 3351cb0ef41Sopenharmony_ci * code entries. 3361cb0ef41Sopenharmony_ci * 3371cb0ef41Sopenharmony_ci * @enum {number} 3381cb0ef41Sopenharmony_ci */ 3391cb0ef41Sopenharmony_ci static Operation = { 3401cb0ef41Sopenharmony_ci MOVE: kProfileOperationMove, 3411cb0ef41Sopenharmony_ci DELETE: kProfileOperationDelete, 3421cb0ef41Sopenharmony_ci TICK: kProfileOperationTick 3431cb0ef41Sopenharmony_ci } 3441cb0ef41Sopenharmony_ci 3451cb0ef41Sopenharmony_ci /** 3461cb0ef41Sopenharmony_ci * Enum for code state regarding its dynamic optimization. 3471cb0ef41Sopenharmony_ci * 3481cb0ef41Sopenharmony_ci * @enum {number} 3491cb0ef41Sopenharmony_ci */ 3501cb0ef41Sopenharmony_ci static CodeState = { 3511cb0ef41Sopenharmony_ci COMPILED: 0, 3521cb0ef41Sopenharmony_ci IGNITION: 1, 3531cb0ef41Sopenharmony_ci BASELINE: 2, 3541cb0ef41Sopenharmony_ci TURBOFAN: 5, 3551cb0ef41Sopenharmony_ci } 3561cb0ef41Sopenharmony_ci 3571cb0ef41Sopenharmony_ci static VMState = { 3581cb0ef41Sopenharmony_ci JS: 0, 3591cb0ef41Sopenharmony_ci GC: 1, 3601cb0ef41Sopenharmony_ci PARSER: 2, 3611cb0ef41Sopenharmony_ci BYTECODE_COMPILER: 3, 3621cb0ef41Sopenharmony_ci // TODO(cbruni): add BASELINE_COMPILER 3631cb0ef41Sopenharmony_ci COMPILER: 4, 3641cb0ef41Sopenharmony_ci OTHER: 5, 3651cb0ef41Sopenharmony_ci EXTERNAL: 6, 3661cb0ef41Sopenharmony_ci IDLE: 7, 3671cb0ef41Sopenharmony_ci } 3681cb0ef41Sopenharmony_ci 3691cb0ef41Sopenharmony_ci static CodeType = { 3701cb0ef41Sopenharmony_ci CPP: 0, 3711cb0ef41Sopenharmony_ci SHARED_LIB: 1 3721cb0ef41Sopenharmony_ci } 3731cb0ef41Sopenharmony_ci 3741cb0ef41Sopenharmony_ci /** 3751cb0ef41Sopenharmony_ci * Parser for dynamic code optimization state. 3761cb0ef41Sopenharmony_ci */ 3771cb0ef41Sopenharmony_ci static parseState(s) { 3781cb0ef41Sopenharmony_ci switch (s) { 3791cb0ef41Sopenharmony_ci case '': 3801cb0ef41Sopenharmony_ci return this.CodeState.COMPILED; 3811cb0ef41Sopenharmony_ci case '~': 3821cb0ef41Sopenharmony_ci return this.CodeState.IGNITION; 3831cb0ef41Sopenharmony_ci case '^': 3841cb0ef41Sopenharmony_ci return this.CodeState.BASELINE; 3851cb0ef41Sopenharmony_ci case '*': 3861cb0ef41Sopenharmony_ci return this.CodeState.TURBOFAN; 3871cb0ef41Sopenharmony_ci } 3881cb0ef41Sopenharmony_ci throw new Error(`unknown code state: ${s}`); 3891cb0ef41Sopenharmony_ci } 3901cb0ef41Sopenharmony_ci 3911cb0ef41Sopenharmony_ci static getKindFromState(state) { 3921cb0ef41Sopenharmony_ci if (state === this.CodeState.COMPILED) { 3931cb0ef41Sopenharmony_ci return "Builtin"; 3941cb0ef41Sopenharmony_ci } else if (state === this.CodeState.IGNITION) { 3951cb0ef41Sopenharmony_ci return "Unopt"; 3961cb0ef41Sopenharmony_ci } else if (state === this.CodeState.BASELINE) { 3971cb0ef41Sopenharmony_ci return "Baseline"; 3981cb0ef41Sopenharmony_ci } else if (state === this.CodeState.TURBOFAN) { 3991cb0ef41Sopenharmony_ci return "Opt"; 4001cb0ef41Sopenharmony_ci } 4011cb0ef41Sopenharmony_ci throw new Error(`unknown code state: ${state}`); 4021cb0ef41Sopenharmony_ci } 4031cb0ef41Sopenharmony_ci 4041cb0ef41Sopenharmony_ci static vmStateString(state) { 4051cb0ef41Sopenharmony_ci switch (state) { 4061cb0ef41Sopenharmony_ci case this.VMState.JS: 4071cb0ef41Sopenharmony_ci return 'JS'; 4081cb0ef41Sopenharmony_ci case this.VMState.GC: 4091cb0ef41Sopenharmony_ci return 'GC'; 4101cb0ef41Sopenharmony_ci case this.VMState.PARSER: 4111cb0ef41Sopenharmony_ci return 'Parse'; 4121cb0ef41Sopenharmony_ci case this.VMState.BYTECODE_COMPILER: 4131cb0ef41Sopenharmony_ci return 'Compile Bytecode'; 4141cb0ef41Sopenharmony_ci case this.VMState.COMPILER: 4151cb0ef41Sopenharmony_ci return 'Compile'; 4161cb0ef41Sopenharmony_ci case this.VMState.OTHER: 4171cb0ef41Sopenharmony_ci return 'Other'; 4181cb0ef41Sopenharmony_ci case this.VMState.EXTERNAL: 4191cb0ef41Sopenharmony_ci return 'External'; 4201cb0ef41Sopenharmony_ci case this.VMState.IDLE: 4211cb0ef41Sopenharmony_ci return 'Idle'; 4221cb0ef41Sopenharmony_ci } 4231cb0ef41Sopenharmony_ci return 'unknown'; 4241cb0ef41Sopenharmony_ci } 4251cb0ef41Sopenharmony_ci 4261cb0ef41Sopenharmony_ci /** 4271cb0ef41Sopenharmony_ci * Called whenever the specified operation has failed finding a function 4281cb0ef41Sopenharmony_ci * containing the specified address. Should be overriden by subclasses. 4291cb0ef41Sopenharmony_ci * See the Profile.Operation enum for the list of 4301cb0ef41Sopenharmony_ci * possible operations. 4311cb0ef41Sopenharmony_ci * 4321cb0ef41Sopenharmony_ci * @param {number} operation Operation. 4331cb0ef41Sopenharmony_ci * @param {number} addr Address of the unknown code. 4341cb0ef41Sopenharmony_ci * @param {number} opt_stackPos If an unknown address is encountered 4351cb0ef41Sopenharmony_ci * during stack strace processing, specifies a position of the frame 4361cb0ef41Sopenharmony_ci * containing the address. 4371cb0ef41Sopenharmony_ci */ 4381cb0ef41Sopenharmony_ci handleUnknownCode(operation, addr, opt_stackPos) { } 4391cb0ef41Sopenharmony_ci 4401cb0ef41Sopenharmony_ci /** 4411cb0ef41Sopenharmony_ci * Registers a library. 4421cb0ef41Sopenharmony_ci * 4431cb0ef41Sopenharmony_ci * @param {string} name Code entry name. 4441cb0ef41Sopenharmony_ci * @param {number} startAddr Starting address. 4451cb0ef41Sopenharmony_ci * @param {number} endAddr Ending address. 4461cb0ef41Sopenharmony_ci */ 4471cb0ef41Sopenharmony_ci addLibrary(name, startAddr, endAddr) { 4481cb0ef41Sopenharmony_ci const entry = new CodeEntry(endAddr - startAddr, name, 'SHARED_LIB'); 4491cb0ef41Sopenharmony_ci this.codeMap_.addLibrary(startAddr, entry); 4501cb0ef41Sopenharmony_ci return entry; 4511cb0ef41Sopenharmony_ci } 4521cb0ef41Sopenharmony_ci 4531cb0ef41Sopenharmony_ci /** 4541cb0ef41Sopenharmony_ci * Registers statically compiled code entry. 4551cb0ef41Sopenharmony_ci * 4561cb0ef41Sopenharmony_ci * @param {string} name Code entry name. 4571cb0ef41Sopenharmony_ci * @param {number} startAddr Starting address. 4581cb0ef41Sopenharmony_ci * @param {number} endAddr Ending address. 4591cb0ef41Sopenharmony_ci */ 4601cb0ef41Sopenharmony_ci addStaticCode(name, startAddr, endAddr) { 4611cb0ef41Sopenharmony_ci const entry = new CodeEntry(endAddr - startAddr, name, 'CPP'); 4621cb0ef41Sopenharmony_ci this.codeMap_.addStaticCode(startAddr, entry); 4631cb0ef41Sopenharmony_ci return entry; 4641cb0ef41Sopenharmony_ci } 4651cb0ef41Sopenharmony_ci 4661cb0ef41Sopenharmony_ci /** 4671cb0ef41Sopenharmony_ci * Registers dynamic (JIT-compiled) code entry. 4681cb0ef41Sopenharmony_ci * 4691cb0ef41Sopenharmony_ci * @param {string} type Code entry type. 4701cb0ef41Sopenharmony_ci * @param {string} name Code entry name. 4711cb0ef41Sopenharmony_ci * @param {number} start Starting address. 4721cb0ef41Sopenharmony_ci * @param {number} size Code entry size. 4731cb0ef41Sopenharmony_ci */ 4741cb0ef41Sopenharmony_ci addCode(type, name, timestamp, start, size) { 4751cb0ef41Sopenharmony_ci const entry = new DynamicCodeEntry(size, type, name); 4761cb0ef41Sopenharmony_ci this.codeMap_.addCode(start, entry); 4771cb0ef41Sopenharmony_ci return entry; 4781cb0ef41Sopenharmony_ci } 4791cb0ef41Sopenharmony_ci 4801cb0ef41Sopenharmony_ci /** 4811cb0ef41Sopenharmony_ci * Registers dynamic (JIT-compiled) code entry. 4821cb0ef41Sopenharmony_ci * 4831cb0ef41Sopenharmony_ci * @param {string} type Code entry type. 4841cb0ef41Sopenharmony_ci * @param {string} name Code entry name. 4851cb0ef41Sopenharmony_ci * @param {number} start Starting address. 4861cb0ef41Sopenharmony_ci * @param {number} size Code entry size. 4871cb0ef41Sopenharmony_ci * @param {number} funcAddr Shared function object address. 4881cb0ef41Sopenharmony_ci * @param {Profile.CodeState} state Optimization state. 4891cb0ef41Sopenharmony_ci */ 4901cb0ef41Sopenharmony_ci addFuncCode(type, name, timestamp, start, size, funcAddr, state) { 4911cb0ef41Sopenharmony_ci // As code and functions are in the same address space, 4921cb0ef41Sopenharmony_ci // it is safe to put them in a single code map. 4931cb0ef41Sopenharmony_ci let func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr); 4941cb0ef41Sopenharmony_ci if (func === null) { 4951cb0ef41Sopenharmony_ci func = new FunctionEntry(name); 4961cb0ef41Sopenharmony_ci this.codeMap_.addCode(funcAddr, func); 4971cb0ef41Sopenharmony_ci } else if (func.name !== name) { 4981cb0ef41Sopenharmony_ci // Function object has been overwritten with a new one. 4991cb0ef41Sopenharmony_ci func.name = name; 5001cb0ef41Sopenharmony_ci } 5011cb0ef41Sopenharmony_ci let entry = this.codeMap_.findDynamicEntryByStartAddress(start); 5021cb0ef41Sopenharmony_ci if (entry !== null) { 5031cb0ef41Sopenharmony_ci if (entry.size === size && entry.func === func) { 5041cb0ef41Sopenharmony_ci // Entry state has changed. 5051cb0ef41Sopenharmony_ci entry.state = state; 5061cb0ef41Sopenharmony_ci } else { 5071cb0ef41Sopenharmony_ci this.codeMap_.deleteCode(start); 5081cb0ef41Sopenharmony_ci entry = null; 5091cb0ef41Sopenharmony_ci } 5101cb0ef41Sopenharmony_ci } 5111cb0ef41Sopenharmony_ci if (entry === null) { 5121cb0ef41Sopenharmony_ci entry = new DynamicFuncCodeEntry(size, type, func, state); 5131cb0ef41Sopenharmony_ci this.codeMap_.addCode(start, entry); 5141cb0ef41Sopenharmony_ci } 5151cb0ef41Sopenharmony_ci return entry; 5161cb0ef41Sopenharmony_ci } 5171cb0ef41Sopenharmony_ci 5181cb0ef41Sopenharmony_ci /** 5191cb0ef41Sopenharmony_ci * Reports about moving of a dynamic code entry. 5201cb0ef41Sopenharmony_ci * 5211cb0ef41Sopenharmony_ci * @param {number} from Current code entry address. 5221cb0ef41Sopenharmony_ci * @param {number} to New code entry address. 5231cb0ef41Sopenharmony_ci */ 5241cb0ef41Sopenharmony_ci moveCode(from, to) { 5251cb0ef41Sopenharmony_ci try { 5261cb0ef41Sopenharmony_ci this.codeMap_.moveCode(from, to); 5271cb0ef41Sopenharmony_ci } catch (e) { 5281cb0ef41Sopenharmony_ci this.handleUnknownCode(kProfileOperationMove, from); 5291cb0ef41Sopenharmony_ci } 5301cb0ef41Sopenharmony_ci } 5311cb0ef41Sopenharmony_ci 5321cb0ef41Sopenharmony_ci deoptCode(timestamp, code, inliningId, scriptOffset, bailoutType, 5331cb0ef41Sopenharmony_ci sourcePositionText, deoptReasonText) { 5341cb0ef41Sopenharmony_ci } 5351cb0ef41Sopenharmony_ci 5361cb0ef41Sopenharmony_ci /** 5371cb0ef41Sopenharmony_ci * Reports about deletion of a dynamic code entry. 5381cb0ef41Sopenharmony_ci * 5391cb0ef41Sopenharmony_ci * @param {number} start Starting address. 5401cb0ef41Sopenharmony_ci */ 5411cb0ef41Sopenharmony_ci deleteCode(start) { 5421cb0ef41Sopenharmony_ci try { 5431cb0ef41Sopenharmony_ci this.codeMap_.deleteCode(start); 5441cb0ef41Sopenharmony_ci } catch (e) { 5451cb0ef41Sopenharmony_ci this.handleUnknownCode(kProfileOperationDelete, start); 5461cb0ef41Sopenharmony_ci } 5471cb0ef41Sopenharmony_ci } 5481cb0ef41Sopenharmony_ci 5491cb0ef41Sopenharmony_ci /** 5501cb0ef41Sopenharmony_ci * Adds source positions for given code. 5511cb0ef41Sopenharmony_ci */ 5521cb0ef41Sopenharmony_ci addSourcePositions(start, scriptId, startPos, endPos, sourcePositionTable, 5531cb0ef41Sopenharmony_ci inliningPositions, inlinedFunctions) { 5541cb0ef41Sopenharmony_ci const script = this.getOrCreateScript(scriptId); 5551cb0ef41Sopenharmony_ci const entry = this.codeMap_.findDynamicEntryByStartAddress(start); 5561cb0ef41Sopenharmony_ci if (entry === null) return; 5571cb0ef41Sopenharmony_ci // Resolve the inlined functions list. 5581cb0ef41Sopenharmony_ci if (inlinedFunctions.length > 0) { 5591cb0ef41Sopenharmony_ci inlinedFunctions = inlinedFunctions.substring(1).split("S"); 5601cb0ef41Sopenharmony_ci for (let i = 0; i < inlinedFunctions.length; i++) { 5611cb0ef41Sopenharmony_ci const funcAddr = parseInt(inlinedFunctions[i]); 5621cb0ef41Sopenharmony_ci const func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr); 5631cb0ef41Sopenharmony_ci if (func === null || func.funcId === undefined) { 5641cb0ef41Sopenharmony_ci // TODO: fix 5651cb0ef41Sopenharmony_ci this.warnings.add(`Could not find function ${inlinedFunctions[i]}`); 5661cb0ef41Sopenharmony_ci inlinedFunctions[i] = null; 5671cb0ef41Sopenharmony_ci } else { 5681cb0ef41Sopenharmony_ci inlinedFunctions[i] = func.funcId; 5691cb0ef41Sopenharmony_ci } 5701cb0ef41Sopenharmony_ci } 5711cb0ef41Sopenharmony_ci } else { 5721cb0ef41Sopenharmony_ci inlinedFunctions = []; 5731cb0ef41Sopenharmony_ci } 5741cb0ef41Sopenharmony_ci 5751cb0ef41Sopenharmony_ci this.getOrCreateSourceInfo(entry).setSourcePositionInfo( 5761cb0ef41Sopenharmony_ci script, startPos, endPos, sourcePositionTable, inliningPositions, 5771cb0ef41Sopenharmony_ci inlinedFunctions); 5781cb0ef41Sopenharmony_ci } 5791cb0ef41Sopenharmony_ci 5801cb0ef41Sopenharmony_ci addDisassemble(start, kind, disassemble) { 5811cb0ef41Sopenharmony_ci const entry = this.codeMap_.findDynamicEntryByStartAddress(start); 5821cb0ef41Sopenharmony_ci if (entry !== null) { 5831cb0ef41Sopenharmony_ci this.getOrCreateSourceInfo(entry).setDisassemble(disassemble); 5841cb0ef41Sopenharmony_ci } 5851cb0ef41Sopenharmony_ci return entry; 5861cb0ef41Sopenharmony_ci } 5871cb0ef41Sopenharmony_ci 5881cb0ef41Sopenharmony_ci getOrCreateSourceInfo(entry) { 5891cb0ef41Sopenharmony_ci return entry.source ?? (entry.source = new SourceInfo()); 5901cb0ef41Sopenharmony_ci } 5911cb0ef41Sopenharmony_ci 5921cb0ef41Sopenharmony_ci addScriptSource(id, url, source) { 5931cb0ef41Sopenharmony_ci const script = this.getOrCreateScript(id); 5941cb0ef41Sopenharmony_ci script.update(url, source); 5951cb0ef41Sopenharmony_ci this.urlToScript_.set(url, script); 5961cb0ef41Sopenharmony_ci } 5971cb0ef41Sopenharmony_ci 5981cb0ef41Sopenharmony_ci getOrCreateScript(id) { 5991cb0ef41Sopenharmony_ci let script = this.scripts_[id]; 6001cb0ef41Sopenharmony_ci if (script === undefined) { 6011cb0ef41Sopenharmony_ci script = new Script(id); 6021cb0ef41Sopenharmony_ci this.scripts_[id] = script; 6031cb0ef41Sopenharmony_ci } 6041cb0ef41Sopenharmony_ci return script; 6051cb0ef41Sopenharmony_ci } 6061cb0ef41Sopenharmony_ci 6071cb0ef41Sopenharmony_ci getScript(url) { 6081cb0ef41Sopenharmony_ci return this.urlToScript_.get(url); 6091cb0ef41Sopenharmony_ci } 6101cb0ef41Sopenharmony_ci 6111cb0ef41Sopenharmony_ci /** 6121cb0ef41Sopenharmony_ci * Reports about moving of a dynamic code entry. 6131cb0ef41Sopenharmony_ci * 6141cb0ef41Sopenharmony_ci * @param {number} from Current code entry address. 6151cb0ef41Sopenharmony_ci * @param {number} to New code entry address. 6161cb0ef41Sopenharmony_ci */ 6171cb0ef41Sopenharmony_ci moveFunc(from, to) { 6181cb0ef41Sopenharmony_ci if (this.codeMap_.findDynamicEntryByStartAddress(from)) { 6191cb0ef41Sopenharmony_ci this.codeMap_.moveCode(from, to); 6201cb0ef41Sopenharmony_ci } 6211cb0ef41Sopenharmony_ci } 6221cb0ef41Sopenharmony_ci 6231cb0ef41Sopenharmony_ci /** 6241cb0ef41Sopenharmony_ci * Retrieves a code entry by an address. 6251cb0ef41Sopenharmony_ci * 6261cb0ef41Sopenharmony_ci * @param {number} addr Entry address. 6271cb0ef41Sopenharmony_ci */ 6281cb0ef41Sopenharmony_ci findEntry(addr) { 6291cb0ef41Sopenharmony_ci return this.codeMap_.findEntry(addr); 6301cb0ef41Sopenharmony_ci } 6311cb0ef41Sopenharmony_ci 6321cb0ef41Sopenharmony_ci /** 6331cb0ef41Sopenharmony_ci * Records a tick event. Stack must contain a sequence of 6341cb0ef41Sopenharmony_ci * addresses starting with the program counter value. 6351cb0ef41Sopenharmony_ci * 6361cb0ef41Sopenharmony_ci * @param {Array<number>} stack Stack sample. 6371cb0ef41Sopenharmony_ci */ 6381cb0ef41Sopenharmony_ci recordTick(time_ns, vmState, stack) { 6391cb0ef41Sopenharmony_ci const {nameStack, entryStack} = this.resolveAndFilterFuncs_(stack); 6401cb0ef41Sopenharmony_ci this.bottomUpTree_.addPath(nameStack); 6411cb0ef41Sopenharmony_ci nameStack.reverse(); 6421cb0ef41Sopenharmony_ci this.topDownTree_.addPath(nameStack); 6431cb0ef41Sopenharmony_ci return entryStack; 6441cb0ef41Sopenharmony_ci } 6451cb0ef41Sopenharmony_ci 6461cb0ef41Sopenharmony_ci /** 6471cb0ef41Sopenharmony_ci * Translates addresses into function names and filters unneeded 6481cb0ef41Sopenharmony_ci * functions. 6491cb0ef41Sopenharmony_ci * 6501cb0ef41Sopenharmony_ci * @param {Array<number>} stack Stack sample. 6511cb0ef41Sopenharmony_ci */ 6521cb0ef41Sopenharmony_ci resolveAndFilterFuncs_(stack) { 6531cb0ef41Sopenharmony_ci const nameStack = []; 6541cb0ef41Sopenharmony_ci const entryStack = []; 6551cb0ef41Sopenharmony_ci let last_seen_c_function = ''; 6561cb0ef41Sopenharmony_ci let look_for_first_c_function = false; 6571cb0ef41Sopenharmony_ci for (let i = 0; i < stack.length; ++i) { 6581cb0ef41Sopenharmony_ci const pc = stack[i]; 6591cb0ef41Sopenharmony_ci const entry = this.codeMap_.findEntry(pc); 6601cb0ef41Sopenharmony_ci if (entry !== null) { 6611cb0ef41Sopenharmony_ci entryStack.push(entry); 6621cb0ef41Sopenharmony_ci const name = entry.getName(); 6631cb0ef41Sopenharmony_ci if (i === 0 && (entry.type === 'CPP' || entry.type === 'SHARED_LIB')) { 6641cb0ef41Sopenharmony_ci look_for_first_c_function = true; 6651cb0ef41Sopenharmony_ci } 6661cb0ef41Sopenharmony_ci if (look_for_first_c_function && entry.type === 'CPP') { 6671cb0ef41Sopenharmony_ci last_seen_c_function = name; 6681cb0ef41Sopenharmony_ci } 6691cb0ef41Sopenharmony_ci if (!this.skipThisFunction(name)) { 6701cb0ef41Sopenharmony_ci nameStack.push(name); 6711cb0ef41Sopenharmony_ci } 6721cb0ef41Sopenharmony_ci } else { 6731cb0ef41Sopenharmony_ci this.handleUnknownCode(kProfileOperationTick, pc, i); 6741cb0ef41Sopenharmony_ci if (i === 0) nameStack.push("UNKNOWN"); 6751cb0ef41Sopenharmony_ci entryStack.push(pc); 6761cb0ef41Sopenharmony_ci } 6771cb0ef41Sopenharmony_ci if (look_for_first_c_function && i > 0 && 6781cb0ef41Sopenharmony_ci (entry === null || entry.type !== 'CPP') 6791cb0ef41Sopenharmony_ci && last_seen_c_function !== '') { 6801cb0ef41Sopenharmony_ci if (this.c_entries_[last_seen_c_function] === undefined) { 6811cb0ef41Sopenharmony_ci this.c_entries_[last_seen_c_function] = 0; 6821cb0ef41Sopenharmony_ci } 6831cb0ef41Sopenharmony_ci this.c_entries_[last_seen_c_function]++; 6841cb0ef41Sopenharmony_ci look_for_first_c_function = false; // Found it, we're done. 6851cb0ef41Sopenharmony_ci } 6861cb0ef41Sopenharmony_ci } 6871cb0ef41Sopenharmony_ci return {nameStack, entryStack}; 6881cb0ef41Sopenharmony_ci } 6891cb0ef41Sopenharmony_ci 6901cb0ef41Sopenharmony_ci /** 6911cb0ef41Sopenharmony_ci * Performs a BF traversal of the top down call graph. 6921cb0ef41Sopenharmony_ci * 6931cb0ef41Sopenharmony_ci * @param {function(CallTreeNode)} f Visitor function. 6941cb0ef41Sopenharmony_ci */ 6951cb0ef41Sopenharmony_ci traverseTopDownTree(f) { 6961cb0ef41Sopenharmony_ci this.topDownTree_.traverse(f); 6971cb0ef41Sopenharmony_ci } 6981cb0ef41Sopenharmony_ci 6991cb0ef41Sopenharmony_ci /** 7001cb0ef41Sopenharmony_ci * Performs a BF traversal of the bottom up call graph. 7011cb0ef41Sopenharmony_ci * 7021cb0ef41Sopenharmony_ci * @param {function(CallTreeNode)} f Visitor function. 7031cb0ef41Sopenharmony_ci */ 7041cb0ef41Sopenharmony_ci traverseBottomUpTree(f) { 7051cb0ef41Sopenharmony_ci this.bottomUpTree_.traverse(f); 7061cb0ef41Sopenharmony_ci } 7071cb0ef41Sopenharmony_ci 7081cb0ef41Sopenharmony_ci /** 7091cb0ef41Sopenharmony_ci * Calculates a top down profile for a node with the specified label. 7101cb0ef41Sopenharmony_ci * If no name specified, returns the whole top down calls tree. 7111cb0ef41Sopenharmony_ci * 7121cb0ef41Sopenharmony_ci * @param {string} opt_label Node label. 7131cb0ef41Sopenharmony_ci */ 7141cb0ef41Sopenharmony_ci getTopDownProfile(opt_label) { 7151cb0ef41Sopenharmony_ci return this.getTreeProfile_(this.topDownTree_, opt_label); 7161cb0ef41Sopenharmony_ci } 7171cb0ef41Sopenharmony_ci 7181cb0ef41Sopenharmony_ci /** 7191cb0ef41Sopenharmony_ci * Calculates a bottom up profile for a node with the specified label. 7201cb0ef41Sopenharmony_ci * If no name specified, returns the whole bottom up calls tree. 7211cb0ef41Sopenharmony_ci * 7221cb0ef41Sopenharmony_ci * @param {string} opt_label Node label. 7231cb0ef41Sopenharmony_ci */ 7241cb0ef41Sopenharmony_ci getBottomUpProfile(opt_label) { 7251cb0ef41Sopenharmony_ci return this.getTreeProfile_(this.bottomUpTree_, opt_label); 7261cb0ef41Sopenharmony_ci } 7271cb0ef41Sopenharmony_ci 7281cb0ef41Sopenharmony_ci /** 7291cb0ef41Sopenharmony_ci * Helper function for calculating a tree profile. 7301cb0ef41Sopenharmony_ci * 7311cb0ef41Sopenharmony_ci * @param {Profile.CallTree} tree Call tree. 7321cb0ef41Sopenharmony_ci * @param {string} opt_label Node label. 7331cb0ef41Sopenharmony_ci */ 7341cb0ef41Sopenharmony_ci getTreeProfile_(tree, opt_label) { 7351cb0ef41Sopenharmony_ci if (!opt_label) { 7361cb0ef41Sopenharmony_ci tree.computeTotalWeights(); 7371cb0ef41Sopenharmony_ci return tree; 7381cb0ef41Sopenharmony_ci } else { 7391cb0ef41Sopenharmony_ci const subTree = tree.cloneSubtree(opt_label); 7401cb0ef41Sopenharmony_ci subTree.computeTotalWeights(); 7411cb0ef41Sopenharmony_ci return subTree; 7421cb0ef41Sopenharmony_ci } 7431cb0ef41Sopenharmony_ci } 7441cb0ef41Sopenharmony_ci 7451cb0ef41Sopenharmony_ci /** 7461cb0ef41Sopenharmony_ci * Calculates a flat profile of callees starting from a node with 7471cb0ef41Sopenharmony_ci * the specified label. If no name specified, starts from the root. 7481cb0ef41Sopenharmony_ci * 7491cb0ef41Sopenharmony_ci * @param {string} opt_label Starting node label. 7501cb0ef41Sopenharmony_ci */ 7511cb0ef41Sopenharmony_ci getFlatProfile(opt_label) { 7521cb0ef41Sopenharmony_ci const counters = new CallTree(); 7531cb0ef41Sopenharmony_ci const rootLabel = opt_label || CallTree.ROOT_NODE_LABEL; 7541cb0ef41Sopenharmony_ci const precs = {__proto__:null}; 7551cb0ef41Sopenharmony_ci precs[rootLabel] = 0; 7561cb0ef41Sopenharmony_ci const root = counters.findOrAddChild(rootLabel); 7571cb0ef41Sopenharmony_ci 7581cb0ef41Sopenharmony_ci this.topDownTree_.computeTotalWeights(); 7591cb0ef41Sopenharmony_ci this.topDownTree_.traverseInDepth( 7601cb0ef41Sopenharmony_ci function onEnter(node) { 7611cb0ef41Sopenharmony_ci if (!(node.label in precs)) { 7621cb0ef41Sopenharmony_ci precs[node.label] = 0; 7631cb0ef41Sopenharmony_ci } 7641cb0ef41Sopenharmony_ci const nodeLabelIsRootLabel = node.label == rootLabel; 7651cb0ef41Sopenharmony_ci if (nodeLabelIsRootLabel || precs[rootLabel] > 0) { 7661cb0ef41Sopenharmony_ci if (precs[rootLabel] == 0) { 7671cb0ef41Sopenharmony_ci root.selfWeight += node.selfWeight; 7681cb0ef41Sopenharmony_ci root.totalWeight += node.totalWeight; 7691cb0ef41Sopenharmony_ci } else { 7701cb0ef41Sopenharmony_ci const rec = root.findOrAddChild(node.label); 7711cb0ef41Sopenharmony_ci rec.selfWeight += node.selfWeight; 7721cb0ef41Sopenharmony_ci if (nodeLabelIsRootLabel || precs[node.label] == 0) { 7731cb0ef41Sopenharmony_ci rec.totalWeight += node.totalWeight; 7741cb0ef41Sopenharmony_ci } 7751cb0ef41Sopenharmony_ci } 7761cb0ef41Sopenharmony_ci precs[node.label]++; 7771cb0ef41Sopenharmony_ci } 7781cb0ef41Sopenharmony_ci }, 7791cb0ef41Sopenharmony_ci function onExit(node) { 7801cb0ef41Sopenharmony_ci if (node.label == rootLabel || precs[rootLabel] > 0) { 7811cb0ef41Sopenharmony_ci precs[node.label]--; 7821cb0ef41Sopenharmony_ci } 7831cb0ef41Sopenharmony_ci }, 7841cb0ef41Sopenharmony_ci null); 7851cb0ef41Sopenharmony_ci 7861cb0ef41Sopenharmony_ci if (!opt_label) { 7871cb0ef41Sopenharmony_ci // If we have created a flat profile for the whole program, we don't 7881cb0ef41Sopenharmony_ci // need an explicit root in it. Thus, replace the counters tree 7891cb0ef41Sopenharmony_ci // root with the node corresponding to the whole program. 7901cb0ef41Sopenharmony_ci counters.root_ = root; 7911cb0ef41Sopenharmony_ci } else { 7921cb0ef41Sopenharmony_ci // Propagate weights so percents can be calculated correctly. 7931cb0ef41Sopenharmony_ci counters.getRoot().selfWeight = root.selfWeight; 7941cb0ef41Sopenharmony_ci counters.getRoot().totalWeight = root.totalWeight; 7951cb0ef41Sopenharmony_ci } 7961cb0ef41Sopenharmony_ci return counters; 7971cb0ef41Sopenharmony_ci } 7981cb0ef41Sopenharmony_ci 7991cb0ef41Sopenharmony_ci getCEntryProfile() { 8001cb0ef41Sopenharmony_ci const result = [new CEntryNode("TOTAL", 0)]; 8011cb0ef41Sopenharmony_ci let total_ticks = 0; 8021cb0ef41Sopenharmony_ci for (let f in this.c_entries_) { 8031cb0ef41Sopenharmony_ci const ticks = this.c_entries_[f]; 8041cb0ef41Sopenharmony_ci total_ticks += ticks; 8051cb0ef41Sopenharmony_ci result.push(new CEntryNode(f, ticks)); 8061cb0ef41Sopenharmony_ci } 8071cb0ef41Sopenharmony_ci result[0].ticks = total_ticks; // Sorting will keep this at index 0. 8081cb0ef41Sopenharmony_ci result.sort((n1, n2) => n2.ticks - n1.ticks || (n2.name < n1.name ? -1 : 1)); 8091cb0ef41Sopenharmony_ci return result; 8101cb0ef41Sopenharmony_ci } 8111cb0ef41Sopenharmony_ci 8121cb0ef41Sopenharmony_ci 8131cb0ef41Sopenharmony_ci /** 8141cb0ef41Sopenharmony_ci * Cleans up function entries that are not referenced by code entries. 8151cb0ef41Sopenharmony_ci */ 8161cb0ef41Sopenharmony_ci cleanUpFuncEntries() { 8171cb0ef41Sopenharmony_ci const referencedFuncEntries = []; 8181cb0ef41Sopenharmony_ci const entries = this.codeMap_.getAllDynamicEntriesWithAddresses(); 8191cb0ef41Sopenharmony_ci for (let i = 0, l = entries.length; i < l; ++i) { 8201cb0ef41Sopenharmony_ci if (entries[i][1].constructor === FunctionEntry) { 8211cb0ef41Sopenharmony_ci entries[i][1].used = false; 8221cb0ef41Sopenharmony_ci } 8231cb0ef41Sopenharmony_ci } 8241cb0ef41Sopenharmony_ci for (let i = 0, l = entries.length; i < l; ++i) { 8251cb0ef41Sopenharmony_ci if ("func" in entries[i][1]) { 8261cb0ef41Sopenharmony_ci entries[i][1].func.used = true; 8271cb0ef41Sopenharmony_ci } 8281cb0ef41Sopenharmony_ci } 8291cb0ef41Sopenharmony_ci for (let i = 0, l = entries.length; i < l; ++i) { 8301cb0ef41Sopenharmony_ci if (entries[i][1].constructor === FunctionEntry && 8311cb0ef41Sopenharmony_ci !entries[i][1].used) { 8321cb0ef41Sopenharmony_ci this.codeMap_.deleteCode(entries[i][0]); 8331cb0ef41Sopenharmony_ci } 8341cb0ef41Sopenharmony_ci } 8351cb0ef41Sopenharmony_ci } 8361cb0ef41Sopenharmony_ci} 8371cb0ef41Sopenharmony_ci 8381cb0ef41Sopenharmony_ciclass CEntryNode { 8391cb0ef41Sopenharmony_ci constructor(name, ticks) { 8401cb0ef41Sopenharmony_ci this.name = name; 8411cb0ef41Sopenharmony_ci this.ticks = ticks; 8421cb0ef41Sopenharmony_ci } 8431cb0ef41Sopenharmony_ci} 8441cb0ef41Sopenharmony_ci 8451cb0ef41Sopenharmony_ci 8461cb0ef41Sopenharmony_ci/** 8471cb0ef41Sopenharmony_ci * Creates a dynamic code entry. 8481cb0ef41Sopenharmony_ci * 8491cb0ef41Sopenharmony_ci * @param {number} size Code size. 8501cb0ef41Sopenharmony_ci * @param {string} type Code type. 8511cb0ef41Sopenharmony_ci * @param {string} name Function name. 8521cb0ef41Sopenharmony_ci * @constructor 8531cb0ef41Sopenharmony_ci */ 8541cb0ef41Sopenharmony_ciclass DynamicCodeEntry extends CodeEntry { 8551cb0ef41Sopenharmony_ci constructor(size, type, name) { 8561cb0ef41Sopenharmony_ci super(size, name, type); 8571cb0ef41Sopenharmony_ci } 8581cb0ef41Sopenharmony_ci 8591cb0ef41Sopenharmony_ci getName() { 8601cb0ef41Sopenharmony_ci return this.type + ': ' + this.name; 8611cb0ef41Sopenharmony_ci } 8621cb0ef41Sopenharmony_ci 8631cb0ef41Sopenharmony_ci /** 8641cb0ef41Sopenharmony_ci * Returns raw node name (without type decoration). 8651cb0ef41Sopenharmony_ci */ 8661cb0ef41Sopenharmony_ci getRawName() { 8671cb0ef41Sopenharmony_ci return this.name; 8681cb0ef41Sopenharmony_ci } 8691cb0ef41Sopenharmony_ci 8701cb0ef41Sopenharmony_ci isJSFunction() { 8711cb0ef41Sopenharmony_ci return false; 8721cb0ef41Sopenharmony_ci } 8731cb0ef41Sopenharmony_ci 8741cb0ef41Sopenharmony_ci toString() { 8751cb0ef41Sopenharmony_ci return this.getName() + ': ' + this.size.toString(16); 8761cb0ef41Sopenharmony_ci } 8771cb0ef41Sopenharmony_ci} 8781cb0ef41Sopenharmony_ci 8791cb0ef41Sopenharmony_ci 8801cb0ef41Sopenharmony_ci/** 8811cb0ef41Sopenharmony_ci * Creates a dynamic code entry. 8821cb0ef41Sopenharmony_ci * 8831cb0ef41Sopenharmony_ci * @param {number} size Code size. 8841cb0ef41Sopenharmony_ci * @param {string} type Code type. 8851cb0ef41Sopenharmony_ci * @param {FunctionEntry} func Shared function entry. 8861cb0ef41Sopenharmony_ci * @param {Profile.CodeState} state Code optimization state. 8871cb0ef41Sopenharmony_ci * @constructor 8881cb0ef41Sopenharmony_ci */ 8891cb0ef41Sopenharmony_ciclass DynamicFuncCodeEntry extends CodeEntry { 8901cb0ef41Sopenharmony_ci constructor(size, type, func, state) { 8911cb0ef41Sopenharmony_ci super(size, '', type); 8921cb0ef41Sopenharmony_ci this.func = func; 8931cb0ef41Sopenharmony_ci func.addDynamicCode(this); 8941cb0ef41Sopenharmony_ci this.state = state; 8951cb0ef41Sopenharmony_ci } 8961cb0ef41Sopenharmony_ci 8971cb0ef41Sopenharmony_ci get functionName() { 8981cb0ef41Sopenharmony_ci return this.func.functionName; 8991cb0ef41Sopenharmony_ci } 9001cb0ef41Sopenharmony_ci 9011cb0ef41Sopenharmony_ci getSourceCode() { 9021cb0ef41Sopenharmony_ci return this.source?.getSourceCode(); 9031cb0ef41Sopenharmony_ci } 9041cb0ef41Sopenharmony_ci 9051cb0ef41Sopenharmony_ci static STATE_PREFIX = ["", "~", "^", "-", "+", "*"]; 9061cb0ef41Sopenharmony_ci getState() { 9071cb0ef41Sopenharmony_ci return DynamicFuncCodeEntry.STATE_PREFIX[this.state]; 9081cb0ef41Sopenharmony_ci } 9091cb0ef41Sopenharmony_ci 9101cb0ef41Sopenharmony_ci getName() { 9111cb0ef41Sopenharmony_ci const name = this.func.getName(); 9121cb0ef41Sopenharmony_ci return this.type + ': ' + this.getState() + name; 9131cb0ef41Sopenharmony_ci } 9141cb0ef41Sopenharmony_ci 9151cb0ef41Sopenharmony_ci /** 9161cb0ef41Sopenharmony_ci * Returns raw node name (without type decoration). 9171cb0ef41Sopenharmony_ci */ 9181cb0ef41Sopenharmony_ci getRawName() { 9191cb0ef41Sopenharmony_ci return this.func.getName(); 9201cb0ef41Sopenharmony_ci } 9211cb0ef41Sopenharmony_ci 9221cb0ef41Sopenharmony_ci isJSFunction() { 9231cb0ef41Sopenharmony_ci return true; 9241cb0ef41Sopenharmony_ci } 9251cb0ef41Sopenharmony_ci 9261cb0ef41Sopenharmony_ci toString() { 9271cb0ef41Sopenharmony_ci return this.getName() + ': ' + this.size.toString(16); 9281cb0ef41Sopenharmony_ci } 9291cb0ef41Sopenharmony_ci} 9301cb0ef41Sopenharmony_ci 9311cb0ef41Sopenharmony_ci/** 9321cb0ef41Sopenharmony_ci * Creates a shared function object entry. 9331cb0ef41Sopenharmony_ci * 9341cb0ef41Sopenharmony_ci * @param {string} name Function name. 9351cb0ef41Sopenharmony_ci * @constructor 9361cb0ef41Sopenharmony_ci */ 9371cb0ef41Sopenharmony_ciclass FunctionEntry extends CodeEntry { 9381cb0ef41Sopenharmony_ci 9391cb0ef41Sopenharmony_ci // Contains the list of generated code for this function. 9401cb0ef41Sopenharmony_ci _codeEntries = new Set(); 9411cb0ef41Sopenharmony_ci 9421cb0ef41Sopenharmony_ci constructor(name) { 9431cb0ef41Sopenharmony_ci super(0, name); 9441cb0ef41Sopenharmony_ci const index = name.lastIndexOf(' '); 9451cb0ef41Sopenharmony_ci this.functionName = 1 <= index ? name.substring(0, index) : '<anonymous>'; 9461cb0ef41Sopenharmony_ci } 9471cb0ef41Sopenharmony_ci 9481cb0ef41Sopenharmony_ci addDynamicCode(code) { 9491cb0ef41Sopenharmony_ci if (code.func != this) { 9501cb0ef41Sopenharmony_ci throw new Error("Adding dynamic code to wrong function"); 9511cb0ef41Sopenharmony_ci } 9521cb0ef41Sopenharmony_ci this._codeEntries.add(code); 9531cb0ef41Sopenharmony_ci } 9541cb0ef41Sopenharmony_ci 9551cb0ef41Sopenharmony_ci getSourceCode() { 9561cb0ef41Sopenharmony_ci // All code entries should map to the same source positions. 9571cb0ef41Sopenharmony_ci return this._codeEntries.values().next().value.getSourceCode(); 9581cb0ef41Sopenharmony_ci } 9591cb0ef41Sopenharmony_ci 9601cb0ef41Sopenharmony_ci get codeEntries() { 9611cb0ef41Sopenharmony_ci return this._codeEntries; 9621cb0ef41Sopenharmony_ci } 9631cb0ef41Sopenharmony_ci 9641cb0ef41Sopenharmony_ci /** 9651cb0ef41Sopenharmony_ci * Returns node name. 9661cb0ef41Sopenharmony_ci */ 9671cb0ef41Sopenharmony_ci getName() { 9681cb0ef41Sopenharmony_ci let name = this.name; 9691cb0ef41Sopenharmony_ci if (name.length == 0) { 9701cb0ef41Sopenharmony_ci return '<anonymous>'; 9711cb0ef41Sopenharmony_ci } else if (name.charAt(0) == ' ') { 9721cb0ef41Sopenharmony_ci // An anonymous function with location: " aaa.js:10". 9731cb0ef41Sopenharmony_ci return `<anonymous>${name}`; 9741cb0ef41Sopenharmony_ci } 9751cb0ef41Sopenharmony_ci return name; 9761cb0ef41Sopenharmony_ci } 9771cb0ef41Sopenharmony_ci} 9781cb0ef41Sopenharmony_ci 9791cb0ef41Sopenharmony_ci/** 9801cb0ef41Sopenharmony_ci * Constructs a call graph. 9811cb0ef41Sopenharmony_ci * 9821cb0ef41Sopenharmony_ci * @constructor 9831cb0ef41Sopenharmony_ci */ 9841cb0ef41Sopenharmony_ciclass CallTree { 9851cb0ef41Sopenharmony_ci root_ = new CallTreeNode(CallTree.ROOT_NODE_LABEL); 9861cb0ef41Sopenharmony_ci totalsComputed_ = false; 9871cb0ef41Sopenharmony_ci 9881cb0ef41Sopenharmony_ci /** 9891cb0ef41Sopenharmony_ci * The label of the root node. 9901cb0ef41Sopenharmony_ci */ 9911cb0ef41Sopenharmony_ci static ROOT_NODE_LABEL = ''; 9921cb0ef41Sopenharmony_ci 9931cb0ef41Sopenharmony_ci /** 9941cb0ef41Sopenharmony_ci * Returns the tree root. 9951cb0ef41Sopenharmony_ci */ 9961cb0ef41Sopenharmony_ci getRoot() { 9971cb0ef41Sopenharmony_ci return this.root_; 9981cb0ef41Sopenharmony_ci } 9991cb0ef41Sopenharmony_ci 10001cb0ef41Sopenharmony_ci /** 10011cb0ef41Sopenharmony_ci * Adds the specified call path, constructing nodes as necessary. 10021cb0ef41Sopenharmony_ci * 10031cb0ef41Sopenharmony_ci * @param {Array<string>} path Call path. 10041cb0ef41Sopenharmony_ci */ 10051cb0ef41Sopenharmony_ci addPath(path) { 10061cb0ef41Sopenharmony_ci if (path.length == 0) return; 10071cb0ef41Sopenharmony_ci let curr = this.root_; 10081cb0ef41Sopenharmony_ci for (let i = 0; i < path.length; ++i) { 10091cb0ef41Sopenharmony_ci curr = curr.findOrAddChild(path[i]); 10101cb0ef41Sopenharmony_ci } 10111cb0ef41Sopenharmony_ci curr.selfWeight++; 10121cb0ef41Sopenharmony_ci this.totalsComputed_ = false; 10131cb0ef41Sopenharmony_ci } 10141cb0ef41Sopenharmony_ci 10151cb0ef41Sopenharmony_ci /** 10161cb0ef41Sopenharmony_ci * Finds an immediate child of the specified parent with the specified 10171cb0ef41Sopenharmony_ci * label, creates a child node if necessary. If a parent node isn't 10181cb0ef41Sopenharmony_ci * specified, uses tree root. 10191cb0ef41Sopenharmony_ci * 10201cb0ef41Sopenharmony_ci * @param {string} label Child node label. 10211cb0ef41Sopenharmony_ci */ 10221cb0ef41Sopenharmony_ci findOrAddChild(label) { 10231cb0ef41Sopenharmony_ci return this.root_.findOrAddChild(label); 10241cb0ef41Sopenharmony_ci } 10251cb0ef41Sopenharmony_ci 10261cb0ef41Sopenharmony_ci /** 10271cb0ef41Sopenharmony_ci * Creates a subtree by cloning and merging all subtrees rooted at nodes 10281cb0ef41Sopenharmony_ci * with a given label. E.g. cloning the following call tree on label 'A' 10291cb0ef41Sopenharmony_ci * will give the following result: 10301cb0ef41Sopenharmony_ci * 10311cb0ef41Sopenharmony_ci * <A>--<B> <B> 10321cb0ef41Sopenharmony_ci * / / 10331cb0ef41Sopenharmony_ci * <root> == clone on 'A' ==> <root>--<A> 10341cb0ef41Sopenharmony_ci * \ \ 10351cb0ef41Sopenharmony_ci * <C>--<A>--<D> <D> 10361cb0ef41Sopenharmony_ci * 10371cb0ef41Sopenharmony_ci * And <A>'s selfWeight will be the sum of selfWeights of <A>'s from the 10381cb0ef41Sopenharmony_ci * source call tree. 10391cb0ef41Sopenharmony_ci * 10401cb0ef41Sopenharmony_ci * @param {string} label The label of the new root node. 10411cb0ef41Sopenharmony_ci */ 10421cb0ef41Sopenharmony_ci cloneSubtree(label) { 10431cb0ef41Sopenharmony_ci const subTree = new CallTree(); 10441cb0ef41Sopenharmony_ci this.traverse((node, parent) => { 10451cb0ef41Sopenharmony_ci if (!parent && node.label != label) { 10461cb0ef41Sopenharmony_ci return null; 10471cb0ef41Sopenharmony_ci } 10481cb0ef41Sopenharmony_ci const child = (parent ? parent : subTree).findOrAddChild(node.label); 10491cb0ef41Sopenharmony_ci child.selfWeight += node.selfWeight; 10501cb0ef41Sopenharmony_ci return child; 10511cb0ef41Sopenharmony_ci }); 10521cb0ef41Sopenharmony_ci return subTree; 10531cb0ef41Sopenharmony_ci } 10541cb0ef41Sopenharmony_ci 10551cb0ef41Sopenharmony_ci /** 10561cb0ef41Sopenharmony_ci * Computes total weights in the call graph. 10571cb0ef41Sopenharmony_ci */ 10581cb0ef41Sopenharmony_ci computeTotalWeights() { 10591cb0ef41Sopenharmony_ci if (this.totalsComputed_) return; 10601cb0ef41Sopenharmony_ci this.root_.computeTotalWeight(); 10611cb0ef41Sopenharmony_ci this.totalsComputed_ = true; 10621cb0ef41Sopenharmony_ci } 10631cb0ef41Sopenharmony_ci 10641cb0ef41Sopenharmony_ci /** 10651cb0ef41Sopenharmony_ci * Traverses the call graph in preorder. This function can be used for 10661cb0ef41Sopenharmony_ci * building optionally modified tree clones. This is the boilerplate code 10671cb0ef41Sopenharmony_ci * for this scenario: 10681cb0ef41Sopenharmony_ci * 10691cb0ef41Sopenharmony_ci * callTree.traverse(function(node, parentClone) { 10701cb0ef41Sopenharmony_ci * var nodeClone = cloneNode(node); 10711cb0ef41Sopenharmony_ci * if (parentClone) 10721cb0ef41Sopenharmony_ci * parentClone.addChild(nodeClone); 10731cb0ef41Sopenharmony_ci * return nodeClone; 10741cb0ef41Sopenharmony_ci * }); 10751cb0ef41Sopenharmony_ci * 10761cb0ef41Sopenharmony_ci * @param {function(CallTreeNode, *)} f Visitor function. 10771cb0ef41Sopenharmony_ci * The second parameter is the result of calling 'f' on the parent node. 10781cb0ef41Sopenharmony_ci */ 10791cb0ef41Sopenharmony_ci traverse(f) { 10801cb0ef41Sopenharmony_ci const pairsToProcess = new ConsArray(); 10811cb0ef41Sopenharmony_ci pairsToProcess.concat([{ node: this.root_, param: null }]); 10821cb0ef41Sopenharmony_ci while (!pairsToProcess.atEnd()) { 10831cb0ef41Sopenharmony_ci const pair = pairsToProcess.next(); 10841cb0ef41Sopenharmony_ci const node = pair.node; 10851cb0ef41Sopenharmony_ci const newParam = f(node, pair.param); 10861cb0ef41Sopenharmony_ci const morePairsToProcess = []; 10871cb0ef41Sopenharmony_ci node.forEachChild((child) => { 10881cb0ef41Sopenharmony_ci morePairsToProcess.push({ node: child, param: newParam }); 10891cb0ef41Sopenharmony_ci }); 10901cb0ef41Sopenharmony_ci pairsToProcess.concat(morePairsToProcess); 10911cb0ef41Sopenharmony_ci } 10921cb0ef41Sopenharmony_ci } 10931cb0ef41Sopenharmony_ci 10941cb0ef41Sopenharmony_ci /** 10951cb0ef41Sopenharmony_ci * Performs an indepth call graph traversal. 10961cb0ef41Sopenharmony_ci * 10971cb0ef41Sopenharmony_ci * @param {function(CallTreeNode)} enter A function called 10981cb0ef41Sopenharmony_ci * prior to visiting node's children. 10991cb0ef41Sopenharmony_ci * @param {function(CallTreeNode)} exit A function called 11001cb0ef41Sopenharmony_ci * after visiting node's children. 11011cb0ef41Sopenharmony_ci */ 11021cb0ef41Sopenharmony_ci traverseInDepth(enter, exit) { 11031cb0ef41Sopenharmony_ci function traverse(node) { 11041cb0ef41Sopenharmony_ci enter(node); 11051cb0ef41Sopenharmony_ci node.forEachChild(traverse); 11061cb0ef41Sopenharmony_ci exit(node); 11071cb0ef41Sopenharmony_ci } 11081cb0ef41Sopenharmony_ci traverse(this.root_); 11091cb0ef41Sopenharmony_ci } 11101cb0ef41Sopenharmony_ci} 11111cb0ef41Sopenharmony_ci 11121cb0ef41Sopenharmony_ci 11131cb0ef41Sopenharmony_ci/** 11141cb0ef41Sopenharmony_ci * Constructs a call graph node. 11151cb0ef41Sopenharmony_ci * 11161cb0ef41Sopenharmony_ci * @param {string} label Node label. 11171cb0ef41Sopenharmony_ci * @param {CallTreeNode} opt_parent Node parent. 11181cb0ef41Sopenharmony_ci */ 11191cb0ef41Sopenharmony_ciclass CallTreeNode { 11201cb0ef41Sopenharmony_ci 11211cb0ef41Sopenharmony_ci constructor(label, opt_parent) { 11221cb0ef41Sopenharmony_ci // Node self weight (how many times this node was the last node in 11231cb0ef41Sopenharmony_ci // a call path). 11241cb0ef41Sopenharmony_ci this.selfWeight = 0; 11251cb0ef41Sopenharmony_ci // Node total weight (includes weights of all children). 11261cb0ef41Sopenharmony_ci this.totalWeight = 0; 11271cb0ef41Sopenharmony_ci this. children = { __proto__:null }; 11281cb0ef41Sopenharmony_ci this.label = label; 11291cb0ef41Sopenharmony_ci this.parent = opt_parent; 11301cb0ef41Sopenharmony_ci } 11311cb0ef41Sopenharmony_ci 11321cb0ef41Sopenharmony_ci 11331cb0ef41Sopenharmony_ci /** 11341cb0ef41Sopenharmony_ci * Adds a child node. 11351cb0ef41Sopenharmony_ci * 11361cb0ef41Sopenharmony_ci * @param {string} label Child node label. 11371cb0ef41Sopenharmony_ci */ 11381cb0ef41Sopenharmony_ci addChild(label) { 11391cb0ef41Sopenharmony_ci const child = new CallTreeNode(label, this); 11401cb0ef41Sopenharmony_ci this.children[label] = child; 11411cb0ef41Sopenharmony_ci return child; 11421cb0ef41Sopenharmony_ci } 11431cb0ef41Sopenharmony_ci 11441cb0ef41Sopenharmony_ci /** 11451cb0ef41Sopenharmony_ci * Computes node's total weight. 11461cb0ef41Sopenharmony_ci */ 11471cb0ef41Sopenharmony_ci computeTotalWeight() { 11481cb0ef41Sopenharmony_ci let totalWeight = this.selfWeight; 11491cb0ef41Sopenharmony_ci this.forEachChild(function (child) { 11501cb0ef41Sopenharmony_ci totalWeight += child.computeTotalWeight(); 11511cb0ef41Sopenharmony_ci }); 11521cb0ef41Sopenharmony_ci return this.totalWeight = totalWeight; 11531cb0ef41Sopenharmony_ci } 11541cb0ef41Sopenharmony_ci 11551cb0ef41Sopenharmony_ci /** 11561cb0ef41Sopenharmony_ci * Returns all node's children as an array. 11571cb0ef41Sopenharmony_ci */ 11581cb0ef41Sopenharmony_ci exportChildren() { 11591cb0ef41Sopenharmony_ci const result = []; 11601cb0ef41Sopenharmony_ci this.forEachChild(function (node) { result.push(node); }); 11611cb0ef41Sopenharmony_ci return result; 11621cb0ef41Sopenharmony_ci } 11631cb0ef41Sopenharmony_ci 11641cb0ef41Sopenharmony_ci /** 11651cb0ef41Sopenharmony_ci * Finds an immediate child with the specified label. 11661cb0ef41Sopenharmony_ci * 11671cb0ef41Sopenharmony_ci * @param {string} label Child node label. 11681cb0ef41Sopenharmony_ci */ 11691cb0ef41Sopenharmony_ci findChild(label) { 11701cb0ef41Sopenharmony_ci const found = this.children[label]; 11711cb0ef41Sopenharmony_ci return found === undefined ? null : found; 11721cb0ef41Sopenharmony_ci } 11731cb0ef41Sopenharmony_ci 11741cb0ef41Sopenharmony_ci /** 11751cb0ef41Sopenharmony_ci * Finds an immediate child with the specified label, creates a child 11761cb0ef41Sopenharmony_ci * node if necessary. 11771cb0ef41Sopenharmony_ci * 11781cb0ef41Sopenharmony_ci * @param {string} label Child node label. 11791cb0ef41Sopenharmony_ci */ 11801cb0ef41Sopenharmony_ci findOrAddChild(label) { 11811cb0ef41Sopenharmony_ci const found = this.findChild(label) 11821cb0ef41Sopenharmony_ci if (found === null) return this.addChild(label); 11831cb0ef41Sopenharmony_ci return found; 11841cb0ef41Sopenharmony_ci } 11851cb0ef41Sopenharmony_ci 11861cb0ef41Sopenharmony_ci /** 11871cb0ef41Sopenharmony_ci * Calls the specified function for every child. 11881cb0ef41Sopenharmony_ci * 11891cb0ef41Sopenharmony_ci * @param {function(CallTreeNode)} f Visitor function. 11901cb0ef41Sopenharmony_ci */ 11911cb0ef41Sopenharmony_ci forEachChild(f) { 11921cb0ef41Sopenharmony_ci for (let c in this.children) { 11931cb0ef41Sopenharmony_ci f(this.children[c]); 11941cb0ef41Sopenharmony_ci } 11951cb0ef41Sopenharmony_ci } 11961cb0ef41Sopenharmony_ci 11971cb0ef41Sopenharmony_ci /** 11981cb0ef41Sopenharmony_ci * Walks up from the current node up to the call tree root. 11991cb0ef41Sopenharmony_ci * 12001cb0ef41Sopenharmony_ci * @param {function(CallTreeNode)} f Visitor function. 12011cb0ef41Sopenharmony_ci */ 12021cb0ef41Sopenharmony_ci walkUpToRoot(f) { 12031cb0ef41Sopenharmony_ci for (let curr = this; curr !== null; curr = curr.parent) { 12041cb0ef41Sopenharmony_ci f(curr); 12051cb0ef41Sopenharmony_ci } 12061cb0ef41Sopenharmony_ci } 12071cb0ef41Sopenharmony_ci 12081cb0ef41Sopenharmony_ci /** 12091cb0ef41Sopenharmony_ci * Tries to find a node with the specified path. 12101cb0ef41Sopenharmony_ci * 12111cb0ef41Sopenharmony_ci * @param {Array<string>} labels The path. 12121cb0ef41Sopenharmony_ci * @param {function(CallTreeNode)} opt_f Visitor function. 12131cb0ef41Sopenharmony_ci */ 12141cb0ef41Sopenharmony_ci descendToChild(labels, opt_f) { 12151cb0ef41Sopenharmony_ci let curr = this; 12161cb0ef41Sopenharmony_ci for (let pos = 0; pos < labels.length && curr != null; pos++) { 12171cb0ef41Sopenharmony_ci const child = curr.findChild(labels[pos]); 12181cb0ef41Sopenharmony_ci if (opt_f) { 12191cb0ef41Sopenharmony_ci opt_f(child, pos); 12201cb0ef41Sopenharmony_ci } 12211cb0ef41Sopenharmony_ci curr = child; 12221cb0ef41Sopenharmony_ci } 12231cb0ef41Sopenharmony_ci return curr; 12241cb0ef41Sopenharmony_ci } 12251cb0ef41Sopenharmony_ci} 12261cb0ef41Sopenharmony_ci 12271cb0ef41Sopenharmony_ciexport function JsonProfile() { 12281cb0ef41Sopenharmony_ci this.codeMap_ = new CodeMap(); 12291cb0ef41Sopenharmony_ci this.codeEntries_ = []; 12301cb0ef41Sopenharmony_ci this.functionEntries_ = []; 12311cb0ef41Sopenharmony_ci this.ticks_ = []; 12321cb0ef41Sopenharmony_ci this.scripts_ = []; 12331cb0ef41Sopenharmony_ci} 12341cb0ef41Sopenharmony_ci 12351cb0ef41Sopenharmony_ciJsonProfile.prototype.addLibrary = function ( 12361cb0ef41Sopenharmony_ci name, startAddr, endAddr) { 12371cb0ef41Sopenharmony_ci const entry = new CodeEntry( 12381cb0ef41Sopenharmony_ci endAddr - startAddr, name, 'SHARED_LIB'); 12391cb0ef41Sopenharmony_ci this.codeMap_.addLibrary(startAddr, entry); 12401cb0ef41Sopenharmony_ci 12411cb0ef41Sopenharmony_ci entry.codeId = this.codeEntries_.length; 12421cb0ef41Sopenharmony_ci this.codeEntries_.push({ name: entry.name, type: entry.type }); 12431cb0ef41Sopenharmony_ci return entry; 12441cb0ef41Sopenharmony_ci}; 12451cb0ef41Sopenharmony_ci 12461cb0ef41Sopenharmony_ciJsonProfile.prototype.addStaticCode = function ( 12471cb0ef41Sopenharmony_ci name, startAddr, endAddr) { 12481cb0ef41Sopenharmony_ci const entry = new CodeEntry( 12491cb0ef41Sopenharmony_ci endAddr - startAddr, name, 'CPP'); 12501cb0ef41Sopenharmony_ci this.codeMap_.addStaticCode(startAddr, entry); 12511cb0ef41Sopenharmony_ci 12521cb0ef41Sopenharmony_ci entry.codeId = this.codeEntries_.length; 12531cb0ef41Sopenharmony_ci this.codeEntries_.push({ name: entry.name, type: entry.type }); 12541cb0ef41Sopenharmony_ci return entry; 12551cb0ef41Sopenharmony_ci}; 12561cb0ef41Sopenharmony_ci 12571cb0ef41Sopenharmony_ciJsonProfile.prototype.addCode = function ( 12581cb0ef41Sopenharmony_ci kind, name, timestamp, start, size) { 12591cb0ef41Sopenharmony_ci let codeId = this.codeEntries_.length; 12601cb0ef41Sopenharmony_ci // Find out if we have a static code entry for the code. If yes, we will 12611cb0ef41Sopenharmony_ci // make sure it is written to the JSON file just once. 12621cb0ef41Sopenharmony_ci let staticEntry = this.codeMap_.findAddress(start); 12631cb0ef41Sopenharmony_ci if (staticEntry && staticEntry.entry.type === 'CPP') { 12641cb0ef41Sopenharmony_ci codeId = staticEntry.entry.codeId; 12651cb0ef41Sopenharmony_ci } 12661cb0ef41Sopenharmony_ci 12671cb0ef41Sopenharmony_ci const entry = new CodeEntry(size, name, 'CODE'); 12681cb0ef41Sopenharmony_ci this.codeMap_.addCode(start, entry); 12691cb0ef41Sopenharmony_ci 12701cb0ef41Sopenharmony_ci entry.codeId = codeId; 12711cb0ef41Sopenharmony_ci this.codeEntries_[codeId] = { 12721cb0ef41Sopenharmony_ci name: entry.name, 12731cb0ef41Sopenharmony_ci timestamp: timestamp, 12741cb0ef41Sopenharmony_ci type: entry.type, 12751cb0ef41Sopenharmony_ci kind: kind, 12761cb0ef41Sopenharmony_ci }; 12771cb0ef41Sopenharmony_ci 12781cb0ef41Sopenharmony_ci return entry; 12791cb0ef41Sopenharmony_ci}; 12801cb0ef41Sopenharmony_ci 12811cb0ef41Sopenharmony_ciJsonProfile.prototype.addFuncCode = function ( 12821cb0ef41Sopenharmony_ci kind, name, timestamp, start, size, funcAddr, state) { 12831cb0ef41Sopenharmony_ci // As code and functions are in the same address space, 12841cb0ef41Sopenharmony_ci // it is safe to put them in a single code map. 12851cb0ef41Sopenharmony_ci let func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr); 12861cb0ef41Sopenharmony_ci if (!func) { 12871cb0ef41Sopenharmony_ci func = new CodeEntry(0, name, 'SFI'); 12881cb0ef41Sopenharmony_ci this.codeMap_.addCode(funcAddr, func); 12891cb0ef41Sopenharmony_ci 12901cb0ef41Sopenharmony_ci func.funcId = this.functionEntries_.length; 12911cb0ef41Sopenharmony_ci this.functionEntries_.push({ name, codes: [] }); 12921cb0ef41Sopenharmony_ci } else if (func.name !== name) { 12931cb0ef41Sopenharmony_ci // Function object has been overwritten with a new one. 12941cb0ef41Sopenharmony_ci func.name = name; 12951cb0ef41Sopenharmony_ci 12961cb0ef41Sopenharmony_ci func.funcId = this.functionEntries_.length; 12971cb0ef41Sopenharmony_ci this.functionEntries_.push({ name, codes: [] }); 12981cb0ef41Sopenharmony_ci } 12991cb0ef41Sopenharmony_ci // TODO(jarin): Insert the code object into the SFI's code list. 13001cb0ef41Sopenharmony_ci let entry = this.codeMap_.findDynamicEntryByStartAddress(start); 13011cb0ef41Sopenharmony_ci if (entry) { 13021cb0ef41Sopenharmony_ci if (entry.size === size && entry.func === func) { 13031cb0ef41Sopenharmony_ci // Entry state has changed. 13041cb0ef41Sopenharmony_ci entry.state = state; 13051cb0ef41Sopenharmony_ci } else { 13061cb0ef41Sopenharmony_ci this.codeMap_.deleteCode(start); 13071cb0ef41Sopenharmony_ci entry = null; 13081cb0ef41Sopenharmony_ci } 13091cb0ef41Sopenharmony_ci } 13101cb0ef41Sopenharmony_ci if (!entry) { 13111cb0ef41Sopenharmony_ci entry = new CodeEntry(size, name, 'JS'); 13121cb0ef41Sopenharmony_ci this.codeMap_.addCode(start, entry); 13131cb0ef41Sopenharmony_ci 13141cb0ef41Sopenharmony_ci entry.codeId = this.codeEntries_.length; 13151cb0ef41Sopenharmony_ci 13161cb0ef41Sopenharmony_ci this.functionEntries_[func.funcId].codes.push(entry.codeId); 13171cb0ef41Sopenharmony_ci 13181cb0ef41Sopenharmony_ci kind = Profile.getKindFromState(state); 13191cb0ef41Sopenharmony_ci 13201cb0ef41Sopenharmony_ci this.codeEntries_.push({ 13211cb0ef41Sopenharmony_ci name: entry.name, 13221cb0ef41Sopenharmony_ci type: entry.type, 13231cb0ef41Sopenharmony_ci kind: kind, 13241cb0ef41Sopenharmony_ci func: func.funcId, 13251cb0ef41Sopenharmony_ci tm: timestamp, 13261cb0ef41Sopenharmony_ci }); 13271cb0ef41Sopenharmony_ci } 13281cb0ef41Sopenharmony_ci return entry; 13291cb0ef41Sopenharmony_ci}; 13301cb0ef41Sopenharmony_ci 13311cb0ef41Sopenharmony_ciJsonProfile.prototype.moveCode = function (from, to) { 13321cb0ef41Sopenharmony_ci try { 13331cb0ef41Sopenharmony_ci this.codeMap_.moveCode(from, to); 13341cb0ef41Sopenharmony_ci } catch (e) { 13351cb0ef41Sopenharmony_ci printErr(`Move: unknown source ${from}`); 13361cb0ef41Sopenharmony_ci } 13371cb0ef41Sopenharmony_ci}; 13381cb0ef41Sopenharmony_ci 13391cb0ef41Sopenharmony_ciJsonProfile.prototype.addSourcePositions = function ( 13401cb0ef41Sopenharmony_ci start, script, startPos, endPos, sourcePositions, inliningPositions, 13411cb0ef41Sopenharmony_ci inlinedFunctions) { 13421cb0ef41Sopenharmony_ci const entry = this.codeMap_.findDynamicEntryByStartAddress(start); 13431cb0ef41Sopenharmony_ci if (!entry) return; 13441cb0ef41Sopenharmony_ci const codeId = entry.codeId; 13451cb0ef41Sopenharmony_ci 13461cb0ef41Sopenharmony_ci // Resolve the inlined functions list. 13471cb0ef41Sopenharmony_ci if (inlinedFunctions.length > 0) { 13481cb0ef41Sopenharmony_ci inlinedFunctions = inlinedFunctions.substring(1).split("S"); 13491cb0ef41Sopenharmony_ci for (let i = 0; i < inlinedFunctions.length; i++) { 13501cb0ef41Sopenharmony_ci const funcAddr = parseInt(inlinedFunctions[i]); 13511cb0ef41Sopenharmony_ci const func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr); 13521cb0ef41Sopenharmony_ci if (!func || func.funcId === undefined) { 13531cb0ef41Sopenharmony_ci printErr(`Could not find function ${inlinedFunctions[i]}`); 13541cb0ef41Sopenharmony_ci inlinedFunctions[i] = null; 13551cb0ef41Sopenharmony_ci } else { 13561cb0ef41Sopenharmony_ci inlinedFunctions[i] = func.funcId; 13571cb0ef41Sopenharmony_ci } 13581cb0ef41Sopenharmony_ci } 13591cb0ef41Sopenharmony_ci } else { 13601cb0ef41Sopenharmony_ci inlinedFunctions = []; 13611cb0ef41Sopenharmony_ci } 13621cb0ef41Sopenharmony_ci 13631cb0ef41Sopenharmony_ci this.codeEntries_[entry.codeId].source = { 13641cb0ef41Sopenharmony_ci script: script, 13651cb0ef41Sopenharmony_ci start: startPos, 13661cb0ef41Sopenharmony_ci end: endPos, 13671cb0ef41Sopenharmony_ci positions: sourcePositions, 13681cb0ef41Sopenharmony_ci inlined: inliningPositions, 13691cb0ef41Sopenharmony_ci fns: inlinedFunctions 13701cb0ef41Sopenharmony_ci }; 13711cb0ef41Sopenharmony_ci}; 13721cb0ef41Sopenharmony_ci 13731cb0ef41Sopenharmony_ciJsonProfile.prototype.addScriptSource = function (id, url, source) { 13741cb0ef41Sopenharmony_ci const script = new Script(id); 13751cb0ef41Sopenharmony_ci script.update(url, source); 13761cb0ef41Sopenharmony_ci this.scripts_[id] = script; 13771cb0ef41Sopenharmony_ci}; 13781cb0ef41Sopenharmony_ci 13791cb0ef41Sopenharmony_ciJsonProfile.prototype.deoptCode = function ( 13801cb0ef41Sopenharmony_ci timestamp, code, inliningId, scriptOffset, bailoutType, 13811cb0ef41Sopenharmony_ci sourcePositionText, deoptReasonText) { 13821cb0ef41Sopenharmony_ci let entry = this.codeMap_.findDynamicEntryByStartAddress(code); 13831cb0ef41Sopenharmony_ci if (entry) { 13841cb0ef41Sopenharmony_ci let codeId = entry.codeId; 13851cb0ef41Sopenharmony_ci if (!this.codeEntries_[codeId].deopt) { 13861cb0ef41Sopenharmony_ci // Only add the deopt if there was no deopt before. 13871cb0ef41Sopenharmony_ci // The subsequent deoptimizations should be lazy deopts for 13881cb0ef41Sopenharmony_ci // other on-stack activations. 13891cb0ef41Sopenharmony_ci this.codeEntries_[codeId].deopt = { 13901cb0ef41Sopenharmony_ci tm: timestamp, 13911cb0ef41Sopenharmony_ci inliningId: inliningId, 13921cb0ef41Sopenharmony_ci scriptOffset: scriptOffset, 13931cb0ef41Sopenharmony_ci posText: sourcePositionText, 13941cb0ef41Sopenharmony_ci reason: deoptReasonText, 13951cb0ef41Sopenharmony_ci bailoutType: bailoutType, 13961cb0ef41Sopenharmony_ci }; 13971cb0ef41Sopenharmony_ci } 13981cb0ef41Sopenharmony_ci } 13991cb0ef41Sopenharmony_ci}; 14001cb0ef41Sopenharmony_ci 14011cb0ef41Sopenharmony_ciJsonProfile.prototype.deleteCode = function (start) { 14021cb0ef41Sopenharmony_ci try { 14031cb0ef41Sopenharmony_ci this.codeMap_.deleteCode(start); 14041cb0ef41Sopenharmony_ci } catch (e) { 14051cb0ef41Sopenharmony_ci printErr(`Delete: unknown address ${start}`); 14061cb0ef41Sopenharmony_ci } 14071cb0ef41Sopenharmony_ci}; 14081cb0ef41Sopenharmony_ci 14091cb0ef41Sopenharmony_ciJsonProfile.prototype.moveFunc = function (from, to) { 14101cb0ef41Sopenharmony_ci if (this.codeMap_.findDynamicEntryByStartAddress(from)) { 14111cb0ef41Sopenharmony_ci this.codeMap_.moveCode(from, to); 14121cb0ef41Sopenharmony_ci } 14131cb0ef41Sopenharmony_ci}; 14141cb0ef41Sopenharmony_ci 14151cb0ef41Sopenharmony_ciJsonProfile.prototype.findEntry = function (addr) { 14161cb0ef41Sopenharmony_ci return this.codeMap_.findEntry(addr); 14171cb0ef41Sopenharmony_ci}; 14181cb0ef41Sopenharmony_ci 14191cb0ef41Sopenharmony_ciJsonProfile.prototype.recordTick = function (time_ns, vmState, stack) { 14201cb0ef41Sopenharmony_ci // TODO(jarin) Resolve the frame-less case (when top of stack is 14211cb0ef41Sopenharmony_ci // known code). 14221cb0ef41Sopenharmony_ci const processedStack = []; 14231cb0ef41Sopenharmony_ci for (let i = 0; i < stack.length; i++) { 14241cb0ef41Sopenharmony_ci const resolved = this.codeMap_.findAddress(stack[i]); 14251cb0ef41Sopenharmony_ci if (resolved) { 14261cb0ef41Sopenharmony_ci processedStack.push(resolved.entry.codeId, resolved.offset); 14271cb0ef41Sopenharmony_ci } else { 14281cb0ef41Sopenharmony_ci processedStack.push(-1, stack[i]); 14291cb0ef41Sopenharmony_ci } 14301cb0ef41Sopenharmony_ci } 14311cb0ef41Sopenharmony_ci this.ticks_.push({ tm: time_ns, vm: vmState, s: processedStack }); 14321cb0ef41Sopenharmony_ci}; 14331cb0ef41Sopenharmony_ci 14341cb0ef41Sopenharmony_cifunction writeJson(s) { 14351cb0ef41Sopenharmony_ci write(JSON.stringify(s, null, 2)); 14361cb0ef41Sopenharmony_ci} 14371cb0ef41Sopenharmony_ci 14381cb0ef41Sopenharmony_ciJsonProfile.prototype.writeJson = function () { 14391cb0ef41Sopenharmony_ci // Write out the JSON in a partially manual way to avoid creating too-large 14401cb0ef41Sopenharmony_ci // strings in one JSON.stringify call when there are a lot of ticks. 14411cb0ef41Sopenharmony_ci write('{\n') 14421cb0ef41Sopenharmony_ci 14431cb0ef41Sopenharmony_ci write(' "code": '); 14441cb0ef41Sopenharmony_ci writeJson(this.codeEntries_); 14451cb0ef41Sopenharmony_ci write(',\n'); 14461cb0ef41Sopenharmony_ci 14471cb0ef41Sopenharmony_ci write(' "functions": '); 14481cb0ef41Sopenharmony_ci writeJson(this.functionEntries_); 14491cb0ef41Sopenharmony_ci write(',\n'); 14501cb0ef41Sopenharmony_ci 14511cb0ef41Sopenharmony_ci write(' "ticks": [\n'); 14521cb0ef41Sopenharmony_ci for (let i = 0; i < this.ticks_.length; i++) { 14531cb0ef41Sopenharmony_ci write(' '); 14541cb0ef41Sopenharmony_ci writeJson(this.ticks_[i]); 14551cb0ef41Sopenharmony_ci if (i < this.ticks_.length - 1) { 14561cb0ef41Sopenharmony_ci write(',\n'); 14571cb0ef41Sopenharmony_ci } else { 14581cb0ef41Sopenharmony_ci write('\n'); 14591cb0ef41Sopenharmony_ci } 14601cb0ef41Sopenharmony_ci } 14611cb0ef41Sopenharmony_ci write(' ],\n'); 14621cb0ef41Sopenharmony_ci 14631cb0ef41Sopenharmony_ci write(' "scripts": '); 14641cb0ef41Sopenharmony_ci writeJson(this.scripts_); 14651cb0ef41Sopenharmony_ci 14661cb0ef41Sopenharmony_ci write('}\n'); 14671cb0ef41Sopenharmony_ci}; 1468