257b0a43ac
Design doc: https://docs.google.com/document/d/1rxM3sDd-ZiOLznqw7MvYraulAPWJSVqC_CztO4YpUTQ/edit Change-Id: I471ff31f32b7bdd22cb03005c1dcc18aa485ad77 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3313793 Auto-Submit: Jianxiao Lu <jianxiao.lu@intel.com> Reviewed-by: Camillo Bruni <cbruni@chromium.org> Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> Cr-Commit-Position: refs/heads/main@{#78428}
267 lines
7.1 KiB
JavaScript
267 lines
7.1 KiB
JavaScript
// Copyright 2021 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.
|
|
|
|
import {MB} from '../js/helper.mjs';
|
|
import {DOM} from '../js/web-api-helper.mjs';
|
|
|
|
import {getColorFromSpaceName, kSpaceNames} from './space-categories.mjs';
|
|
|
|
class TrendLineHelper {
|
|
static re_gc_count = /(?<=(Before|After) GC:)\d+(?=,)/;
|
|
static re_allocated = /allocated/;
|
|
static re_space_name = /^[a-z_]+_space/;
|
|
|
|
static snapshotHeaderToXLabel(header) {
|
|
const gc_count = this.re_gc_count.exec(header)[0];
|
|
const alpha = header[0];
|
|
return alpha + gc_count;
|
|
}
|
|
|
|
static getLineSymbolFromTrendLineName(trend_line_name) {
|
|
const is_allocated_line = this.re_allocated.test(trend_line_name);
|
|
if (is_allocated_line) {
|
|
return 'emptyTriangle';
|
|
}
|
|
return 'emptyCircle';
|
|
}
|
|
|
|
static getSizeTrendLineName(space_name) {
|
|
return space_name + ' size';
|
|
}
|
|
|
|
static getAllocatedTrendSizeName(space_name) {
|
|
return space_name + ' allocated';
|
|
}
|
|
|
|
static getSpaceNameFromTrendLineName(trend_line_name) {
|
|
const space_name = this.re_space_name.exec(trend_line_name)[0];
|
|
return space_name;
|
|
}
|
|
}
|
|
|
|
DOM.defineCustomElement('heap-size-trend-viewer',
|
|
(templateText) =>
|
|
class HeapSizeTrendViewer extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
const shadowRoot = this.attachShadow({mode: 'open'});
|
|
shadowRoot.innerHTML = templateText;
|
|
this.chart = echarts.init(this.$('#chart'), null, {
|
|
renderer: 'canvas',
|
|
});
|
|
this.chart.getZr().on('click', 'series.line', (params) => {
|
|
const pointInPixel = [params.offsetX, params.offsetY];
|
|
const pointInGrid =
|
|
this.chart.convertFromPixel({seriesIndex: 0}, pointInPixel);
|
|
const xIndex = pointInGrid[0];
|
|
this.dispatchEvent(new CustomEvent('change', {
|
|
bubbles: true,
|
|
composed: true,
|
|
detail: xIndex,
|
|
}));
|
|
this.setXMarkLine(xIndex);
|
|
});
|
|
this.chartXAxisData = null;
|
|
this.chartSeriesData = null;
|
|
this.currentIndex = 0;
|
|
window.addEventListener('resize', () => {
|
|
this.chart.resize();
|
|
});
|
|
}
|
|
|
|
$(id) {
|
|
return this.shadowRoot.querySelector(id);
|
|
}
|
|
|
|
set data(value) {
|
|
this._data = value;
|
|
this.stateChanged();
|
|
}
|
|
|
|
get data() {
|
|
return this._data;
|
|
}
|
|
|
|
hide() {
|
|
this.$('#container').style.display = 'none';
|
|
}
|
|
|
|
show() {
|
|
this.$('#container').style.display = 'block';
|
|
}
|
|
|
|
stateChanged() {
|
|
this.initTrendLineNames();
|
|
this.initXAxisDataAndSeries();
|
|
this.drawChart();
|
|
}
|
|
|
|
initTrendLineNames() {
|
|
this.trend_line_names = [];
|
|
for (const space_name of kSpaceNames) {
|
|
this.trend_line_names.push(
|
|
TrendLineHelper.getSizeTrendLineName(space_name));
|
|
this.trend_line_names.push(
|
|
TrendLineHelper.getAllocatedTrendSizeName(space_name));
|
|
}
|
|
}
|
|
|
|
// X axis represent the moment before or after nth GC : [B1,A1,...Bn,An].
|
|
initXAxisDataAndSeries() {
|
|
this.chartXAxisData = [];
|
|
this.chartSeriesData = [];
|
|
let trend_line_name_data_dict = {};
|
|
|
|
for (const trend_line_name of this.trend_line_names) {
|
|
trend_line_name_data_dict[trend_line_name] = [];
|
|
}
|
|
|
|
// Init x axis data and trend line series.
|
|
for (const snapshot of this.data) {
|
|
this.chartXAxisData.push(
|
|
TrendLineHelper.snapshotHeaderToXLabel(snapshot.header));
|
|
for (const [space_name, pageinfos] of Object.entries(snapshot.data)) {
|
|
const size_trend_line_name =
|
|
TrendLineHelper.getSizeTrendLineName(space_name);
|
|
const allocated_trend_line_name =
|
|
TrendLineHelper.getAllocatedTrendSizeName(space_name);
|
|
let size_sum = 0;
|
|
let allocated_sum = 0;
|
|
for (const pageinfo of pageinfos) {
|
|
size_sum += pageinfo[2] - pageinfo[1];
|
|
allocated_sum += pageinfo[3];
|
|
}
|
|
trend_line_name_data_dict[size_trend_line_name].push(size_sum);
|
|
trend_line_name_data_dict[allocated_trend_line_name].push(
|
|
allocated_sum);
|
|
}
|
|
}
|
|
|
|
// Init mark line series as the first series
|
|
const markline_series = {
|
|
name: 'mark-line',
|
|
type: 'line',
|
|
|
|
markLine: {
|
|
silent: true,
|
|
symbol: 'none',
|
|
label: {
|
|
show: false,
|
|
},
|
|
lineStyle: {
|
|
color: '#333',
|
|
},
|
|
data: [
|
|
{
|
|
xAxis: 0,
|
|
},
|
|
],
|
|
},
|
|
};
|
|
this.chartSeriesData.push(markline_series);
|
|
|
|
for (const [trend_line_name, trend_line_data] of Object.entries(
|
|
trend_line_name_data_dict)) {
|
|
const color = getColorFromSpaceName(
|
|
TrendLineHelper.getSpaceNameFromTrendLineName(trend_line_name));
|
|
const trend_line_series = {
|
|
name: trend_line_name,
|
|
type: 'line',
|
|
data: trend_line_data,
|
|
lineStyle: {
|
|
color: color,
|
|
},
|
|
itemStyle: {
|
|
color: color,
|
|
},
|
|
symbol: TrendLineHelper.getLineSymbolFromTrendLineName(trend_line_name),
|
|
symbolSize: 8,
|
|
};
|
|
this.chartSeriesData.push(trend_line_series);
|
|
}
|
|
}
|
|
|
|
setXMarkLine(index) {
|
|
if (index < 0 || index >= this.data.length) {
|
|
console.error('Invalid index:', index);
|
|
return;
|
|
}
|
|
// Set the mark-line series
|
|
this.chartSeriesData[0].markLine.data[0].xAxis = index;
|
|
this.chart.setOption({
|
|
series: this.chartSeriesData,
|
|
});
|
|
this.currentIndex = index;
|
|
}
|
|
|
|
drawChart() {
|
|
const option = {
|
|
dataZoom: [
|
|
{
|
|
type: 'inside',
|
|
filterMode: 'weakFilter',
|
|
},
|
|
{
|
|
type: 'slider',
|
|
filterMode: 'weakFilter',
|
|
labelFormatter: '',
|
|
},
|
|
],
|
|
title: {
|
|
text: 'Size Trend',
|
|
left: 'center',
|
|
},
|
|
tooltip: {
|
|
trigger: 'axis',
|
|
position(point, params, dom, rect, size) {
|
|
let ret_x = point[0] + 10;
|
|
if (point[0] > size.viewSize[0] * 0.7) {
|
|
ret_x = point[0] - dom.clientWidth - 10;
|
|
}
|
|
return [ret_x, '85%'];
|
|
},
|
|
formatter(params) {
|
|
const colorSpan = (color) =>
|
|
'<span style="display:inline-block;margin-right:1px;border-radius:5px;width:9px;height:9px;background-color:' +
|
|
color + '"></span>';
|
|
let result = '<p>' + params[0].axisValue + '</p>';
|
|
params.forEach((item) => {
|
|
const xx = '<p style="margin:0;">' + colorSpan(item.color) + ' ' +
|
|
item.seriesName + ': ' + (item.data / MB).toFixed(2) + 'MB' +
|
|
'</p>';
|
|
result += xx;
|
|
});
|
|
|
|
return result;
|
|
},
|
|
},
|
|
legend: {
|
|
data: this.trend_line_names,
|
|
top: '6%',
|
|
type: 'scroll',
|
|
},
|
|
|
|
xAxis: {
|
|
minInterval: 1,
|
|
type: 'category',
|
|
boundaryGap: false,
|
|
data: this.chartXAxisData,
|
|
},
|
|
yAxis: {
|
|
type: 'value',
|
|
axisLabel: {
|
|
formatter(value, index) {
|
|
return (value / MB).toFixed(3) + 'MB';
|
|
},
|
|
},
|
|
},
|
|
|
|
series: this.chartSeriesData,
|
|
};
|
|
this.show();
|
|
this.chart.resize();
|
|
this.chart.setOption(option);
|
|
}
|
|
});
|