• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2021 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import {GB, MB} from '../js/helper.mjs';
6import {DOM} from '../js/web-api-helper.mjs';
7
8import {getColorFromSpaceName, kSpaceNames} from './space-categories.mjs';
9
10DOM.defineCustomElement('heap-layout-viewer',
11                        (templateText) =>
12                            class HeapLayoutViewer extends HTMLElement {
13  constructor() {
14    super();
15    const shadowRoot = this.attachShadow({mode: 'open'});
16    shadowRoot.innerHTML = templateText;
17    this.chart = echarts.init(this.$('#chart'), null, {
18      renderer: 'canvas',
19    });
20    window.addEventListener('resize', () => {
21      this.chart.resize();
22    });
23    this.currentIndex = 0;
24  }
25
26  $(id) {
27    return this.shadowRoot.querySelector(id);
28  }
29
30  set data(value) {
31    this._data = value;
32    this.stateChanged();
33  }
34
35  get data() {
36    return this._data;
37  }
38
39  hide() {
40    this.$('#container').style.display = 'none';
41  }
42
43  show() {
44    this.$('#container').style.display = 'block';
45  }
46
47  stateChanged() {
48    this.drawChart(0);
49  }
50
51  getChartTitle(index) {
52    return this.data[index].header;
53  }
54
55  getSeriesData(pageinfos) {
56    let ret = [];
57    for (let pageinfo of pageinfos) {
58      ret.push({value: pageinfo});
59    }
60    return ret;
61  }
62
63  getChartSeries(index) {
64    const snapshot = this.data[index];
65    let series = [];
66    for (const [space_name, pageinfos] of Object.entries(snapshot.data)) {
67      let space_series = {
68        name: space_name,
69        type: 'custom',
70        renderItem(params, api) {
71          const addressBegin = api.value(1);
72          const addressEnd = api.value(2);
73          const allocated = api.value(3);
74          const start = api.coord([addressBegin, 0]);
75          const end = api.coord([addressEnd, 0]);
76
77          const allocatedRate = allocated / (addressEnd - addressBegin);
78          const unAllocatedRate = 1 - allocatedRate;
79
80          const standardH = api.size([0, 1])[1];
81          const standardY = start[1] - standardH / 2;
82
83          const allocatedY = standardY + standardH * unAllocatedRate;
84          const allocatedH = standardH * allocatedRate;
85
86          const unAllocatedY = standardY;
87          const unAllocatedH = standardH - allocatedH;
88
89          const allocatedShape = echarts.graphic.clipRectByRect(
90              {
91                x: start[0],
92                y: allocatedY,
93                width: end[0] - start[0],
94                height: allocatedH,
95              },
96              {
97                x: params.coordSys.x,
98                y: params.coordSys.y,
99                width: params.coordSys.width,
100                height: params.coordSys.height,
101              });
102
103          const unAllocatedShape = echarts.graphic.clipRectByRect(
104              {
105                x: start[0],
106                y: unAllocatedY,
107                width: end[0] - start[0],
108                height: unAllocatedH,
109              },
110              {
111                x: params.coordSys.x,
112                y: params.coordSys.y,
113                width: params.coordSys.width,
114                height: params.coordSys.height,
115              });
116
117          const ret = {
118            type: 'group',
119            children: [
120              {
121                type: 'rect',
122                shape: allocatedShape,
123                style: api.style(),
124              },
125              {
126                type: 'rect',
127                shape: unAllocatedShape,
128                style: {
129                  fill: '#000000',
130                },
131              },
132            ],
133          };
134          return ret;
135        },
136        data: this.getSeriesData(pageinfos),
137        encode: {
138          x: [1, 2],
139        },
140        itemStyle: {
141          color: getColorFromSpaceName(space_name),
142        },
143      };
144      series.push(space_series);
145    }
146    return series;
147  }
148
149  drawChart(index) {
150    if (index >= this.data.length || index < 0) {
151      console.error('Invalid index:', index);
152      return;
153    }
154    const option = {
155      tooltip: {
156        formatter(params) {
157          const ret = params.marker + params.value[0] + '<br>' +
158              'address:' + (params.value[1] / MB).toFixed(3) + 'MB' +
159              '<br>' +
160              'size:' + ((params.value[2] - params.value[1]) / MB).toFixed(3) +
161              'MB' +
162              '<br>' +
163              'allocated:' + (params.value[3] / MB).toFixed(3) + 'MB' +
164              '<br>' +
165              'wasted:' + params.value[4] + 'B';
166          return ret;
167        },
168      },
169      grid: {
170        bottom: 120,
171        top: 120,
172      },
173      dataZoom: [
174        {
175          type: 'slider',
176          filterMode: 'weakFilter',
177          showDataShadow: true,
178          labelFormatter: '',
179        },
180        {
181          type: 'inside',
182          filterMode: 'weakFilter',
183        },
184      ],
185      legend: {
186        show: true,
187        data: kSpaceNames,
188        top: '6%',
189        type: 'scroll',
190      },
191      title: {
192        text: this.getChartTitle(index),
193        left: 'center',
194      },
195      xAxis: {
196        name: 'Address offset in heap(MB)',
197        nameLocation: 'center',
198        nameTextStyle: {
199          fontSize: 25,
200          padding: [30, 0, 50, 0],
201        },
202        type: 'value',
203        min: 0,
204        max: 4 * GB,
205        axisLabel: {
206          rotate: 0,
207          formatter(value, index) {
208            value = value / MB;
209            value = value.toFixed(3);
210            return value;
211          },
212        },
213      },
214      yAxis: {
215        data: ['Page'],
216      },
217      series: this.getChartSeries(index),
218    };
219
220    this.show();
221    this.chart.resize();
222    this.chart.setOption(option);
223    this.currentIndex = index;
224  }
225});
226