• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 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
5'use strict';
6
7import {
8  VIEW_BY_INSTANCE_TYPE,
9  VIEW_BY_INSTANCE_CATEGORY,
10  VIEW_BY_FIELD_TYPE
11} from './details-selection.js';
12
13defineCustomElement('global-timeline', (templateText) =>
14 class GlobalTimeline extends HTMLElement {
15  constructor() {
16    super();
17    const shadowRoot = this.attachShadow({mode: 'open'});
18    shadowRoot.innerHTML = templateText;
19  }
20
21  $(id) {
22    return this.shadowRoot.querySelector(id);
23  }
24
25  set data(value) {
26    this._data = value;
27    this.stateChanged();
28  }
29
30  get data() {
31    return this._data;
32  }
33
34  set selection(value) {
35    this._selection = value;
36    this.stateChanged();
37  }
38
39  get selection() {
40    return this._selection;
41  }
42
43  isValid() {
44    return this.data && this.selection;
45  }
46
47  hide() {
48    this.$('#container').style.display = 'none';
49  }
50
51  show() {
52    this.$('#container').style.display = 'block';
53  }
54
55  stateChanged() {
56    if (this.isValid()) {
57      this.drawChart();
58    } else {
59      this.hide();
60    }
61  }
62
63  getFieldData() {
64    const labels = [
65      {type: 'number', label: 'Time'},
66      {type: 'number', label: 'Ptr compression benefit'},
67      {type: 'string', role: 'tooltip'},
68      {type: 'number', label: 'Embedder fields'},
69      {type: 'number', label: 'Tagged fields (excl. in-object Smis)'},
70      {type: 'number', label: 'In-object Smi-only fields'},
71      {type: 'number', label: 'Other raw fields'},
72      {type: 'number', label: 'Unboxed doubles'},
73      {type: 'number', label: 'Boxed doubles'},
74      {type: 'number', label: 'String data'}
75    ];
76    const chart_data = [labels];
77    const isolate_data = this.data[this.selection.isolate];
78    let sum_total = 0;
79    let sum_ptr_compr_benefit_perc = 0;
80    let count = 0;
81    Object.keys(isolate_data.gcs).forEach(gc_key => {
82      const gc_data = isolate_data.gcs[gc_key];
83      const data_set = gc_data[this.selection.data_set].field_data;
84      const data = [];
85      data.push(gc_data.time * kMillis2Seconds);
86      const total = data_set.tagged_fields +
87                    data_set.inobject_smi_fields +
88                    data_set.embedder_fields +
89                    data_set.other_raw_fields +
90                    data_set.unboxed_double_fields +
91                    data_set.boxed_double_fields +
92                    data_set.string_data;
93      const ptr_compr_benefit =
94          (data_set.inobject_smi_fields + data_set.tagged_fields) / 2;
95      const ptr_compr_benefit_perc = ptr_compr_benefit / total * 100;
96      sum_total += total;
97      sum_ptr_compr_benefit_perc += ptr_compr_benefit_perc;
98      count++;
99      const tooltip = "Ptr compression benefit: " +
100                      (ptr_compr_benefit / KB).toFixed(2) + "KB " +
101                      " (" + ptr_compr_benefit_perc.toFixed(2) + "%)";
102      data.push(ptr_compr_benefit / KB);
103      data.push(tooltip);
104      data.push(data_set.embedder_fields / KB);
105      data.push(data_set.tagged_fields / KB);
106      data.push(data_set.inobject_smi_fields / KB);
107      data.push(data_set.other_raw_fields / KB);
108      data.push(data_set.unboxed_double_fields / KB);
109      data.push(data_set.boxed_double_fields / KB);
110      data.push(data_set.string_data / KB);
111      chart_data.push(data);
112    });
113    const avg_ptr_compr_benefit_perc =
114        count ? sum_ptr_compr_benefit_perc / count : 0;
115    console.log("==================================================");
116    console.log("= Average ptr compression benefit is " +
117                avg_ptr_compr_benefit_perc.toFixed(2) + "%");
118    console.log("= Average V8 heap size " +
119                (sum_total / count / KB).toFixed(2) + " KB");
120    console.log("==================================================");
121    return chart_data;
122  }
123
124  getCategoryData() {
125    const categories = Object.keys(this.selection.categories)
126                           .map(k => this.selection.category_names.get(k));
127    const labels = ['Time', ...categories];
128    const chart_data = [labels];
129    const isolate_data = this.data[this.selection.isolate];
130    Object.keys(isolate_data.gcs).forEach(gc_key => {
131      const gc_data = isolate_data.gcs[gc_key];
132      const data_set = gc_data[this.selection.data_set].instance_type_data;
133      const data = [];
134      data.push(gc_data.time * kMillis2Seconds);
135      Object.values(this.selection.categories).forEach(instance_types => {
136        data.push(
137            instance_types
138                .map(instance_type => {
139                  return data_set[instance_type].overall;
140                })
141                .reduce((accu, current) => accu + current, 0) /
142            KB);
143      });
144      chart_data.push(data);
145    });
146    return chart_data;
147  }
148
149  getInstanceTypeData() {
150    const instance_types =
151        Object.values(this.selection.categories)
152            .reduce((accu, current) => accu.concat(current), []);
153    const labels = ['Time', ...instance_types];
154    const chart_data = [labels];
155    const isolate_data = this.data[this.selection.isolate];
156    Object.keys(isolate_data.gcs).forEach(gc_key => {
157      const gc_data = isolate_data.gcs[gc_key];
158      const data_set = gc_data[this.selection.data_set].instance_type_data;
159      const data = [];
160      data.push(gc_data.time * kMillis2Seconds);
161      instance_types.forEach(instance_type => {
162        data.push(data_set[instance_type].overall / KB);
163      });
164      chart_data.push(data);
165    });
166    return chart_data;
167  }
168
169  getChartData() {
170    switch (this.selection.data_view) {
171      case VIEW_BY_FIELD_TYPE:
172        return this.getFieldData();
173      case VIEW_BY_INSTANCE_CATEGORY:
174        return this.getCategoryData();
175      case VIEW_BY_INSTANCE_TYPE:
176      default:
177        return this.getInstanceTypeData();
178    }
179  }
180
181  getChartOptions() {
182    const options = {
183      isStacked: true,
184      hAxis: {
185        format: '###.##s',
186        title: 'Time [s]',
187      },
188      vAxis: {
189        format: '#,###KB',
190        title: 'Memory consumption [KBytes]'
191      },
192      chartArea: {left:100, width: '85%', height: '70%'},
193      legend: {position: 'top', maxLines: '1'},
194      pointsVisible: true,
195      pointSize: 5,
196      explorer: {},
197    };
198    switch (this.selection.data_view) {
199      case VIEW_BY_FIELD_TYPE:
200        // Overlay pointer compression benefit on top of the graph
201        return Object.assign(options, {
202          series: {0: {type: 'line', lineDashStyle: [13, 13]}},
203        });
204      case VIEW_BY_INSTANCE_CATEGORY:
205      case VIEW_BY_INSTANCE_TYPE:
206      default:
207        return options;
208    }
209  }
210
211  drawChart() {
212    setTimeout(() => this._drawChart(), 10);
213  }
214
215  _drawChart() {
216    console.assert(this.data, 'invalid data');
217    console.assert(this.selection, 'invalid selection');
218
219    const chart_data = this.getChartData();
220
221    const data = google.visualization.arrayToDataTable(chart_data);
222    const options = this.getChartOptions();
223    const chart = new google.visualization.AreaChart(this.$('#chart'));
224    this.show();
225    chart.draw(data, google.charts.Line.convertOptions(options));
226  }
227});
228