11cb0ef41Sopenharmony_ci// Copyright 2017 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci"use strict";
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_cilet codeKinds = [
81cb0ef41Sopenharmony_ci    "UNKNOWN",
91cb0ef41Sopenharmony_ci    "CPP_PARSE",
101cb0ef41Sopenharmony_ci    "CPP_COMP_BC",
111cb0ef41Sopenharmony_ci    "CPP_COMP_BASELINE",
121cb0ef41Sopenharmony_ci    "CPP_COMP",
131cb0ef41Sopenharmony_ci    "CPP_GC",
141cb0ef41Sopenharmony_ci    "CPP_EXT",
151cb0ef41Sopenharmony_ci    "CPP",
161cb0ef41Sopenharmony_ci    "LIB",
171cb0ef41Sopenharmony_ci    "IC",
181cb0ef41Sopenharmony_ci    "BC",
191cb0ef41Sopenharmony_ci    "STUB",
201cb0ef41Sopenharmony_ci    "BUILTIN",
211cb0ef41Sopenharmony_ci    "REGEXP",
221cb0ef41Sopenharmony_ci    "JS_OPT",
231cb0ef41Sopenharmony_ci    "JS_UNOPT",
241cb0ef41Sopenharmony_ci    "JS_TURBOPROP",
251cb0ef41Sopenharmony_ci    "JS_BASELINE",
261cb0ef41Sopenharmony_ci];
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_cifunction resolveCodeKind(code) {
291cb0ef41Sopenharmony_ci  if (!code || !code.type) {
301cb0ef41Sopenharmony_ci    return "UNKNOWN";
311cb0ef41Sopenharmony_ci  } else if (code.type === "CPP") {
321cb0ef41Sopenharmony_ci    return "CPP";
331cb0ef41Sopenharmony_ci  } else if (code.type === "SHARED_LIB") {
341cb0ef41Sopenharmony_ci    return "LIB";
351cb0ef41Sopenharmony_ci  } else if (code.type === "CODE") {
361cb0ef41Sopenharmony_ci    if (code.kind === "LoadIC" ||
371cb0ef41Sopenharmony_ci        code.kind === "StoreIC" ||
381cb0ef41Sopenharmony_ci        code.kind === "KeyedStoreIC" ||
391cb0ef41Sopenharmony_ci        code.kind === "KeyedLoadIC" ||
401cb0ef41Sopenharmony_ci        code.kind === "LoadGlobalIC" ||
411cb0ef41Sopenharmony_ci        code.kind === "Handler") {
421cb0ef41Sopenharmony_ci      return "IC";
431cb0ef41Sopenharmony_ci    } else if (code.kind === "BytecodeHandler") {
441cb0ef41Sopenharmony_ci      return "BC";
451cb0ef41Sopenharmony_ci    } else if (code.kind === "Stub") {
461cb0ef41Sopenharmony_ci      return "STUB";
471cb0ef41Sopenharmony_ci    } else if (code.kind === "Builtin") {
481cb0ef41Sopenharmony_ci      return "BUILTIN";
491cb0ef41Sopenharmony_ci    } else if (code.kind === "RegExp") {
501cb0ef41Sopenharmony_ci      return "REGEXP";
511cb0ef41Sopenharmony_ci    }
521cb0ef41Sopenharmony_ci    console.log("Unknown CODE: '" + code.kind + "'.");
531cb0ef41Sopenharmony_ci    return "CODE";
541cb0ef41Sopenharmony_ci  } else if (code.type === "JS") {
551cb0ef41Sopenharmony_ci    if (code.kind === "Builtin") {
561cb0ef41Sopenharmony_ci      return "JS_UNOPT";
571cb0ef41Sopenharmony_ci    } else if (code.kind === "Opt") {
581cb0ef41Sopenharmony_ci      return "JS_OPT";
591cb0ef41Sopenharmony_ci    } else if (code.kind === "Unopt") {
601cb0ef41Sopenharmony_ci      return "JS_UNOPT";
611cb0ef41Sopenharmony_ci    } else if (code.kind === "Baseline") {
621cb0ef41Sopenharmony_ci      return "JS_BASELINE";
631cb0ef41Sopenharmony_ci    } else if (code.kind === "Turboprop") {
641cb0ef41Sopenharmony_ci      return "JS_TURBOPROP";
651cb0ef41Sopenharmony_ci    }
661cb0ef41Sopenharmony_ci  }
671cb0ef41Sopenharmony_ci  console.log("Unknown code type '" + type + "'.");
681cb0ef41Sopenharmony_ci}
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_cifunction resolveCodeKindAndVmState(code, vmState) {
711cb0ef41Sopenharmony_ci  let kind = resolveCodeKind(code);
721cb0ef41Sopenharmony_ci  if (kind === "CPP") {
731cb0ef41Sopenharmony_ci    if (vmState === 1) {
741cb0ef41Sopenharmony_ci      kind = "CPP_GC";
751cb0ef41Sopenharmony_ci    } else if (vmState === 2) {
761cb0ef41Sopenharmony_ci      kind = "CPP_PARSE";
771cb0ef41Sopenharmony_ci    } else if (vmState === 3) {
781cb0ef41Sopenharmony_ci      kind = "CPP_COMP_BC";
791cb0ef41Sopenharmony_ci    } else if (vmState === 4) {
801cb0ef41Sopenharmony_ci      kind = "CPP_COMP";
811cb0ef41Sopenharmony_ci    } else if (vmState === 6) {
821cb0ef41Sopenharmony_ci      kind = "CPP_EXT";
831cb0ef41Sopenharmony_ci    }
841cb0ef41Sopenharmony_ci    // TODO(cbruni): add CPP_COMP_BASELINE
851cb0ef41Sopenharmony_ci  }
861cb0ef41Sopenharmony_ci  return kind;
871cb0ef41Sopenharmony_ci}
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_cifunction codeEquals(code1, code2, allowDifferentKinds = false) {
901cb0ef41Sopenharmony_ci  if (!code1 || !code2) return false;
911cb0ef41Sopenharmony_ci  if (code1.name !== code2.name || code1.type !== code2.type) return false;
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_ci  if (code1.type === 'CODE') {
941cb0ef41Sopenharmony_ci    if (!allowDifferentKinds && code1.kind !== code2.kind) return false;
951cb0ef41Sopenharmony_ci  } else if (code1.type === 'JS') {
961cb0ef41Sopenharmony_ci    if (!allowDifferentKinds && code1.kind !== code2.kind) return false;
971cb0ef41Sopenharmony_ci    if (code1.func !== code2.func) return false;
981cb0ef41Sopenharmony_ci  }
991cb0ef41Sopenharmony_ci  return true;
1001cb0ef41Sopenharmony_ci}
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_cifunction createNodeFromStackEntry(code, codeId, vmState) {
1031cb0ef41Sopenharmony_ci  let name = code ? code.name : "UNKNOWN";
1041cb0ef41Sopenharmony_ci  let node = createEmptyNode(name);
1051cb0ef41Sopenharmony_ci  node.codeId = codeId;
1061cb0ef41Sopenharmony_ci  node.type = resolveCodeKindAndVmState(code, vmState);
1071cb0ef41Sopenharmony_ci  return node;
1081cb0ef41Sopenharmony_ci}
1091cb0ef41Sopenharmony_ci
1101cb0ef41Sopenharmony_cifunction childIdFromCode(codeId, code) {
1111cb0ef41Sopenharmony_ci  // For JavaScript function, pretend there is one instance of optimized
1121cb0ef41Sopenharmony_ci  // function and one instance of unoptimized function per SFI.
1131cb0ef41Sopenharmony_ci  // Otherwise, just compute the id from code id.
1141cb0ef41Sopenharmony_ci  let type = resolveCodeKind(code);
1151cb0ef41Sopenharmony_ci  if (type === "JSOPT") {
1161cb0ef41Sopenharmony_ci    return code.func * 4 + 1;
1171cb0ef41Sopenharmony_ci  } else if (type === "JSUNOPT") {
1181cb0ef41Sopenharmony_ci    return code.func * 4 + 2;
1191cb0ef41Sopenharmony_ci  } else {
1201cb0ef41Sopenharmony_ci    return codeId * 4;
1211cb0ef41Sopenharmony_ci  }
1221cb0ef41Sopenharmony_ci}
1231cb0ef41Sopenharmony_ci
1241cb0ef41Sopenharmony_ci// We store list of ticks and positions within the ticks stack by
1251cb0ef41Sopenharmony_ci// storing flattened triplets of { tickIndex, depth, count }.
1261cb0ef41Sopenharmony_ci// Triplet { 123, 2, 3 } encodes positions in ticks 123, 124, 125,
1271cb0ef41Sopenharmony_ci// all of them at depth 2. The flattened array is used to encode
1281cb0ef41Sopenharmony_ci// position within the call-tree.
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_ci// The following function helps to encode such triplets.
1311cb0ef41Sopenharmony_cifunction addFrameToFrameList(paths, pathIndex, depth) {
1321cb0ef41Sopenharmony_ci  // Try to combine with the previous code run.
1331cb0ef41Sopenharmony_ci  if (paths.length > 0 &&
1341cb0ef41Sopenharmony_ci      paths[paths.length - 3] + 1 === pathIndex &&
1351cb0ef41Sopenharmony_ci      paths[paths.length - 2] === depth) {
1361cb0ef41Sopenharmony_ci    paths[paths.length - 1]++;
1371cb0ef41Sopenharmony_ci  } else {
1381cb0ef41Sopenharmony_ci    paths.push(pathIndex, depth, 1);
1391cb0ef41Sopenharmony_ci  }
1401cb0ef41Sopenharmony_ci}
1411cb0ef41Sopenharmony_ci
1421cb0ef41Sopenharmony_cifunction findNextFrame(file, stack, stackPos, step, filter) {
1431cb0ef41Sopenharmony_ci  let codeId = -1;
1441cb0ef41Sopenharmony_ci  let code = null;
1451cb0ef41Sopenharmony_ci  while (stackPos >= 0 && stackPos < stack.length) {
1461cb0ef41Sopenharmony_ci    codeId = stack[stackPos];
1471cb0ef41Sopenharmony_ci    code = codeId >= 0 ? file.code[codeId] : undefined;
1481cb0ef41Sopenharmony_ci
1491cb0ef41Sopenharmony_ci    if (filter) {
1501cb0ef41Sopenharmony_ci      let type = code ? code.type : undefined;
1511cb0ef41Sopenharmony_ci      let kind = code ? code.kind : undefined;
1521cb0ef41Sopenharmony_ci      if (filter(type, kind)) return stackPos;
1531cb0ef41Sopenharmony_ci    }
1541cb0ef41Sopenharmony_ci    stackPos += step;
1551cb0ef41Sopenharmony_ci  }
1561cb0ef41Sopenharmony_ci  return -1;
1571cb0ef41Sopenharmony_ci}
1581cb0ef41Sopenharmony_ci
1591cb0ef41Sopenharmony_cifunction addOrUpdateChildNode(parent, file, stackIndex, stackPos, ascending) {
1601cb0ef41Sopenharmony_ci  if (stackPos === -1) {
1611cb0ef41Sopenharmony_ci    // We reached the end without finding the next step.
1621cb0ef41Sopenharmony_ci    // If we are doing top-down call tree, update own ticks.
1631cb0ef41Sopenharmony_ci    if (!ascending) {
1641cb0ef41Sopenharmony_ci      parent.ownTicks++;
1651cb0ef41Sopenharmony_ci    }
1661cb0ef41Sopenharmony_ci    return;
1671cb0ef41Sopenharmony_ci  }
1681cb0ef41Sopenharmony_ci
1691cb0ef41Sopenharmony_ci  let stack = file.ticks[stackIndex].s;
1701cb0ef41Sopenharmony_ci  console.assert(stackPos >= 0 && stackPos < stack.length);
1711cb0ef41Sopenharmony_ci  let codeId = stack[stackPos];
1721cb0ef41Sopenharmony_ci  let code = codeId >= 0 ? file.code[codeId] : undefined;
1731cb0ef41Sopenharmony_ci  // We found a child node.
1741cb0ef41Sopenharmony_ci  let childId = childIdFromCode(codeId, code);
1751cb0ef41Sopenharmony_ci  let child = parent.children[childId];
1761cb0ef41Sopenharmony_ci  if (!child) {
1771cb0ef41Sopenharmony_ci    let vmState = file.ticks[stackIndex].vm;
1781cb0ef41Sopenharmony_ci    child = createNodeFromStackEntry(code, codeId, vmState);
1791cb0ef41Sopenharmony_ci    child.delayedExpansion = { frameList : [], ascending };
1801cb0ef41Sopenharmony_ci    parent.children[childId] = child;
1811cb0ef41Sopenharmony_ci  }
1821cb0ef41Sopenharmony_ci  child.ticks++;
1831cb0ef41Sopenharmony_ci  addFrameToFrameList(child.delayedExpansion.frameList, stackIndex, stackPos);
1841cb0ef41Sopenharmony_ci}
1851cb0ef41Sopenharmony_ci
1861cb0ef41Sopenharmony_ci// This expands a tree node (direct children only).
1871cb0ef41Sopenharmony_cifunction expandTreeNode(file, node, filter) {
1881cb0ef41Sopenharmony_ci  let { frameList, ascending } = node.delayedExpansion;
1891cb0ef41Sopenharmony_ci
1901cb0ef41Sopenharmony_ci  let step = ascending ? 2 : -2;
1911cb0ef41Sopenharmony_ci
1921cb0ef41Sopenharmony_ci  for (let i = 0; i < frameList.length; i+= 3) {
1931cb0ef41Sopenharmony_ci    let firstStackIndex = frameList[i];
1941cb0ef41Sopenharmony_ci    let depth = frameList[i + 1];
1951cb0ef41Sopenharmony_ci    let count = frameList[i + 2];
1961cb0ef41Sopenharmony_ci    for (let j = 0; j < count; j++) {
1971cb0ef41Sopenharmony_ci      let stackIndex = firstStackIndex + j;
1981cb0ef41Sopenharmony_ci      let stack = file.ticks[stackIndex].s;
1991cb0ef41Sopenharmony_ci
2001cb0ef41Sopenharmony_ci      // Get to the next frame that has not been filtered out.
2011cb0ef41Sopenharmony_ci      let stackPos = findNextFrame(file, stack, depth + step, step, filter);
2021cb0ef41Sopenharmony_ci      addOrUpdateChildNode(node, file, stackIndex, stackPos, ascending);
2031cb0ef41Sopenharmony_ci    }
2041cb0ef41Sopenharmony_ci  }
2051cb0ef41Sopenharmony_ci  node.delayedExpansion = null;
2061cb0ef41Sopenharmony_ci}
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_cifunction createEmptyNode(name) {
2091cb0ef41Sopenharmony_ci  return {
2101cb0ef41Sopenharmony_ci      name : name,
2111cb0ef41Sopenharmony_ci      codeId: -1,
2121cb0ef41Sopenharmony_ci      type : "CAT",
2131cb0ef41Sopenharmony_ci      children : [],
2141cb0ef41Sopenharmony_ci      ownTicks : 0,
2151cb0ef41Sopenharmony_ci      ticks : 0
2161cb0ef41Sopenharmony_ci  };
2171cb0ef41Sopenharmony_ci}
2181cb0ef41Sopenharmony_ci
2191cb0ef41Sopenharmony_ciclass RuntimeCallTreeProcessor {
2201cb0ef41Sopenharmony_ci  constructor() {
2211cb0ef41Sopenharmony_ci    this.tree = createEmptyNode("root");
2221cb0ef41Sopenharmony_ci    this.tree.delayedExpansion = { frameList : [], ascending : false };
2231cb0ef41Sopenharmony_ci  }
2241cb0ef41Sopenharmony_ci
2251cb0ef41Sopenharmony_ci  addStack(file, tickIndex) {
2261cb0ef41Sopenharmony_ci    this.tree.ticks++;
2271cb0ef41Sopenharmony_ci
2281cb0ef41Sopenharmony_ci    let stack = file.ticks[tickIndex].s;
2291cb0ef41Sopenharmony_ci    let i;
2301cb0ef41Sopenharmony_ci    for (i = 0; i < stack.length; i += 2) {
2311cb0ef41Sopenharmony_ci      let codeId = stack[i];
2321cb0ef41Sopenharmony_ci      if (codeId < 0) return;
2331cb0ef41Sopenharmony_ci      let code = file.code[codeId];
2341cb0ef41Sopenharmony_ci      if (code.type !== "CPP" && code.type !== "SHARED_LIB") {
2351cb0ef41Sopenharmony_ci        i -= 2;
2361cb0ef41Sopenharmony_ci        break;
2371cb0ef41Sopenharmony_ci      }
2381cb0ef41Sopenharmony_ci    }
2391cb0ef41Sopenharmony_ci    if (i < 0 || i >= stack.length) return;
2401cb0ef41Sopenharmony_ci    addOrUpdateChildNode(this.tree, file, tickIndex, i, false);
2411cb0ef41Sopenharmony_ci  }
2421cb0ef41Sopenharmony_ci}
2431cb0ef41Sopenharmony_ci
2441cb0ef41Sopenharmony_ciclass PlainCallTreeProcessor {
2451cb0ef41Sopenharmony_ci  constructor(filter, isBottomUp) {
2461cb0ef41Sopenharmony_ci    this.filter = filter;
2471cb0ef41Sopenharmony_ci    this.tree = createEmptyNode("root");
2481cb0ef41Sopenharmony_ci    this.tree.delayedExpansion = { frameList : [], ascending : isBottomUp };
2491cb0ef41Sopenharmony_ci    this.isBottomUp = isBottomUp;
2501cb0ef41Sopenharmony_ci  }
2511cb0ef41Sopenharmony_ci
2521cb0ef41Sopenharmony_ci  addStack(file, tickIndex) {
2531cb0ef41Sopenharmony_ci    let stack = file.ticks[tickIndex].s;
2541cb0ef41Sopenharmony_ci    let step = this.isBottomUp ? 2 : -2;
2551cb0ef41Sopenharmony_ci    let start = this.isBottomUp ? 0 : stack.length - 2;
2561cb0ef41Sopenharmony_ci
2571cb0ef41Sopenharmony_ci    let stackPos = findNextFrame(file, stack, start, step, this.filter);
2581cb0ef41Sopenharmony_ci    addOrUpdateChildNode(this.tree, file, tickIndex, stackPos, this.isBottomUp);
2591cb0ef41Sopenharmony_ci
2601cb0ef41Sopenharmony_ci    this.tree.ticks++;
2611cb0ef41Sopenharmony_ci  }
2621cb0ef41Sopenharmony_ci}
2631cb0ef41Sopenharmony_ci
2641cb0ef41Sopenharmony_cifunction buildCategoryTreeAndLookup() {
2651cb0ef41Sopenharmony_ci  let root = createEmptyNode("root");
2661cb0ef41Sopenharmony_ci  let categories = {};
2671cb0ef41Sopenharmony_ci  function addCategory(name, types) {
2681cb0ef41Sopenharmony_ci    let n = createEmptyNode(name);
2691cb0ef41Sopenharmony_ci    for (let i = 0; i < types.length; i++) {
2701cb0ef41Sopenharmony_ci      categories[types[i]] = n;
2711cb0ef41Sopenharmony_ci    }
2721cb0ef41Sopenharmony_ci    root.children.push(n);
2731cb0ef41Sopenharmony_ci  }
2741cb0ef41Sopenharmony_ci  addCategory("JS Optimized", [ "JS_OPT" ]);
2751cb0ef41Sopenharmony_ci  addCategory("JS Turboprop", [ "JS_TURBOPROP" ]);
2761cb0ef41Sopenharmony_ci  addCategory("JS Baseline", [ "JS_BASELINE" ]);
2771cb0ef41Sopenharmony_ci  addCategory("JS Unoptimized", [ "JS_UNOPT", "BC" ]);
2781cb0ef41Sopenharmony_ci  addCategory("IC", [ "IC" ]);
2791cb0ef41Sopenharmony_ci  addCategory("RegExp", [ "REGEXP" ]);
2801cb0ef41Sopenharmony_ci  addCategory("Other generated", [ "STUB", "BUILTIN" ]);
2811cb0ef41Sopenharmony_ci  addCategory("C++", [ "CPP", "LIB" ]);
2821cb0ef41Sopenharmony_ci  addCategory("C++/GC", [ "CPP_GC" ]);
2831cb0ef41Sopenharmony_ci  addCategory("C++/Parser", [ "CPP_PARSE" ]);
2841cb0ef41Sopenharmony_ci  addCategory("C++/Bytecode Compiler", [ "CPP_COMP_BC" ]);
2851cb0ef41Sopenharmony_ci  addCategory("C++/Baseline Compiler", [ "CPP_COMP_BASELINE" ]);
2861cb0ef41Sopenharmony_ci  addCategory("C++/Compiler", [ "CPP_COMP" ]);
2871cb0ef41Sopenharmony_ci  addCategory("C++/External", [ "CPP_EXT" ]);
2881cb0ef41Sopenharmony_ci  addCategory("Unknown", [ "UNKNOWN" ]);
2891cb0ef41Sopenharmony_ci
2901cb0ef41Sopenharmony_ci  return { categories, root };
2911cb0ef41Sopenharmony_ci}
2921cb0ef41Sopenharmony_ci
2931cb0ef41Sopenharmony_ciclass CategorizedCallTreeProcessor {
2941cb0ef41Sopenharmony_ci  constructor(filter, isBottomUp) {
2951cb0ef41Sopenharmony_ci    this.filter = filter;
2961cb0ef41Sopenharmony_ci    let { categories, root } = buildCategoryTreeAndLookup();
2971cb0ef41Sopenharmony_ci
2981cb0ef41Sopenharmony_ci    this.tree = root;
2991cb0ef41Sopenharmony_ci    this.categories = categories;
3001cb0ef41Sopenharmony_ci    this.isBottomUp = isBottomUp;
3011cb0ef41Sopenharmony_ci  }
3021cb0ef41Sopenharmony_ci
3031cb0ef41Sopenharmony_ci  addStack(file, tickIndex) {
3041cb0ef41Sopenharmony_ci    let stack = file.ticks[tickIndex].s;
3051cb0ef41Sopenharmony_ci    let vmState = file.ticks[tickIndex].vm;
3061cb0ef41Sopenharmony_ci    if (stack.length === 0) return;
3071cb0ef41Sopenharmony_ci    let codeId = stack[0];
3081cb0ef41Sopenharmony_ci    let code = codeId >= 0 ? file.code[codeId] : undefined;
3091cb0ef41Sopenharmony_ci    let kind = resolveCodeKindAndVmState(code, vmState);
3101cb0ef41Sopenharmony_ci    let node = this.categories[kind];
3111cb0ef41Sopenharmony_ci
3121cb0ef41Sopenharmony_ci    this.tree.ticks++;
3131cb0ef41Sopenharmony_ci    node.ticks++;
3141cb0ef41Sopenharmony_ci
3151cb0ef41Sopenharmony_ci    let step = this.isBottomUp ? 2 : -2;
3161cb0ef41Sopenharmony_ci    let start = this.isBottomUp ? 0 : stack.length - 2;
3171cb0ef41Sopenharmony_ci
3181cb0ef41Sopenharmony_ci    let stackPos = findNextFrame(file, stack, start, step, this.filter);
3191cb0ef41Sopenharmony_ci    addOrUpdateChildNode(node, file, tickIndex, stackPos, this.isBottomUp);
3201cb0ef41Sopenharmony_ci  }
3211cb0ef41Sopenharmony_ci}
3221cb0ef41Sopenharmony_ci
3231cb0ef41Sopenharmony_ciclass FunctionListTree {
3241cb0ef41Sopenharmony_ci  constructor(filter, withCategories) {
3251cb0ef41Sopenharmony_ci    if (withCategories) {
3261cb0ef41Sopenharmony_ci      let { categories, root } = buildCategoryTreeAndLookup();
3271cb0ef41Sopenharmony_ci      this.tree = root;
3281cb0ef41Sopenharmony_ci      this.categories = categories;
3291cb0ef41Sopenharmony_ci    } else {
3301cb0ef41Sopenharmony_ci      this.tree = createEmptyNode("root");
3311cb0ef41Sopenharmony_ci      this.categories = null;
3321cb0ef41Sopenharmony_ci    }
3331cb0ef41Sopenharmony_ci
3341cb0ef41Sopenharmony_ci    this.codeVisited = [];
3351cb0ef41Sopenharmony_ci    this.filter = filter;
3361cb0ef41Sopenharmony_ci  }
3371cb0ef41Sopenharmony_ci
3381cb0ef41Sopenharmony_ci  addStack(file, tickIndex) {
3391cb0ef41Sopenharmony_ci    let stack = file.ticks[tickIndex].s;
3401cb0ef41Sopenharmony_ci    let vmState = file.ticks[tickIndex].vm;
3411cb0ef41Sopenharmony_ci
3421cb0ef41Sopenharmony_ci    this.tree.ticks++;
3431cb0ef41Sopenharmony_ci    let child = null;
3441cb0ef41Sopenharmony_ci    let tree = null;
3451cb0ef41Sopenharmony_ci    for (let i = stack.length - 2; i >= 0; i -= 2) {
3461cb0ef41Sopenharmony_ci      let codeId = stack[i];
3471cb0ef41Sopenharmony_ci      if (codeId < 0 || this.codeVisited[codeId]) continue;
3481cb0ef41Sopenharmony_ci
3491cb0ef41Sopenharmony_ci      let code = file.code[codeId];
3501cb0ef41Sopenharmony_ci      if (this.filter) {
3511cb0ef41Sopenharmony_ci        let type = code ? code.type : undefined;
3521cb0ef41Sopenharmony_ci        let kind = code ? code.kind : undefined;
3531cb0ef41Sopenharmony_ci        if (!this.filter(type, kind)) continue;
3541cb0ef41Sopenharmony_ci      }
3551cb0ef41Sopenharmony_ci      let childId = childIdFromCode(codeId, code);
3561cb0ef41Sopenharmony_ci      if (this.categories) {
3571cb0ef41Sopenharmony_ci        let kind = resolveCodeKindAndVmState(code, vmState);
3581cb0ef41Sopenharmony_ci        tree = this.categories[kind];
3591cb0ef41Sopenharmony_ci      } else {
3601cb0ef41Sopenharmony_ci        tree = this.tree;
3611cb0ef41Sopenharmony_ci      }
3621cb0ef41Sopenharmony_ci      child = tree.children[childId];
3631cb0ef41Sopenharmony_ci      if (!child) {
3641cb0ef41Sopenharmony_ci        child = createNodeFromStackEntry(code, codeId, vmState);
3651cb0ef41Sopenharmony_ci        child.children[0] = createEmptyNode("Top-down tree");
3661cb0ef41Sopenharmony_ci        child.children[0].delayedExpansion =
3671cb0ef41Sopenharmony_ci          { frameList : [], ascending : false };
3681cb0ef41Sopenharmony_ci        child.children[1] = createEmptyNode("Bottom-up tree");
3691cb0ef41Sopenharmony_ci        child.children[1].delayedExpansion =
3701cb0ef41Sopenharmony_ci          { frameList : [], ascending : true };
3711cb0ef41Sopenharmony_ci        tree.children[childId] = child;
3721cb0ef41Sopenharmony_ci      }
3731cb0ef41Sopenharmony_ci      child.ticks++;
3741cb0ef41Sopenharmony_ci      child.children[0].ticks++;
3751cb0ef41Sopenharmony_ci      addFrameToFrameList(
3761cb0ef41Sopenharmony_ci          child.children[0].delayedExpansion.frameList, tickIndex, i);
3771cb0ef41Sopenharmony_ci      child.children[1].ticks++;
3781cb0ef41Sopenharmony_ci      addFrameToFrameList(
3791cb0ef41Sopenharmony_ci          child.children[1].delayedExpansion.frameList, tickIndex, i);
3801cb0ef41Sopenharmony_ci      this.codeVisited[codeId] = true;
3811cb0ef41Sopenharmony_ci    }
3821cb0ef41Sopenharmony_ci    if (child) {
3831cb0ef41Sopenharmony_ci      child.ownTicks++;
3841cb0ef41Sopenharmony_ci      console.assert(tree !== null);
3851cb0ef41Sopenharmony_ci      tree.ticks++;
3861cb0ef41Sopenharmony_ci      console.assert(tree.type === "CAT");
3871cb0ef41Sopenharmony_ci    }
3881cb0ef41Sopenharmony_ci
3891cb0ef41Sopenharmony_ci    for (let i = 0; i < stack.length; i += 2) {
3901cb0ef41Sopenharmony_ci      let codeId = stack[i];
3911cb0ef41Sopenharmony_ci      if (codeId >= 0) this.codeVisited[codeId] = false;
3921cb0ef41Sopenharmony_ci    }
3931cb0ef41Sopenharmony_ci  }
3941cb0ef41Sopenharmony_ci}
3951cb0ef41Sopenharmony_ci
3961cb0ef41Sopenharmony_ci
3971cb0ef41Sopenharmony_ciclass CategorySampler {
3981cb0ef41Sopenharmony_ci  constructor(file, bucketCount) {
3991cb0ef41Sopenharmony_ci    this.bucketCount = bucketCount;
4001cb0ef41Sopenharmony_ci
4011cb0ef41Sopenharmony_ci    this.firstTime = file.ticks[0].tm;
4021cb0ef41Sopenharmony_ci    let lastTime = file.ticks[file.ticks.length - 1].tm;
4031cb0ef41Sopenharmony_ci    this.step = (lastTime - this.firstTime) / bucketCount;
4041cb0ef41Sopenharmony_ci
4051cb0ef41Sopenharmony_ci    this.buckets = [];
4061cb0ef41Sopenharmony_ci    let bucket = {};
4071cb0ef41Sopenharmony_ci    for (let i = 0; i < codeKinds.length; i++) {
4081cb0ef41Sopenharmony_ci      bucket[codeKinds[i]] = 0;
4091cb0ef41Sopenharmony_ci    }
4101cb0ef41Sopenharmony_ci    for (let i = 0; i < bucketCount; i++) {
4111cb0ef41Sopenharmony_ci      this.buckets.push(Object.assign({ total : 0 }, bucket));
4121cb0ef41Sopenharmony_ci    }
4131cb0ef41Sopenharmony_ci  }
4141cb0ef41Sopenharmony_ci
4151cb0ef41Sopenharmony_ci  addStack(file, tickIndex) {
4161cb0ef41Sopenharmony_ci    let { tm : timestamp, vm : vmState, s : stack } = file.ticks[tickIndex];
4171cb0ef41Sopenharmony_ci
4181cb0ef41Sopenharmony_ci    let i = Math.floor((timestamp - this.firstTime) / this.step);
4191cb0ef41Sopenharmony_ci    if (i === this.buckets.length) i--;
4201cb0ef41Sopenharmony_ci    console.assert(i >= 0 && i < this.buckets.length);
4211cb0ef41Sopenharmony_ci
4221cb0ef41Sopenharmony_ci    let bucket = this.buckets[i];
4231cb0ef41Sopenharmony_ci    bucket.total++;
4241cb0ef41Sopenharmony_ci
4251cb0ef41Sopenharmony_ci    let codeId = (stack.length > 0) ? stack[0] : -1;
4261cb0ef41Sopenharmony_ci    let code = codeId >= 0 ? file.code[codeId] : undefined;
4271cb0ef41Sopenharmony_ci    let kind = resolveCodeKindAndVmState(code, vmState);
4281cb0ef41Sopenharmony_ci    bucket[kind]++;
4291cb0ef41Sopenharmony_ci  }
4301cb0ef41Sopenharmony_ci}
4311cb0ef41Sopenharmony_ci
4321cb0ef41Sopenharmony_ciclass FunctionTimelineProcessor {
4331cb0ef41Sopenharmony_ci  constructor(functionCodeId, filter) {
4341cb0ef41Sopenharmony_ci    this.functionCodeId = functionCodeId;
4351cb0ef41Sopenharmony_ci    this.filter = filter;
4361cb0ef41Sopenharmony_ci    this.blocks = [];
4371cb0ef41Sopenharmony_ci    this.currentBlock = null;
4381cb0ef41Sopenharmony_ci  }
4391cb0ef41Sopenharmony_ci
4401cb0ef41Sopenharmony_ci  addStack(file, tickIndex) {
4411cb0ef41Sopenharmony_ci    if (!this.functionCodeId) return;
4421cb0ef41Sopenharmony_ci
4431cb0ef41Sopenharmony_ci    let { tm : timestamp, vm : vmState, s : stack } = file.ticks[tickIndex];
4441cb0ef41Sopenharmony_ci    let functionCode = file.code[this.functionCodeId];
4451cb0ef41Sopenharmony_ci
4461cb0ef41Sopenharmony_ci    // Find if the function is on the stack, and its position on the stack,
4471cb0ef41Sopenharmony_ci    // ignoring any filtered entries.
4481cb0ef41Sopenharmony_ci    let stackCode = undefined;
4491cb0ef41Sopenharmony_ci    let functionPosInStack = -1;
4501cb0ef41Sopenharmony_ci    let filteredI = 0;
4511cb0ef41Sopenharmony_ci    for (let i = 0; i < stack.length - 1; i += 2) {
4521cb0ef41Sopenharmony_ci      let codeId = stack[i];
4531cb0ef41Sopenharmony_ci      let code = codeId >= 0 ? file.code[codeId] : undefined;
4541cb0ef41Sopenharmony_ci      let type = code ? code.type : undefined;
4551cb0ef41Sopenharmony_ci      let kind = code ? code.kind : undefined;
4561cb0ef41Sopenharmony_ci      if (!this.filter(type, kind)) continue;
4571cb0ef41Sopenharmony_ci
4581cb0ef41Sopenharmony_ci      // Match other instances of the same function (e.g. unoptimised, various
4591cb0ef41Sopenharmony_ci      // different optimised versions).
4601cb0ef41Sopenharmony_ci      if (codeEquals(code, functionCode, true)) {
4611cb0ef41Sopenharmony_ci        functionPosInStack = filteredI;
4621cb0ef41Sopenharmony_ci        stackCode = code;
4631cb0ef41Sopenharmony_ci        break;
4641cb0ef41Sopenharmony_ci      }
4651cb0ef41Sopenharmony_ci      filteredI++;
4661cb0ef41Sopenharmony_ci    }
4671cb0ef41Sopenharmony_ci
4681cb0ef41Sopenharmony_ci    if (functionPosInStack >= 0) {
4691cb0ef41Sopenharmony_ci      let stackKind = resolveCodeKindAndVmState(stackCode, vmState);
4701cb0ef41Sopenharmony_ci
4711cb0ef41Sopenharmony_ci      let codeIsTopOfStack = (functionPosInStack === 0);
4721cb0ef41Sopenharmony_ci
4731cb0ef41Sopenharmony_ci      if (this.currentBlock !== null) {
4741cb0ef41Sopenharmony_ci        this.currentBlock.end = timestamp;
4751cb0ef41Sopenharmony_ci
4761cb0ef41Sopenharmony_ci        if (codeIsTopOfStack === this.currentBlock.topOfStack
4771cb0ef41Sopenharmony_ci          && stackKind === this.currentBlock.kind) {
4781cb0ef41Sopenharmony_ci          // If we haven't changed the stack top or the function kind, then
4791cb0ef41Sopenharmony_ci          // we're happy just extending the current block and not starting
4801cb0ef41Sopenharmony_ci          // a new one.
4811cb0ef41Sopenharmony_ci          return;
4821cb0ef41Sopenharmony_ci        }
4831cb0ef41Sopenharmony_ci      }
4841cb0ef41Sopenharmony_ci
4851cb0ef41Sopenharmony_ci      // Start a new block at the current timestamp.
4861cb0ef41Sopenharmony_ci      this.currentBlock = {
4871cb0ef41Sopenharmony_ci        start: timestamp,
4881cb0ef41Sopenharmony_ci        end: timestamp,
4891cb0ef41Sopenharmony_ci        code: stackCode,
4901cb0ef41Sopenharmony_ci        kind: stackKind,
4911cb0ef41Sopenharmony_ci        topOfStack: codeIsTopOfStack
4921cb0ef41Sopenharmony_ci      };
4931cb0ef41Sopenharmony_ci      this.blocks.push(this.currentBlock);
4941cb0ef41Sopenharmony_ci    } else {
4951cb0ef41Sopenharmony_ci      this.currentBlock = null;
4961cb0ef41Sopenharmony_ci    }
4971cb0ef41Sopenharmony_ci  }
4981cb0ef41Sopenharmony_ci}
4991cb0ef41Sopenharmony_ci
5001cb0ef41Sopenharmony_ci// Generates a tree out of a ticks sequence.
5011cb0ef41Sopenharmony_ci// {file} is the JSON files with the ticks and code objects.
5021cb0ef41Sopenharmony_ci// {startTime}, {endTime} is the interval.
5031cb0ef41Sopenharmony_ci// {tree} is the processor of stacks.
5041cb0ef41Sopenharmony_cifunction generateTree(
5051cb0ef41Sopenharmony_ci    file, startTime, endTime, tree) {
5061cb0ef41Sopenharmony_ci  let ticks = file.ticks;
5071cb0ef41Sopenharmony_ci  let i = 0;
5081cb0ef41Sopenharmony_ci  while (i < ticks.length && ticks[i].tm < startTime) {
5091cb0ef41Sopenharmony_ci    i++;
5101cb0ef41Sopenharmony_ci  }
5111cb0ef41Sopenharmony_ci
5121cb0ef41Sopenharmony_ci  let tickCount = 0;
5131cb0ef41Sopenharmony_ci  while (i < ticks.length && ticks[i].tm < endTime) {
5141cb0ef41Sopenharmony_ci    tree.addStack(file, i);
5151cb0ef41Sopenharmony_ci    i++;
5161cb0ef41Sopenharmony_ci    tickCount++;
5171cb0ef41Sopenharmony_ci  }
5181cb0ef41Sopenharmony_ci
5191cb0ef41Sopenharmony_ci  return tickCount;
5201cb0ef41Sopenharmony_ci}
5211cb0ef41Sopenharmony_ci
5221cb0ef41Sopenharmony_cifunction computeOptimizationStats(file,
5231cb0ef41Sopenharmony_ci    timeStart = -Infinity, timeEnd = Infinity) {
5241cb0ef41Sopenharmony_ci  function newCollection() {
5251cb0ef41Sopenharmony_ci    return { count : 0, functions : [], functionTable : [] };
5261cb0ef41Sopenharmony_ci  }
5271cb0ef41Sopenharmony_ci  function addToCollection(collection, code) {
5281cb0ef41Sopenharmony_ci    collection.count++;
5291cb0ef41Sopenharmony_ci    let funcData = collection.functionTable[code.func];
5301cb0ef41Sopenharmony_ci    if (!funcData) {
5311cb0ef41Sopenharmony_ci      funcData = { f : file.functions[code.func], instances : [] };
5321cb0ef41Sopenharmony_ci      collection.functionTable[code.func] = funcData;
5331cb0ef41Sopenharmony_ci      collection.functions.push(funcData);
5341cb0ef41Sopenharmony_ci    }
5351cb0ef41Sopenharmony_ci    funcData.instances.push(code);
5361cb0ef41Sopenharmony_ci  }
5371cb0ef41Sopenharmony_ci
5381cb0ef41Sopenharmony_ci  let functionCount = 0;
5391cb0ef41Sopenharmony_ci  let optimizedFunctionCount = 0;
5401cb0ef41Sopenharmony_ci  let turbopropOptimizedFunctionCount = 0;
5411cb0ef41Sopenharmony_ci  let deoptimizedFunctionCount = 0;
5421cb0ef41Sopenharmony_ci  let optimizations = newCollection();
5431cb0ef41Sopenharmony_ci  let turbopropOptimizations = newCollection();
5441cb0ef41Sopenharmony_ci  let eagerDeoptimizations = newCollection();
5451cb0ef41Sopenharmony_ci  let softDeoptimizations = newCollection();
5461cb0ef41Sopenharmony_ci  let lazyDeoptimizations = newCollection();
5471cb0ef41Sopenharmony_ci  let softBailouts = newCollection();
5481cb0ef41Sopenharmony_ci  let eagerBailouts = newCollection();
5491cb0ef41Sopenharmony_ci
5501cb0ef41Sopenharmony_ci  for (let i = 0; i < file.functions.length; i++) {
5511cb0ef41Sopenharmony_ci    let f = file.functions[i];
5521cb0ef41Sopenharmony_ci
5531cb0ef41Sopenharmony_ci    // Skip special SFIs that do not correspond to JS functions.
5541cb0ef41Sopenharmony_ci    if (f.codes.length === 0) continue;
5551cb0ef41Sopenharmony_ci    if (file.code[f.codes[0]].type !== "JS") continue;
5561cb0ef41Sopenharmony_ci
5571cb0ef41Sopenharmony_ci    functionCount++;
5581cb0ef41Sopenharmony_ci    let optimized = false;
5591cb0ef41Sopenharmony_ci    let turboprop_optimized = false;
5601cb0ef41Sopenharmony_ci    let deoptimized = false;
5611cb0ef41Sopenharmony_ci
5621cb0ef41Sopenharmony_ci    for (let j = 0; j < f.codes.length; j++) {
5631cb0ef41Sopenharmony_ci      let code = file.code[f.codes[j]];
5641cb0ef41Sopenharmony_ci      console.assert(code.type === "JS");
5651cb0ef41Sopenharmony_ci      if (code.kind === "Opt") {
5661cb0ef41Sopenharmony_ci        optimized = true;
5671cb0ef41Sopenharmony_ci        if (code.tm >= timeStart && code.tm <= timeEnd) {
5681cb0ef41Sopenharmony_ci          addToCollection(optimizations, code);
5691cb0ef41Sopenharmony_ci        }
5701cb0ef41Sopenharmony_ci      }
5711cb0ef41Sopenharmony_ci      if (code.kind === "Turboprop") {
5721cb0ef41Sopenharmony_ci        turboprop_optimized = true;
5731cb0ef41Sopenharmony_ci        if (code.tm >= timeStart && code.tm <= timeEnd) {
5741cb0ef41Sopenharmony_ci          addToCollection(turbopropOptimizations, code);
5751cb0ef41Sopenharmony_ci        }
5761cb0ef41Sopenharmony_ci      }
5771cb0ef41Sopenharmony_ci      if (code.deopt) {
5781cb0ef41Sopenharmony_ci        if (code.deopt.bailoutType === "deopt-lazy" || code.deopt.bailoutType === "deopt-eager" || code.deopt.bailoutType === "deopt-lazy") {
5791cb0ef41Sopenharmony_ci          deoptimized = true;
5801cb0ef41Sopenharmony_ci        }
5811cb0ef41Sopenharmony_ci        if (code.deopt.tm >= timeStart && code.deopt.tm <= timeEnd) {
5821cb0ef41Sopenharmony_ci          switch (code.deopt.bailoutType) {
5831cb0ef41Sopenharmony_ci            case "deopt-lazy":
5841cb0ef41Sopenharmony_ci              addToCollection(lazyDeoptimizations, code);
5851cb0ef41Sopenharmony_ci              break;
5861cb0ef41Sopenharmony_ci            case "deopt-eager":
5871cb0ef41Sopenharmony_ci              addToCollection(eagerDeoptimizations, code);
5881cb0ef41Sopenharmony_ci              break;
5891cb0ef41Sopenharmony_ci            case "deopt-soft":
5901cb0ef41Sopenharmony_ci              addToCollection(softDeoptimizations, code);
5911cb0ef41Sopenharmony_ci              break;
5921cb0ef41Sopenharmony_ci            case "bailout-soft":
5931cb0ef41Sopenharmony_ci              addToCollection(softBailouts, code);
5941cb0ef41Sopenharmony_ci              break;
5951cb0ef41Sopenharmony_ci            case "bailout":
5961cb0ef41Sopenharmony_ci              addToCollection(eagerBailouts, code);
5971cb0ef41Sopenharmony_ci              break;
5981cb0ef41Sopenharmony_ci          }
5991cb0ef41Sopenharmony_ci        }
6001cb0ef41Sopenharmony_ci      }
6011cb0ef41Sopenharmony_ci    }
6021cb0ef41Sopenharmony_ci    if (optimized) {
6031cb0ef41Sopenharmony_ci      optimizedFunctionCount++;
6041cb0ef41Sopenharmony_ci    }
6051cb0ef41Sopenharmony_ci    if (turboprop_optimized) {
6061cb0ef41Sopenharmony_ci      turbopropOptimizedFunctionCount++;
6071cb0ef41Sopenharmony_ci    }
6081cb0ef41Sopenharmony_ci    if (deoptimized) {
6091cb0ef41Sopenharmony_ci      deoptimizedFunctionCount++;
6101cb0ef41Sopenharmony_ci    }
6111cb0ef41Sopenharmony_ci  }
6121cb0ef41Sopenharmony_ci
6131cb0ef41Sopenharmony_ci  function sortCollection(collection) {
6141cb0ef41Sopenharmony_ci    collection.functions.sort(
6151cb0ef41Sopenharmony_ci        (a, b) => a.instances.length - b.instances.length);
6161cb0ef41Sopenharmony_ci  }
6171cb0ef41Sopenharmony_ci
6181cb0ef41Sopenharmony_ci  sortCollection(eagerDeoptimizations);
6191cb0ef41Sopenharmony_ci  sortCollection(lazyDeoptimizations);
6201cb0ef41Sopenharmony_ci  sortCollection(softDeoptimizations);
6211cb0ef41Sopenharmony_ci  sortCollection(optimizations);
6221cb0ef41Sopenharmony_ci  sortCollection(turbopropOptimizations);
6231cb0ef41Sopenharmony_ci
6241cb0ef41Sopenharmony_ci  return {
6251cb0ef41Sopenharmony_ci    functionCount,
6261cb0ef41Sopenharmony_ci    optimizedFunctionCount,
6271cb0ef41Sopenharmony_ci    turbopropOptimizedFunctionCount,
6281cb0ef41Sopenharmony_ci    deoptimizedFunctionCount,
6291cb0ef41Sopenharmony_ci    optimizations,
6301cb0ef41Sopenharmony_ci    turbopropOptimizations,
6311cb0ef41Sopenharmony_ci    eagerDeoptimizations,
6321cb0ef41Sopenharmony_ci    lazyDeoptimizations,
6331cb0ef41Sopenharmony_ci    softDeoptimizations,
6341cb0ef41Sopenharmony_ci    softBailouts,
6351cb0ef41Sopenharmony_ci    eagerBailouts,
6361cb0ef41Sopenharmony_ci  };
6371cb0ef41Sopenharmony_ci}
6381cb0ef41Sopenharmony_ci
6391cb0ef41Sopenharmony_cifunction normalizeLeadingWhitespace(lines) {
6401cb0ef41Sopenharmony_ci  let regex = /^\s*/;
6411cb0ef41Sopenharmony_ci  let minimumLeadingWhitespaceChars = Infinity;
6421cb0ef41Sopenharmony_ci  for (let line of lines) {
6431cb0ef41Sopenharmony_ci    minimumLeadingWhitespaceChars =
6441cb0ef41Sopenharmony_ci        Math.min(minimumLeadingWhitespaceChars, regex.exec(line)[0].length);
6451cb0ef41Sopenharmony_ci  }
6461cb0ef41Sopenharmony_ci  for (let i = 0; i < lines.length; i++) {
6471cb0ef41Sopenharmony_ci    lines[i] = lines[i].substring(minimumLeadingWhitespaceChars);
6481cb0ef41Sopenharmony_ci  }
6491cb0ef41Sopenharmony_ci}
650