11cb0ef41Sopenharmony_ci// Copyright 2021 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_ciimport {TickLogEntry} from '../../log/tick.mjs';
61cb0ef41Sopenharmony_ciimport {Timeline} from '../../timeline.mjs';
71cb0ef41Sopenharmony_ciimport {delay, DOM, SVG} from '../helper.mjs';
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ciimport {TimelineTrackStackedBase} from './timeline-track-stacked-base.mjs'
101cb0ef41Sopenharmony_ci
111cb0ef41Sopenharmony_ciclass Flame {
121cb0ef41Sopenharmony_ci  constructor(time, logEntry, depth) {
131cb0ef41Sopenharmony_ci    this._time = time;
141cb0ef41Sopenharmony_ci    this._logEntry = logEntry;
151cb0ef41Sopenharmony_ci    this.depth = depth;
161cb0ef41Sopenharmony_ci    this._duration = -1;
171cb0ef41Sopenharmony_ci    this.parent = undefined;
181cb0ef41Sopenharmony_ci    this.children = [];
191cb0ef41Sopenharmony_ci  }
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_ci  static add(time, logEntry, stack, flames) {
221cb0ef41Sopenharmony_ci    const depth = stack.length;
231cb0ef41Sopenharmony_ci    const newFlame = new Flame(time, logEntry, depth)
241cb0ef41Sopenharmony_ci    if (depth > 0) {
251cb0ef41Sopenharmony_ci      const parent = stack[depth - 1];
261cb0ef41Sopenharmony_ci      newFlame.parent = parent;
271cb0ef41Sopenharmony_ci      parent.children.push(newFlame);
281cb0ef41Sopenharmony_ci    }
291cb0ef41Sopenharmony_ci    flames.push(newFlame);
301cb0ef41Sopenharmony_ci    stack.push(newFlame);
311cb0ef41Sopenharmony_ci  }
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ci  stop(time) {
341cb0ef41Sopenharmony_ci    if (this._duration !== -1) throw new Error('Already stopped');
351cb0ef41Sopenharmony_ci    this._duration = time - this._time
361cb0ef41Sopenharmony_ci  }
371cb0ef41Sopenharmony_ci
381cb0ef41Sopenharmony_ci  get time() {
391cb0ef41Sopenharmony_ci    return this._time;
401cb0ef41Sopenharmony_ci  }
411cb0ef41Sopenharmony_ci
421cb0ef41Sopenharmony_ci  get logEntry() {
431cb0ef41Sopenharmony_ci    return this._logEntry;
441cb0ef41Sopenharmony_ci  }
451cb0ef41Sopenharmony_ci
461cb0ef41Sopenharmony_ci  get startTime() {
471cb0ef41Sopenharmony_ci    return this._time;
481cb0ef41Sopenharmony_ci  }
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ci  get endTime() {
511cb0ef41Sopenharmony_ci    return this._time + this._duration;
521cb0ef41Sopenharmony_ci  }
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_ci  get duration() {
551cb0ef41Sopenharmony_ci    return this._duration;
561cb0ef41Sopenharmony_ci  }
571cb0ef41Sopenharmony_ci
581cb0ef41Sopenharmony_ci  get type() {
591cb0ef41Sopenharmony_ci    return TickLogEntry.extractCodeEntryType(this._logEntry?.entry);
601cb0ef41Sopenharmony_ci  }
611cb0ef41Sopenharmony_ci}
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ciDOM.defineCustomElement(
641cb0ef41Sopenharmony_ci    'view/timeline/timeline-track', 'timeline-track-tick',
651cb0ef41Sopenharmony_ci    (templateText) => class TimelineTrackTick extends TimelineTrackStackedBase {
661cb0ef41Sopenharmony_ci      constructor() {
671cb0ef41Sopenharmony_ci        super(templateText);
681cb0ef41Sopenharmony_ci        this._annotations = new Annotations(this);
691cb0ef41Sopenharmony_ci      }
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_ci      _prepareDrawableItems() {
721cb0ef41Sopenharmony_ci        const tmpFlames = [];
731cb0ef41Sopenharmony_ci        // flameStack = [bottom, ..., top];
741cb0ef41Sopenharmony_ci        const flameStack = [];
751cb0ef41Sopenharmony_ci        const ticks = this._timeline.values;
761cb0ef41Sopenharmony_ci        let maxDepth = 0;
771cb0ef41Sopenharmony_ci        for (let tickIndex = 0; tickIndex < ticks.length; tickIndex++) {
781cb0ef41Sopenharmony_ci          const tick = ticks[tickIndex];
791cb0ef41Sopenharmony_ci          const tickStack = tick.stack;
801cb0ef41Sopenharmony_ci          maxDepth = Math.max(maxDepth, tickStack.length);
811cb0ef41Sopenharmony_ci          // tick.stack = [top, .... , bottom];
821cb0ef41Sopenharmony_ci          for (let stackIndex = tickStack.length - 1; stackIndex >= 0;
831cb0ef41Sopenharmony_ci               stackIndex--) {
841cb0ef41Sopenharmony_ci            const codeEntry = tickStack[stackIndex];
851cb0ef41Sopenharmony_ci            // codeEntry is either a CodeEntry or a raw pc.
861cb0ef41Sopenharmony_ci            const logEntry = codeEntry?.logEntry;
871cb0ef41Sopenharmony_ci            const flameStackIndex = tickStack.length - stackIndex - 1;
881cb0ef41Sopenharmony_ci            if (flameStackIndex < flameStack.length) {
891cb0ef41Sopenharmony_ci              if (flameStack[flameStackIndex].logEntry === logEntry) continue;
901cb0ef41Sopenharmony_ci              for (let k = flameStackIndex; k < flameStack.length; k++) {
911cb0ef41Sopenharmony_ci                flameStack[k].stop(tick.time);
921cb0ef41Sopenharmony_ci              }
931cb0ef41Sopenharmony_ci              flameStack.length = flameStackIndex;
941cb0ef41Sopenharmony_ci            }
951cb0ef41Sopenharmony_ci            Flame.add(tick.time, logEntry, flameStack, tmpFlames);
961cb0ef41Sopenharmony_ci          }
971cb0ef41Sopenharmony_ci          if (tickStack.length < flameStack.length) {
981cb0ef41Sopenharmony_ci            for (let k = tickStack.length; k < flameStack.length; k++) {
991cb0ef41Sopenharmony_ci              flameStack[k].stop(tick.time);
1001cb0ef41Sopenharmony_ci            }
1011cb0ef41Sopenharmony_ci            flameStack.length = tickStack.length;
1021cb0ef41Sopenharmony_ci          }
1031cb0ef41Sopenharmony_ci        }
1041cb0ef41Sopenharmony_ci        const lastTime = ticks[ticks.length - 1].time;
1051cb0ef41Sopenharmony_ci        for (let k = 0; k < flameStack.length; k++) {
1061cb0ef41Sopenharmony_ci          flameStack[k].stop(lastTime);
1071cb0ef41Sopenharmony_ci        }
1081cb0ef41Sopenharmony_ci        this._drawableItems = new Timeline(Flame, tmpFlames);
1091cb0ef41Sopenharmony_ci        this._annotations.flames = this._drawableItems;
1101cb0ef41Sopenharmony_ci        this._adjustStackDepth(maxDepth);
1111cb0ef41Sopenharmony_ci      }
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_ci      _drawAnnotations(logEntry, time) {
1141cb0ef41Sopenharmony_ci        if (time === undefined) {
1151cb0ef41Sopenharmony_ci          time = this.relativePositionToTime(this._timelineScrollLeft);
1161cb0ef41Sopenharmony_ci        }
1171cb0ef41Sopenharmony_ci        this._annotations.update(logEntry, time);
1181cb0ef41Sopenharmony_ci      }
1191cb0ef41Sopenharmony_ci
1201cb0ef41Sopenharmony_ci      _drawableItemToLogEntry(flame) {
1211cb0ef41Sopenharmony_ci        const logEntry = flame?.logEntry;
1221cb0ef41Sopenharmony_ci        if (logEntry === undefined || typeof logEntry == 'number')
1231cb0ef41Sopenharmony_ci          return undefined;
1241cb0ef41Sopenharmony_ci        return logEntry;
1251cb0ef41Sopenharmony_ci      }
1261cb0ef41Sopenharmony_ci    })
1271cb0ef41Sopenharmony_ci
1281cb0ef41Sopenharmony_ciclass Annotations {
1291cb0ef41Sopenharmony_ci  _flames;
1301cb0ef41Sopenharmony_ci  _logEntry;
1311cb0ef41Sopenharmony_ci  _buffer;
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_ci  constructor(track) {
1341cb0ef41Sopenharmony_ci    this._track = track;
1351cb0ef41Sopenharmony_ci  }
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_ci  set flames(flames) {
1381cb0ef41Sopenharmony_ci    this._flames = flames;
1391cb0ef41Sopenharmony_ci  }
1401cb0ef41Sopenharmony_ci
1411cb0ef41Sopenharmony_ci  get _node() {
1421cb0ef41Sopenharmony_ci    return this._track.timelineAnnotationsNode;
1431cb0ef41Sopenharmony_ci  }
1441cb0ef41Sopenharmony_ci
1451cb0ef41Sopenharmony_ci  async update(logEntry, time) {
1461cb0ef41Sopenharmony_ci    if (this._logEntry == logEntry) return;
1471cb0ef41Sopenharmony_ci    this._logEntry = logEntry;
1481cb0ef41Sopenharmony_ci    this._node.innerHTML = '';
1491cb0ef41Sopenharmony_ci    if (logEntry === undefined) return;
1501cb0ef41Sopenharmony_ci    this._buffer = '';
1511cb0ef41Sopenharmony_ci    const start = this._flames.find(time);
1521cb0ef41Sopenharmony_ci    let offset = 0;
1531cb0ef41Sopenharmony_ci    // Draw annotations gradually outwards starting form the given time.
1541cb0ef41Sopenharmony_ci    let deadline = performance.now() + 100;
1551cb0ef41Sopenharmony_ci    for (let range = 0; range < this._flames.length; range += 10000) {
1561cb0ef41Sopenharmony_ci      this._markFlames(start - range, start - offset);
1571cb0ef41Sopenharmony_ci      this._markFlames(start + offset, start + range);
1581cb0ef41Sopenharmony_ci      offset = range;
1591cb0ef41Sopenharmony_ci      if ((navigator?.scheduling?.isInputPending({includeContinuous: true}) ??
1601cb0ef41Sopenharmony_ci           false) ||
1611cb0ef41Sopenharmony_ci          performance.now() >= deadline) {
1621cb0ef41Sopenharmony_ci        // Yield if we have to handle an input event, or we're out of time.
1631cb0ef41Sopenharmony_ci        await delay(50);
1641cb0ef41Sopenharmony_ci        // Abort if we started another update asynchronously.
1651cb0ef41Sopenharmony_ci        if (this._logEntry != logEntry) return;
1661cb0ef41Sopenharmony_ci
1671cb0ef41Sopenharmony_ci        deadline = performance.now() + 100;
1681cb0ef41Sopenharmony_ci      }
1691cb0ef41Sopenharmony_ci      this._drawBuffer();
1701cb0ef41Sopenharmony_ci    }
1711cb0ef41Sopenharmony_ci    this._drawBuffer();
1721cb0ef41Sopenharmony_ci  }
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci  _markFlames(start, end) {
1751cb0ef41Sopenharmony_ci    const rawFlames = this._flames.values;
1761cb0ef41Sopenharmony_ci    if (start < 0) start = 0;
1771cb0ef41Sopenharmony_ci    if (end > rawFlames.length) end = rawFlames.length;
1781cb0ef41Sopenharmony_ci    const logEntry = this._logEntry;
1791cb0ef41Sopenharmony_ci    // Also compare against the function, if any.
1801cb0ef41Sopenharmony_ci    const func = logEntry.entry?.func ?? -1;
1811cb0ef41Sopenharmony_ci    for (let i = start; i < end; i++) {
1821cb0ef41Sopenharmony_ci      const flame = rawFlames[i];
1831cb0ef41Sopenharmony_ci      const flameLogEntry = flame.logEntry;
1841cb0ef41Sopenharmony_ci      if (!flameLogEntry) continue;
1851cb0ef41Sopenharmony_ci      if (flameLogEntry !== logEntry) {
1861cb0ef41Sopenharmony_ci        if (flameLogEntry.entry?.func !== func) continue;
1871cb0ef41Sopenharmony_ci      }
1881cb0ef41Sopenharmony_ci      this._buffer += this._track._drawItem(flame, i, true);
1891cb0ef41Sopenharmony_ci    }
1901cb0ef41Sopenharmony_ci  }
1911cb0ef41Sopenharmony_ci
1921cb0ef41Sopenharmony_ci  _drawBuffer() {
1931cb0ef41Sopenharmony_ci    if (this._buffer.length == 0) return;
1941cb0ef41Sopenharmony_ci    const svg = SVG.svg();
1951cb0ef41Sopenharmony_ci    svg.innerHTML = this._buffer;
1961cb0ef41Sopenharmony_ci    this._node.appendChild(svg);
1971cb0ef41Sopenharmony_ci    this._buffer = '';
1981cb0ef41Sopenharmony_ci  }
1991cb0ef41Sopenharmony_ci}
200