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 {MB} from '../js/helper.mjs';
61cb0ef41Sopenharmony_ciimport {DOM} from '../js/web-api-helper.mjs';
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ciimport {getColorFromSpaceName, kSpaceNames} from './space-categories.mjs';
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_ciclass TrendLineHelper {
111cb0ef41Sopenharmony_ci  static re_gc_count = /(?<=(Before|After) GC:)\d+(?=,)/;
121cb0ef41Sopenharmony_ci  static re_allocated = /allocated/;
131cb0ef41Sopenharmony_ci  static re_space_name = /^[a-z_]+_space/;
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_ci  static snapshotHeaderToXLabel(header) {
161cb0ef41Sopenharmony_ci    const gc_count = this.re_gc_count.exec(header)[0];
171cb0ef41Sopenharmony_ci    const alpha = header[0];
181cb0ef41Sopenharmony_ci    return alpha + gc_count;
191cb0ef41Sopenharmony_ci  }
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_ci  static getLineSymbolFromTrendLineName(trend_line_name) {
221cb0ef41Sopenharmony_ci    const is_allocated_line = this.re_allocated.test(trend_line_name);
231cb0ef41Sopenharmony_ci    if (is_allocated_line) {
241cb0ef41Sopenharmony_ci      return 'emptyTriangle';
251cb0ef41Sopenharmony_ci    }
261cb0ef41Sopenharmony_ci    return 'emptyCircle';
271cb0ef41Sopenharmony_ci  }
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ci  static getSizeTrendLineName(space_name) {
301cb0ef41Sopenharmony_ci    return space_name + ' size';
311cb0ef41Sopenharmony_ci  }
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ci  static getAllocatedTrendSizeName(space_name) {
341cb0ef41Sopenharmony_ci    return space_name + ' allocated';
351cb0ef41Sopenharmony_ci  }
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ci  static getSpaceNameFromTrendLineName(trend_line_name) {
381cb0ef41Sopenharmony_ci    const space_name = this.re_space_name.exec(trend_line_name)[0];
391cb0ef41Sopenharmony_ci    return space_name;
401cb0ef41Sopenharmony_ci  }
411cb0ef41Sopenharmony_ci}
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_ciDOM.defineCustomElement('heap-size-trend-viewer',
441cb0ef41Sopenharmony_ci                        (templateText) =>
451cb0ef41Sopenharmony_ci                            class HeapSizeTrendViewer extends HTMLElement {
461cb0ef41Sopenharmony_ci  constructor() {
471cb0ef41Sopenharmony_ci    super();
481cb0ef41Sopenharmony_ci    const shadowRoot = this.attachShadow({mode: 'open'});
491cb0ef41Sopenharmony_ci    shadowRoot.innerHTML = templateText;
501cb0ef41Sopenharmony_ci    this.chart = echarts.init(this.$('#chart'), null, {
511cb0ef41Sopenharmony_ci      renderer: 'canvas',
521cb0ef41Sopenharmony_ci    });
531cb0ef41Sopenharmony_ci    this.chart.getZr().on('click', 'series.line', (params) => {
541cb0ef41Sopenharmony_ci      const pointInPixel = [params.offsetX, params.offsetY];
551cb0ef41Sopenharmony_ci      const pointInGrid =
561cb0ef41Sopenharmony_ci          this.chart.convertFromPixel({seriesIndex: 0}, pointInPixel);
571cb0ef41Sopenharmony_ci      const xIndex = pointInGrid[0];
581cb0ef41Sopenharmony_ci      this.dispatchEvent(new CustomEvent('change', {
591cb0ef41Sopenharmony_ci        bubbles: true,
601cb0ef41Sopenharmony_ci        composed: true,
611cb0ef41Sopenharmony_ci        detail: xIndex,
621cb0ef41Sopenharmony_ci      }));
631cb0ef41Sopenharmony_ci      this.setXMarkLine(xIndex);
641cb0ef41Sopenharmony_ci    });
651cb0ef41Sopenharmony_ci    this.chartXAxisData = null;
661cb0ef41Sopenharmony_ci    this.chartSeriesData = null;
671cb0ef41Sopenharmony_ci    this.currentIndex = 0;
681cb0ef41Sopenharmony_ci    window.addEventListener('resize', () => {
691cb0ef41Sopenharmony_ci      this.chart.resize();
701cb0ef41Sopenharmony_ci    });
711cb0ef41Sopenharmony_ci  }
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_ci  $(id) {
741cb0ef41Sopenharmony_ci    return this.shadowRoot.querySelector(id);
751cb0ef41Sopenharmony_ci  }
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ci  set data(value) {
781cb0ef41Sopenharmony_ci    this._data = value;
791cb0ef41Sopenharmony_ci    this.stateChanged();
801cb0ef41Sopenharmony_ci  }
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci  get data() {
831cb0ef41Sopenharmony_ci    return this._data;
841cb0ef41Sopenharmony_ci  }
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci  hide() {
871cb0ef41Sopenharmony_ci    this.$('#container').style.display = 'none';
881cb0ef41Sopenharmony_ci  }
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci  show() {
911cb0ef41Sopenharmony_ci    this.$('#container').style.display = 'block';
921cb0ef41Sopenharmony_ci  }
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ci  stateChanged() {
951cb0ef41Sopenharmony_ci    this.initTrendLineNames();
961cb0ef41Sopenharmony_ci    this.initXAxisDataAndSeries();
971cb0ef41Sopenharmony_ci    this.drawChart();
981cb0ef41Sopenharmony_ci  }
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ci  initTrendLineNames() {
1011cb0ef41Sopenharmony_ci    this.trend_line_names = [];
1021cb0ef41Sopenharmony_ci    for (const space_name of kSpaceNames) {
1031cb0ef41Sopenharmony_ci      this.trend_line_names.push(
1041cb0ef41Sopenharmony_ci          TrendLineHelper.getSizeTrendLineName(space_name));
1051cb0ef41Sopenharmony_ci      this.trend_line_names.push(
1061cb0ef41Sopenharmony_ci          TrendLineHelper.getAllocatedTrendSizeName(space_name));
1071cb0ef41Sopenharmony_ci    }
1081cb0ef41Sopenharmony_ci  }
1091cb0ef41Sopenharmony_ci
1101cb0ef41Sopenharmony_ci  // X axis represent the moment before or after nth GC : [B1,A1,...Bn,An].
1111cb0ef41Sopenharmony_ci  initXAxisDataAndSeries() {
1121cb0ef41Sopenharmony_ci    this.chartXAxisData = [];
1131cb0ef41Sopenharmony_ci    this.chartSeriesData = [];
1141cb0ef41Sopenharmony_ci    let trend_line_name_data_dict = {};
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci    for (const trend_line_name of this.trend_line_names) {
1171cb0ef41Sopenharmony_ci      trend_line_name_data_dict[trend_line_name] = [];
1181cb0ef41Sopenharmony_ci    }
1191cb0ef41Sopenharmony_ci
1201cb0ef41Sopenharmony_ci    // Init x axis data and trend line series.
1211cb0ef41Sopenharmony_ci    for (const snapshot of this.data) {
1221cb0ef41Sopenharmony_ci      this.chartXAxisData.push(
1231cb0ef41Sopenharmony_ci          TrendLineHelper.snapshotHeaderToXLabel(snapshot.header));
1241cb0ef41Sopenharmony_ci      for (const [space_name, pageinfos] of Object.entries(snapshot.data)) {
1251cb0ef41Sopenharmony_ci        const size_trend_line_name =
1261cb0ef41Sopenharmony_ci            TrendLineHelper.getSizeTrendLineName(space_name);
1271cb0ef41Sopenharmony_ci        const allocated_trend_line_name =
1281cb0ef41Sopenharmony_ci            TrendLineHelper.getAllocatedTrendSizeName(space_name);
1291cb0ef41Sopenharmony_ci        let size_sum = 0;
1301cb0ef41Sopenharmony_ci        let allocated_sum = 0;
1311cb0ef41Sopenharmony_ci        for (const pageinfo of pageinfos) {
1321cb0ef41Sopenharmony_ci          size_sum += pageinfo[2] - pageinfo[1];
1331cb0ef41Sopenharmony_ci          allocated_sum += pageinfo[3];
1341cb0ef41Sopenharmony_ci        }
1351cb0ef41Sopenharmony_ci        trend_line_name_data_dict[size_trend_line_name].push(size_sum);
1361cb0ef41Sopenharmony_ci        trend_line_name_data_dict[allocated_trend_line_name].push(
1371cb0ef41Sopenharmony_ci            allocated_sum);
1381cb0ef41Sopenharmony_ci      }
1391cb0ef41Sopenharmony_ci    }
1401cb0ef41Sopenharmony_ci
1411cb0ef41Sopenharmony_ci    // Init mark line series as the first series
1421cb0ef41Sopenharmony_ci    const markline_series = {
1431cb0ef41Sopenharmony_ci      name: 'mark-line',
1441cb0ef41Sopenharmony_ci      type: 'line',
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ci      markLine: {
1471cb0ef41Sopenharmony_ci        silent: true,
1481cb0ef41Sopenharmony_ci        symbol: 'none',
1491cb0ef41Sopenharmony_ci        label: {
1501cb0ef41Sopenharmony_ci          show: false,
1511cb0ef41Sopenharmony_ci        },
1521cb0ef41Sopenharmony_ci        lineStyle: {
1531cb0ef41Sopenharmony_ci          color: '#333',
1541cb0ef41Sopenharmony_ci        },
1551cb0ef41Sopenharmony_ci        data: [
1561cb0ef41Sopenharmony_ci          {
1571cb0ef41Sopenharmony_ci            xAxis: 0,
1581cb0ef41Sopenharmony_ci          },
1591cb0ef41Sopenharmony_ci        ],
1601cb0ef41Sopenharmony_ci      },
1611cb0ef41Sopenharmony_ci    };
1621cb0ef41Sopenharmony_ci    this.chartSeriesData.push(markline_series);
1631cb0ef41Sopenharmony_ci
1641cb0ef41Sopenharmony_ci    for (const [trend_line_name, trend_line_data] of Object.entries(
1651cb0ef41Sopenharmony_ci             trend_line_name_data_dict)) {
1661cb0ef41Sopenharmony_ci      const color = getColorFromSpaceName(
1671cb0ef41Sopenharmony_ci          TrendLineHelper.getSpaceNameFromTrendLineName(trend_line_name));
1681cb0ef41Sopenharmony_ci      const trend_line_series = {
1691cb0ef41Sopenharmony_ci        name: trend_line_name,
1701cb0ef41Sopenharmony_ci        type: 'line',
1711cb0ef41Sopenharmony_ci        data: trend_line_data,
1721cb0ef41Sopenharmony_ci        lineStyle: {
1731cb0ef41Sopenharmony_ci          color: color,
1741cb0ef41Sopenharmony_ci        },
1751cb0ef41Sopenharmony_ci        itemStyle: {
1761cb0ef41Sopenharmony_ci          color: color,
1771cb0ef41Sopenharmony_ci        },
1781cb0ef41Sopenharmony_ci        symbol: TrendLineHelper.getLineSymbolFromTrendLineName(trend_line_name),
1791cb0ef41Sopenharmony_ci        symbolSize: 8,
1801cb0ef41Sopenharmony_ci      };
1811cb0ef41Sopenharmony_ci      this.chartSeriesData.push(trend_line_series);
1821cb0ef41Sopenharmony_ci    }
1831cb0ef41Sopenharmony_ci  }
1841cb0ef41Sopenharmony_ci
1851cb0ef41Sopenharmony_ci  setXMarkLine(index) {
1861cb0ef41Sopenharmony_ci    if (index < 0 || index >= this.data.length) {
1871cb0ef41Sopenharmony_ci      console.error('Invalid index:', index);
1881cb0ef41Sopenharmony_ci      return;
1891cb0ef41Sopenharmony_ci    }
1901cb0ef41Sopenharmony_ci    // Set the mark-line series
1911cb0ef41Sopenharmony_ci    this.chartSeriesData[0].markLine.data[0].xAxis = index;
1921cb0ef41Sopenharmony_ci    this.chart.setOption({
1931cb0ef41Sopenharmony_ci      series: this.chartSeriesData,
1941cb0ef41Sopenharmony_ci    });
1951cb0ef41Sopenharmony_ci    this.currentIndex = index;
1961cb0ef41Sopenharmony_ci  }
1971cb0ef41Sopenharmony_ci
1981cb0ef41Sopenharmony_ci  drawChart() {
1991cb0ef41Sopenharmony_ci    const option = {
2001cb0ef41Sopenharmony_ci      dataZoom: [
2011cb0ef41Sopenharmony_ci        {
2021cb0ef41Sopenharmony_ci          type: 'inside',
2031cb0ef41Sopenharmony_ci          filterMode: 'weakFilter',
2041cb0ef41Sopenharmony_ci        },
2051cb0ef41Sopenharmony_ci        {
2061cb0ef41Sopenharmony_ci          type: 'slider',
2071cb0ef41Sopenharmony_ci          filterMode: 'weakFilter',
2081cb0ef41Sopenharmony_ci          labelFormatter: '',
2091cb0ef41Sopenharmony_ci        },
2101cb0ef41Sopenharmony_ci      ],
2111cb0ef41Sopenharmony_ci      title: {
2121cb0ef41Sopenharmony_ci        text: 'Size Trend',
2131cb0ef41Sopenharmony_ci        left: 'center',
2141cb0ef41Sopenharmony_ci      },
2151cb0ef41Sopenharmony_ci      tooltip: {
2161cb0ef41Sopenharmony_ci        trigger: 'axis',
2171cb0ef41Sopenharmony_ci        position(point, params, dom, rect, size) {
2181cb0ef41Sopenharmony_ci          let ret_x = point[0] + 10;
2191cb0ef41Sopenharmony_ci          if (point[0] > size.viewSize[0] * 0.7) {
2201cb0ef41Sopenharmony_ci            ret_x = point[0] - dom.clientWidth - 10;
2211cb0ef41Sopenharmony_ci          }
2221cb0ef41Sopenharmony_ci          return [ret_x, '85%'];
2231cb0ef41Sopenharmony_ci        },
2241cb0ef41Sopenharmony_ci        formatter(params) {
2251cb0ef41Sopenharmony_ci          const colorSpan = (color) =>
2261cb0ef41Sopenharmony_ci              '<span style="display:inline-block;margin-right:1px;border-radius:5px;width:9px;height:9px;background-color:' +
2271cb0ef41Sopenharmony_ci              color + '"></span>';
2281cb0ef41Sopenharmony_ci          let result = '<p>' + params[0].axisValue + '</p>';
2291cb0ef41Sopenharmony_ci          params.forEach((item) => {
2301cb0ef41Sopenharmony_ci            const xx = '<p style="margin:0;">' + colorSpan(item.color) + ' ' +
2311cb0ef41Sopenharmony_ci                item.seriesName + ': ' + (item.data / MB).toFixed(2) + 'MB' +
2321cb0ef41Sopenharmony_ci                '</p>';
2331cb0ef41Sopenharmony_ci            result += xx;
2341cb0ef41Sopenharmony_ci          });
2351cb0ef41Sopenharmony_ci
2361cb0ef41Sopenharmony_ci          return result;
2371cb0ef41Sopenharmony_ci        },
2381cb0ef41Sopenharmony_ci      },
2391cb0ef41Sopenharmony_ci      legend: {
2401cb0ef41Sopenharmony_ci        data: this.trend_line_names,
2411cb0ef41Sopenharmony_ci        top: '6%',
2421cb0ef41Sopenharmony_ci        type: 'scroll',
2431cb0ef41Sopenharmony_ci      },
2441cb0ef41Sopenharmony_ci
2451cb0ef41Sopenharmony_ci      xAxis: {
2461cb0ef41Sopenharmony_ci        minInterval: 1,
2471cb0ef41Sopenharmony_ci        type: 'category',
2481cb0ef41Sopenharmony_ci        boundaryGap: false,
2491cb0ef41Sopenharmony_ci        data: this.chartXAxisData,
2501cb0ef41Sopenharmony_ci      },
2511cb0ef41Sopenharmony_ci      yAxis: {
2521cb0ef41Sopenharmony_ci        type: 'value',
2531cb0ef41Sopenharmony_ci        axisLabel: {
2541cb0ef41Sopenharmony_ci          formatter(value, index) {
2551cb0ef41Sopenharmony_ci            return (value / MB).toFixed(3) + 'MB';
2561cb0ef41Sopenharmony_ci          },
2571cb0ef41Sopenharmony_ci        },
2581cb0ef41Sopenharmony_ci      },
2591cb0ef41Sopenharmony_ci
2601cb0ef41Sopenharmony_ci      series: this.chartSeriesData,
2611cb0ef41Sopenharmony_ci    };
2621cb0ef41Sopenharmony_ci    this.show();
2631cb0ef41Sopenharmony_ci    this.chart.resize();
2641cb0ef41Sopenharmony_ci    this.chart.setOption(option);
2651cb0ef41Sopenharmony_ci  }
2661cb0ef41Sopenharmony_ci});
267