summaryrefslogtreecommitdiffstats
path: root/chromium/v8/tools/zone-stats/global-timeline.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/v8/tools/zone-stats/global-timeline.js')
-rw-r--r--chromium/v8/tools/zone-stats/global-timeline.js323
1 files changed, 323 insertions, 0 deletions
diff --git a/chromium/v8/tools/zone-stats/global-timeline.js b/chromium/v8/tools/zone-stats/global-timeline.js
new file mode 100644
index 00000000000..ea1793101b4
--- /dev/null
+++ b/chromium/v8/tools/zone-stats/global-timeline.js
@@ -0,0 +1,323 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+import {categoryByZoneName} from './categories.js';
+
+import {
+ VIEW_TOTALS,
+ VIEW_BY_ZONE_NAME,
+ VIEW_BY_ZONE_CATEGORY,
+
+ KIND_ALLOCATED_MEMORY,
+ KIND_USED_MEMORY,
+} from './details-selection.js';
+
+defineCustomElement('global-timeline', (templateText) =>
+ class GlobalTimeline extends HTMLElement {
+ constructor() {
+ super();
+ const shadowRoot = this.attachShadow({mode: 'open'});
+ shadowRoot.innerHTML = templateText;
+ }
+
+ $(id) {
+ return this.shadowRoot.querySelector(id);
+ }
+
+ set data(value) {
+ this._data = value;
+ this.stateChanged();
+ }
+
+ get data() {
+ return this._data;
+ }
+
+ set selection(value) {
+ this._selection = value;
+ this.stateChanged();
+ }
+
+ get selection() {
+ return this._selection;
+ }
+
+ isValid() {
+ return this.data && this.selection;
+ }
+
+ hide() {
+ this.$('#container').style.display = 'none';
+ }
+
+ show() {
+ this.$('#container').style.display = 'block';
+ }
+
+ stateChanged() {
+ if (this.isValid()) {
+ const isolate_data = this.data[this.selection.isolate];
+ const peakAllocatedMemory = isolate_data.peakAllocatedMemory;
+ this.$('#peak-memory-label').innerText = formatBytes(peakAllocatedMemory);
+ this.drawChart();
+ } else {
+ this.hide();
+ }
+ }
+
+ getZoneLabels(zone_names) {
+ switch (this.selection.data_kind) {
+ case KIND_ALLOCATED_MEMORY:
+ return zone_names.map(name => {
+ return {label: name + " (allocated)", type: 'number'};
+ });
+
+ case KIND_USED_MEMORY:
+ return zone_names.map(name => {
+ return {label: name + " (used)", type: 'number'};
+ });
+
+ default:
+ // Don't show detailed per-zone information.
+ return [];
+ }
+ }
+
+ getTotalsData() {
+ const isolate_data = this.data[this.selection.isolate];
+ const labels = [
+ { label: "Time", type: "number" },
+ { label: "Total allocated", type: "number" },
+ { label: "Total used", type: "number" },
+ ];
+ const chart_data = [labels];
+
+ const timeStart = this.selection.timeStart;
+ const timeEnd = this.selection.timeEnd;
+ const filter_entries = timeStart > 0 || timeEnd > 0;
+
+ for (const [time, zone_data] of isolate_data.samples) {
+ if (filter_entries && (time < timeStart || time > timeEnd)) continue;
+ const data = [];
+ data.push(time * kMillis2Seconds);
+ data.push(zone_data.allocated / KB);
+ data.push(zone_data.used / KB);
+ chart_data.push(data);
+ }
+ return chart_data;
+ }
+
+ getZoneData() {
+ const isolate_data = this.data[this.selection.isolate];
+ const zone_names = isolate_data.sorted_zone_names;
+ const selected_zones = this.selection.zones;
+ const data_kind = this.selection.data_kind;
+ const show_totals = this.selection.show_totals;
+ const zones_labels = this.getZoneLabels(zone_names);
+
+ const totals_labels = show_totals
+ ? [
+ { label: "Total allocated", type: "number" },
+ { label: "Total used", type: "number" },
+ ]
+ : [];
+
+ const labels = [
+ { label: "Time", type: "number" },
+ ...totals_labels,
+ ...zones_labels,
+ ];
+ const chart_data = [labels];
+
+ const timeStart = this.selection.timeStart;
+ const timeEnd = this.selection.timeEnd;
+ const filter_entries = timeStart > 0 || timeEnd > 0;
+
+ for (const [time, zone_data] of isolate_data.samples) {
+ if (filter_entries && (time < timeStart || time > timeEnd)) continue;
+ const active_zone_stats = Object.create(null);
+ if (zone_data.zones !== undefined) {
+ for (const [zone_name, zone_stats] of zone_data.zones) {
+ if (!selected_zones.has(zone_name)) continue; // Not selected, skip.
+
+ const current_stats = active_zone_stats[zone_name];
+ if (current_stats === undefined) {
+ active_zone_stats[zone_name] =
+ { allocated: zone_stats.allocated, used: zone_stats.used };
+ } else {
+ // We've got two zones with the same name.
+ console.log("=== Duplicate zone names: " + zone_name);
+ // Sum stats.
+ current_stats.allocated += zone_stats.allocated;
+ current_stats.used += zone_stats.used;
+ }
+ }
+ }
+
+ const data = [];
+ data.push(time * kMillis2Seconds);
+ if (show_totals) {
+ data.push(zone_data.allocated / KB);
+ data.push(zone_data.used / KB);
+ }
+
+ if (zone_data.used > 30 * MB) {
+ console.log("BOOOM!!!! Zone usage in a sample is too big: " +
+ (zone_data.used / MB) + " MB");
+ }
+
+ zone_names.forEach(zone => {
+ const sample = active_zone_stats[zone];
+ let used = null;
+ let allocated = null;
+ if (sample !== undefined) {
+ used = sample.used / KB;
+ allocated = sample.allocated / KB;
+ }
+ if (data_kind == KIND_ALLOCATED_MEMORY) {
+ data.push(allocated);
+ } else {
+ // KIND_USED_MEMORY
+ data.push(used);
+ }
+ });
+ chart_data.push(data);
+ }
+ return chart_data;
+ }
+
+ getCategoryData() {
+ const isolate_data = this.data[this.selection.isolate];
+ const categories = Object.keys(this.selection.categories);
+ const categories_names =
+ categories.map(k => this.selection.category_names.get(k));
+ const selected_zones = this.selection.zones;
+ const data_kind = this.selection.data_kind;
+ const show_totals = this.selection.show_totals;
+
+ const categories_labels = this.getZoneLabels(categories_names);
+
+ const totals_labels = show_totals
+ ? [
+ { label: "Total allocated", type: "number" },
+ { label: "Total used", type: "number" },
+ ]
+ : [];
+
+ const labels = [
+ { label: "Time", type: "number" },
+ ...totals_labels,
+ ...categories_labels,
+ ];
+ const chart_data = [labels];
+
+ const timeStart = this.selection.timeStart;
+ const timeEnd = this.selection.timeEnd;
+ const filter_entries = timeStart > 0 || timeEnd > 0;
+
+ for (const [time, zone_data] of isolate_data.samples) {
+ if (filter_entries && (time < timeStart || time > timeEnd)) continue;
+ const active_category_stats = Object.create(null);
+ if (zone_data.zones !== undefined) {
+ for (const [zone_name, zone_stats] of zone_data.zones) {
+ const category = selected_zones.get(zone_name);
+ if (category === undefined) continue; // Zone was not selected.
+
+ const current_stats = active_category_stats[category];
+ if (current_stats === undefined) {
+ active_category_stats[category] =
+ { allocated: zone_stats.allocated, used: zone_stats.used };
+ } else {
+ // Sum stats.
+ current_stats.allocated += zone_stats.allocated;
+ current_stats.used += zone_stats.used;
+ }
+ }
+ }
+
+ const data = [];
+ data.push(time * kMillis2Seconds);
+ if (show_totals) {
+ data.push(zone_data.allocated / KB);
+ data.push(zone_data.used / KB);
+ }
+
+ categories.forEach(category => {
+ const sample = active_category_stats[category];
+ let used = null;
+ let allocated = null;
+ if (sample !== undefined) {
+ used = sample.used / KB;
+ allocated = sample.allocated / KB;
+ }
+ if (data_kind == KIND_ALLOCATED_MEMORY) {
+ data.push(allocated);
+ } else {
+ // KIND_USED_MEMORY
+ data.push(used);
+ }
+ });
+ chart_data.push(data);
+ }
+ return chart_data;
+ }
+
+ getChartData() {
+ switch (this.selection.data_view) {
+ case VIEW_BY_ZONE_NAME:
+ return this.getZoneData();
+ case VIEW_BY_ZONE_CATEGORY:
+ return this.getCategoryData();
+ case VIEW_TOTALS:
+ default:
+ return this.getTotalsData();
+ }
+ }
+
+ getChartOptions() {
+ const options = {
+ isStacked: true,
+ interpolateNulls: true,
+ hAxis: {
+ format: '###.##s',
+ title: 'Time [s]',
+ },
+ vAxis: {
+ format: '#,###KB',
+ title: 'Memory consumption [KBytes]'
+ },
+ chartArea: {left:100, width: '85%', height: '70%'},
+ legend: {position: 'top', maxLines: '1'},
+ pointsVisible: true,
+ pointSize: 3,
+ explorer: {},
+ };
+
+ // Overlay total allocated/used points on top of the graph.
+ const series = {}
+ if (this.selection.data_view == VIEW_TOTALS) {
+ series[0] = {type: 'line', color: "red"};
+ series[1] = {type: 'line', color: "blue"};
+ } else if (this.selection.show_totals) {
+ series[0] = {type: 'line', color: "red", lineDashStyle: [13, 13]};
+ series[1] = {type: 'line', color: "blue", lineDashStyle: [13, 13]};
+ }
+ return Object.assign(options, {series: series});
+ }
+
+ drawChart() {
+ console.assert(this.data, 'invalid data');
+ console.assert(this.selection, 'invalid selection');
+
+ const chart_data = this.getChartData();
+
+ const data = google.visualization.arrayToDataTable(chart_data);
+ const options = this.getChartOptions();
+ const chart = new google.visualization.AreaChart(this.$('#chart'));
+ this.show();
+ chart.draw(data, google.charts.Line.convertOptions(options));
+ }
+});