xref: /third_party/node/deps/v8/tools/profile.mjs (revision 1cb0ef41)
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