11cb0ef41Sopenharmony_ci// Copyright 2020 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_ciimport {categoryByZoneName} from './categories.js';
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ciimport {
101cb0ef41Sopenharmony_ci  VIEW_TOTALS,
111cb0ef41Sopenharmony_ci  VIEW_BY_ZONE_NAME,
121cb0ef41Sopenharmony_ci  VIEW_BY_ZONE_CATEGORY,
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ci  KIND_ALLOCATED_MEMORY,
151cb0ef41Sopenharmony_ci  KIND_USED_MEMORY,
161cb0ef41Sopenharmony_ci  KIND_FREED_MEMORY,
171cb0ef41Sopenharmony_ci} from './details-selection.js';
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_cidefineCustomElement('global-timeline', (templateText) =>
201cb0ef41Sopenharmony_ci class GlobalTimeline extends HTMLElement {
211cb0ef41Sopenharmony_ci  constructor() {
221cb0ef41Sopenharmony_ci    super();
231cb0ef41Sopenharmony_ci    const shadowRoot = this.attachShadow({mode: 'open'});
241cb0ef41Sopenharmony_ci    shadowRoot.innerHTML = templateText;
251cb0ef41Sopenharmony_ci  }
261cb0ef41Sopenharmony_ci
271cb0ef41Sopenharmony_ci  $(id) {
281cb0ef41Sopenharmony_ci    return this.shadowRoot.querySelector(id);
291cb0ef41Sopenharmony_ci  }
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_ci  set data(value) {
321cb0ef41Sopenharmony_ci    this._data = value;
331cb0ef41Sopenharmony_ci    this.stateChanged();
341cb0ef41Sopenharmony_ci  }
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_ci  get data() {
371cb0ef41Sopenharmony_ci    return this._data;
381cb0ef41Sopenharmony_ci  }
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ci  set selection(value) {
411cb0ef41Sopenharmony_ci    this._selection = value;
421cb0ef41Sopenharmony_ci    this.stateChanged();
431cb0ef41Sopenharmony_ci  }
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ci  get selection() {
461cb0ef41Sopenharmony_ci    return this._selection;
471cb0ef41Sopenharmony_ci  }
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ci  isValid() {
501cb0ef41Sopenharmony_ci    return this.data && this.selection;
511cb0ef41Sopenharmony_ci  }
521cb0ef41Sopenharmony_ci
531cb0ef41Sopenharmony_ci  hide() {
541cb0ef41Sopenharmony_ci    this.$('#container').style.display = 'none';
551cb0ef41Sopenharmony_ci  }
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci  show() {
581cb0ef41Sopenharmony_ci    this.$('#container').style.display = 'block';
591cb0ef41Sopenharmony_ci  }
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ci  stateChanged() {
621cb0ef41Sopenharmony_ci    if (this.isValid()) {
631cb0ef41Sopenharmony_ci      const isolate_data = this.data[this.selection.isolate];
641cb0ef41Sopenharmony_ci      const peakAllocatedMemory = isolate_data.peakAllocatedMemory;
651cb0ef41Sopenharmony_ci      this.$('#peak-memory-label').innerText = formatBytes(peakAllocatedMemory);
661cb0ef41Sopenharmony_ci      this.drawChart();
671cb0ef41Sopenharmony_ci    } else {
681cb0ef41Sopenharmony_ci      this.hide();
691cb0ef41Sopenharmony_ci    }
701cb0ef41Sopenharmony_ci  }
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ci  getZoneLabels(zone_names) {
731cb0ef41Sopenharmony_ci    switch (this.selection.data_kind) {
741cb0ef41Sopenharmony_ci      case KIND_ALLOCATED_MEMORY:
751cb0ef41Sopenharmony_ci        return zone_names.map(name => {
761cb0ef41Sopenharmony_ci          return {label: name + " (allocated)", type: 'number'};
771cb0ef41Sopenharmony_ci        });
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ci      case KIND_USED_MEMORY:
801cb0ef41Sopenharmony_ci        return zone_names.map(name => {
811cb0ef41Sopenharmony_ci          return {label: name + " (used)", type: 'number'};
821cb0ef41Sopenharmony_ci        });
831cb0ef41Sopenharmony_ci
841cb0ef41Sopenharmony_ci        case KIND_FREED_MEMORY:
851cb0ef41Sopenharmony_ci          return zone_names.map(name => {
861cb0ef41Sopenharmony_ci            return {label: name + " (freed)", type: 'number'};
871cb0ef41Sopenharmony_ci          });
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci        default:
901cb0ef41Sopenharmony_ci        // Don't show detailed per-zone information.
911cb0ef41Sopenharmony_ci        return [];
921cb0ef41Sopenharmony_ci    }
931cb0ef41Sopenharmony_ci  }
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ci  getTotalsData() {
961cb0ef41Sopenharmony_ci    const isolate_data = this.data[this.selection.isolate];
971cb0ef41Sopenharmony_ci    const labels = [
981cb0ef41Sopenharmony_ci      { label: "Time", type: "number" },
991cb0ef41Sopenharmony_ci      { label: "Total allocated", type: "number" },
1001cb0ef41Sopenharmony_ci      { label: "Total used", type: "number" },
1011cb0ef41Sopenharmony_ci      { label: "Total freed", type: "number" },
1021cb0ef41Sopenharmony_ci    ];
1031cb0ef41Sopenharmony_ci    const chart_data = [labels];
1041cb0ef41Sopenharmony_ci
1051cb0ef41Sopenharmony_ci    const timeStart = this.selection.timeStart;
1061cb0ef41Sopenharmony_ci    const timeEnd = this.selection.timeEnd;
1071cb0ef41Sopenharmony_ci    const filter_entries = timeStart > 0 || timeEnd > 0;
1081cb0ef41Sopenharmony_ci
1091cb0ef41Sopenharmony_ci    for (const [time, zone_data] of isolate_data.samples) {
1101cb0ef41Sopenharmony_ci      if (filter_entries && (time < timeStart || time > timeEnd)) continue;
1111cb0ef41Sopenharmony_ci      const data = [];
1121cb0ef41Sopenharmony_ci      data.push(time * kMillis2Seconds);
1131cb0ef41Sopenharmony_ci      data.push(zone_data.allocated / KB);
1141cb0ef41Sopenharmony_ci      data.push(zone_data.used / KB);
1151cb0ef41Sopenharmony_ci      data.push(zone_data.freed / KB);
1161cb0ef41Sopenharmony_ci      chart_data.push(data);
1171cb0ef41Sopenharmony_ci    }
1181cb0ef41Sopenharmony_ci    return chart_data;
1191cb0ef41Sopenharmony_ci  }
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ci  getZoneData() {
1221cb0ef41Sopenharmony_ci    const isolate_data = this.data[this.selection.isolate];
1231cb0ef41Sopenharmony_ci    const selected_zones = this.selection.zones;
1241cb0ef41Sopenharmony_ci    const zone_names = isolate_data.sorted_zone_names.filter(
1251cb0ef41Sopenharmony_ci        zone_name => selected_zones.has(zone_name));
1261cb0ef41Sopenharmony_ci    const data_kind = this.selection.data_kind;
1271cb0ef41Sopenharmony_ci    const show_totals = this.selection.show_totals;
1281cb0ef41Sopenharmony_ci    const zones_labels = this.getZoneLabels(zone_names);
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_ci    const totals_labels = show_totals
1311cb0ef41Sopenharmony_ci        ? [
1321cb0ef41Sopenharmony_ci            { label: "Total allocated", type: "number" },
1331cb0ef41Sopenharmony_ci            { label: "Total used", type: "number" },
1341cb0ef41Sopenharmony_ci            { label: "Total freed", type: "number" },
1351cb0ef41Sopenharmony_ci          ]
1361cb0ef41Sopenharmony_ci        : [];
1371cb0ef41Sopenharmony_ci
1381cb0ef41Sopenharmony_ci    const labels = [
1391cb0ef41Sopenharmony_ci      { label: "Time", type: "number" },
1401cb0ef41Sopenharmony_ci      ...totals_labels,
1411cb0ef41Sopenharmony_ci      ...zones_labels,
1421cb0ef41Sopenharmony_ci    ];
1431cb0ef41Sopenharmony_ci    const chart_data = [labels];
1441cb0ef41Sopenharmony_ci
1451cb0ef41Sopenharmony_ci    const timeStart = this.selection.timeStart;
1461cb0ef41Sopenharmony_ci    const timeEnd = this.selection.timeEnd;
1471cb0ef41Sopenharmony_ci    const filter_entries = timeStart > 0 || timeEnd > 0;
1481cb0ef41Sopenharmony_ci
1491cb0ef41Sopenharmony_ci    for (const [time, zone_data] of isolate_data.samples) {
1501cb0ef41Sopenharmony_ci      if (filter_entries && (time < timeStart || time > timeEnd)) continue;
1511cb0ef41Sopenharmony_ci      const active_zone_stats = Object.create(null);
1521cb0ef41Sopenharmony_ci      if (zone_data.zones !== undefined) {
1531cb0ef41Sopenharmony_ci        for (const [zone_name, zone_stats] of zone_data.zones) {
1541cb0ef41Sopenharmony_ci          if (!selected_zones.has(zone_name)) continue;  // Not selected, skip.
1551cb0ef41Sopenharmony_ci
1561cb0ef41Sopenharmony_ci          const current_stats = active_zone_stats[zone_name];
1571cb0ef41Sopenharmony_ci          if (current_stats === undefined) {
1581cb0ef41Sopenharmony_ci            active_zone_stats[zone_name] =
1591cb0ef41Sopenharmony_ci                { allocated: zone_stats.allocated,
1601cb0ef41Sopenharmony_ci                  used: zone_stats.used,
1611cb0ef41Sopenharmony_ci                  freed: zone_stats.freed,
1621cb0ef41Sopenharmony_ci                };
1631cb0ef41Sopenharmony_ci          } else {
1641cb0ef41Sopenharmony_ci            // We've got two zones with the same name.
1651cb0ef41Sopenharmony_ci            console.log("=== Duplicate zone names: " + zone_name);
1661cb0ef41Sopenharmony_ci            // Sum stats.
1671cb0ef41Sopenharmony_ci            current_stats.allocated += zone_stats.allocated;
1681cb0ef41Sopenharmony_ci            current_stats.used += zone_stats.used;
1691cb0ef41Sopenharmony_ci            current_stats.freed += zone_stats.freed;
1701cb0ef41Sopenharmony_ci          }
1711cb0ef41Sopenharmony_ci        }
1721cb0ef41Sopenharmony_ci      }
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci      const data = [];
1751cb0ef41Sopenharmony_ci      data.push(time * kMillis2Seconds);
1761cb0ef41Sopenharmony_ci      if (show_totals) {
1771cb0ef41Sopenharmony_ci        data.push(zone_data.allocated / KB);
1781cb0ef41Sopenharmony_ci        data.push(zone_data.used / KB);
1791cb0ef41Sopenharmony_ci        data.push(zone_data.freed / KB);
1801cb0ef41Sopenharmony_ci      }
1811cb0ef41Sopenharmony_ci
1821cb0ef41Sopenharmony_ci      zone_names.forEach(zone => {
1831cb0ef41Sopenharmony_ci        const sample = active_zone_stats[zone];
1841cb0ef41Sopenharmony_ci        let value = null;
1851cb0ef41Sopenharmony_ci        if (sample !== undefined) {
1861cb0ef41Sopenharmony_ci          if (data_kind == KIND_ALLOCATED_MEMORY) {
1871cb0ef41Sopenharmony_ci            value = sample.allocated / KB;
1881cb0ef41Sopenharmony_ci          } else if (data_kind == KIND_FREED_MEMORY) {
1891cb0ef41Sopenharmony_ci            value = sample.freed / KB;
1901cb0ef41Sopenharmony_ci          } else {
1911cb0ef41Sopenharmony_ci            // KIND_USED_MEMORY
1921cb0ef41Sopenharmony_ci            value = sample.used / KB;
1931cb0ef41Sopenharmony_ci          }
1941cb0ef41Sopenharmony_ci        }
1951cb0ef41Sopenharmony_ci        data.push(value);
1961cb0ef41Sopenharmony_ci      });
1971cb0ef41Sopenharmony_ci      chart_data.push(data);
1981cb0ef41Sopenharmony_ci    }
1991cb0ef41Sopenharmony_ci    return chart_data;
2001cb0ef41Sopenharmony_ci  }
2011cb0ef41Sopenharmony_ci
2021cb0ef41Sopenharmony_ci  getCategoryData() {
2031cb0ef41Sopenharmony_ci    const isolate_data = this.data[this.selection.isolate];
2041cb0ef41Sopenharmony_ci    const categories = Object.keys(this.selection.categories);
2051cb0ef41Sopenharmony_ci    const categories_names =
2061cb0ef41Sopenharmony_ci        categories.map(k => this.selection.category_names.get(k));
2071cb0ef41Sopenharmony_ci    const selected_zones = this.selection.zones;
2081cb0ef41Sopenharmony_ci    const data_kind = this.selection.data_kind;
2091cb0ef41Sopenharmony_ci    const show_totals = this.selection.show_totals;
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci    const categories_labels = this.getZoneLabels(categories_names);
2121cb0ef41Sopenharmony_ci
2131cb0ef41Sopenharmony_ci    const totals_labels = show_totals
2141cb0ef41Sopenharmony_ci        ? [
2151cb0ef41Sopenharmony_ci            { label: "Total allocated", type: "number" },
2161cb0ef41Sopenharmony_ci            { label: "Total used", type: "number" },
2171cb0ef41Sopenharmony_ci            { label: "Total freed", type: "number" },
2181cb0ef41Sopenharmony_ci          ]
2191cb0ef41Sopenharmony_ci        : [];
2201cb0ef41Sopenharmony_ci
2211cb0ef41Sopenharmony_ci    const labels = [
2221cb0ef41Sopenharmony_ci      { label: "Time", type: "number" },
2231cb0ef41Sopenharmony_ci      ...totals_labels,
2241cb0ef41Sopenharmony_ci      ...categories_labels,
2251cb0ef41Sopenharmony_ci    ];
2261cb0ef41Sopenharmony_ci    const chart_data = [labels];
2271cb0ef41Sopenharmony_ci
2281cb0ef41Sopenharmony_ci    const timeStart = this.selection.timeStart;
2291cb0ef41Sopenharmony_ci    const timeEnd = this.selection.timeEnd;
2301cb0ef41Sopenharmony_ci    const filter_entries = timeStart > 0 || timeEnd > 0;
2311cb0ef41Sopenharmony_ci
2321cb0ef41Sopenharmony_ci    for (const [time, zone_data] of isolate_data.samples) {
2331cb0ef41Sopenharmony_ci      if (filter_entries && (time < timeStart || time > timeEnd)) continue;
2341cb0ef41Sopenharmony_ci      const active_category_stats = Object.create(null);
2351cb0ef41Sopenharmony_ci      if (zone_data.zones !== undefined) {
2361cb0ef41Sopenharmony_ci        for (const [zone_name, zone_stats] of zone_data.zones) {
2371cb0ef41Sopenharmony_ci          const category = selected_zones.get(zone_name);
2381cb0ef41Sopenharmony_ci          if (category === undefined) continue;  // Zone was not selected.
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_ci          const current_stats = active_category_stats[category];
2411cb0ef41Sopenharmony_ci          if (current_stats === undefined) {
2421cb0ef41Sopenharmony_ci            active_category_stats[category] =
2431cb0ef41Sopenharmony_ci                { allocated: zone_stats.allocated,
2441cb0ef41Sopenharmony_ci                  used: zone_stats.used,
2451cb0ef41Sopenharmony_ci                  freed: zone_stats.freed,
2461cb0ef41Sopenharmony_ci                };
2471cb0ef41Sopenharmony_ci          } else {
2481cb0ef41Sopenharmony_ci            // Sum stats.
2491cb0ef41Sopenharmony_ci            current_stats.allocated += zone_stats.allocated;
2501cb0ef41Sopenharmony_ci            current_stats.used += zone_stats.used;
2511cb0ef41Sopenharmony_ci            current_stats.freed += zone_stats.freed;
2521cb0ef41Sopenharmony_ci          }
2531cb0ef41Sopenharmony_ci        }
2541cb0ef41Sopenharmony_ci      }
2551cb0ef41Sopenharmony_ci
2561cb0ef41Sopenharmony_ci      const data = [];
2571cb0ef41Sopenharmony_ci      data.push(time * kMillis2Seconds);
2581cb0ef41Sopenharmony_ci      if (show_totals) {
2591cb0ef41Sopenharmony_ci        data.push(zone_data.allocated / KB);
2601cb0ef41Sopenharmony_ci        data.push(zone_data.used / KB);
2611cb0ef41Sopenharmony_ci        data.push(zone_data.freed / KB);
2621cb0ef41Sopenharmony_ci      }
2631cb0ef41Sopenharmony_ci
2641cb0ef41Sopenharmony_ci      categories.forEach(category => {
2651cb0ef41Sopenharmony_ci        const sample = active_category_stats[category];
2661cb0ef41Sopenharmony_ci        let value = null;
2671cb0ef41Sopenharmony_ci        if (sample !== undefined) {
2681cb0ef41Sopenharmony_ci          if (data_kind == KIND_ALLOCATED_MEMORY) {
2691cb0ef41Sopenharmony_ci            value = sample.allocated / KB;
2701cb0ef41Sopenharmony_ci          } else if (data_kind == KIND_FREED_MEMORY) {
2711cb0ef41Sopenharmony_ci            value = sample.freed / KB;
2721cb0ef41Sopenharmony_ci          } else {
2731cb0ef41Sopenharmony_ci            // KIND_USED_MEMORY
2741cb0ef41Sopenharmony_ci            value = sample.used / KB;
2751cb0ef41Sopenharmony_ci          }
2761cb0ef41Sopenharmony_ci        }
2771cb0ef41Sopenharmony_ci        data.push(value);
2781cb0ef41Sopenharmony_ci      });
2791cb0ef41Sopenharmony_ci      chart_data.push(data);
2801cb0ef41Sopenharmony_ci    }
2811cb0ef41Sopenharmony_ci    return chart_data;
2821cb0ef41Sopenharmony_ci  }
2831cb0ef41Sopenharmony_ci
2841cb0ef41Sopenharmony_ci  getChartData() {
2851cb0ef41Sopenharmony_ci    switch (this.selection.data_view) {
2861cb0ef41Sopenharmony_ci      case VIEW_BY_ZONE_NAME:
2871cb0ef41Sopenharmony_ci        return this.getZoneData();
2881cb0ef41Sopenharmony_ci      case VIEW_BY_ZONE_CATEGORY:
2891cb0ef41Sopenharmony_ci        return this.getCategoryData();
2901cb0ef41Sopenharmony_ci      case VIEW_TOTALS:
2911cb0ef41Sopenharmony_ci      default:
2921cb0ef41Sopenharmony_ci        return this.getTotalsData();
2931cb0ef41Sopenharmony_ci      }
2941cb0ef41Sopenharmony_ci  }
2951cb0ef41Sopenharmony_ci
2961cb0ef41Sopenharmony_ci  getChartOptions() {
2971cb0ef41Sopenharmony_ci    const options = {
2981cb0ef41Sopenharmony_ci      isStacked: true,
2991cb0ef41Sopenharmony_ci      interpolateNulls: true,
3001cb0ef41Sopenharmony_ci      hAxis: {
3011cb0ef41Sopenharmony_ci        format: '###.##s',
3021cb0ef41Sopenharmony_ci        title: 'Time [s]',
3031cb0ef41Sopenharmony_ci      },
3041cb0ef41Sopenharmony_ci      vAxis: {
3051cb0ef41Sopenharmony_ci        format: '#,###KB',
3061cb0ef41Sopenharmony_ci        title: 'Memory consumption [KBytes]'
3071cb0ef41Sopenharmony_ci      },
3081cb0ef41Sopenharmony_ci      chartArea: {left:100, width: '85%', height: '70%'},
3091cb0ef41Sopenharmony_ci      legend: {position: 'top', maxLines: '1'},
3101cb0ef41Sopenharmony_ci      pointsVisible: true,
3111cb0ef41Sopenharmony_ci      pointSize: 3,
3121cb0ef41Sopenharmony_ci      explorer: {},
3131cb0ef41Sopenharmony_ci    };
3141cb0ef41Sopenharmony_ci
3151cb0ef41Sopenharmony_ci    // Overlay total allocated/used points on top of the graph.
3161cb0ef41Sopenharmony_ci    const series = {}
3171cb0ef41Sopenharmony_ci    if (this.selection.data_view == VIEW_TOTALS) {
3181cb0ef41Sopenharmony_ci      series[0] = {type: 'line', color: "red"};
3191cb0ef41Sopenharmony_ci      series[1] = {type: 'line', color: "blue"};
3201cb0ef41Sopenharmony_ci      series[2] = {type: 'line', color: "orange"};
3211cb0ef41Sopenharmony_ci    } else if (this.selection.show_totals) {
3221cb0ef41Sopenharmony_ci      series[0] = {type: 'line', color: "red", lineDashStyle: [13, 13]};
3231cb0ef41Sopenharmony_ci      series[1] = {type: 'line', color: "blue", lineDashStyle: [13, 13]};
3241cb0ef41Sopenharmony_ci      series[2] = {type: 'line', color: "orange", lineDashStyle: [13, 13]};
3251cb0ef41Sopenharmony_ci    }
3261cb0ef41Sopenharmony_ci    return Object.assign(options, {series: series});
3271cb0ef41Sopenharmony_ci  }
3281cb0ef41Sopenharmony_ci
3291cb0ef41Sopenharmony_ci  drawChart() {
3301cb0ef41Sopenharmony_ci    console.assert(this.data, 'invalid data');
3311cb0ef41Sopenharmony_ci    console.assert(this.selection, 'invalid selection');
3321cb0ef41Sopenharmony_ci
3331cb0ef41Sopenharmony_ci    const chart_data = this.getChartData();
3341cb0ef41Sopenharmony_ci
3351cb0ef41Sopenharmony_ci    const data = google.visualization.arrayToDataTable(chart_data);
3361cb0ef41Sopenharmony_ci    const options = this.getChartOptions();
3371cb0ef41Sopenharmony_ci    const chart = new google.visualization.AreaChart(this.$('#chart'));
3381cb0ef41Sopenharmony_ci    this.show();
3391cb0ef41Sopenharmony_ci    chart.draw(data, google.charts.Line.convertOptions(options));
3401cb0ef41Sopenharmony_ci  }
3411cb0ef41Sopenharmony_ci});
342