• 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
7const histogram_viewer_template =
8    document.currentScript.ownerDocument.querySelector(
9        '#histogram-viewer-template');
10
11class HistogramViewer extends HTMLElement {
12  constructor() {
13    super();
14    const shadowRoot = this.attachShadow({mode: 'open'});
15    shadowRoot.appendChild(histogram_viewer_template.content.cloneNode(true));
16  }
17
18  $(id) {
19    return this.shadowRoot.querySelector(id);
20  }
21
22  set data(value) {
23    this._data = value;
24    this.stateChanged();
25  }
26
27  get data() {
28    return this._data;
29  }
30
31  set selection(value) {
32    this._selection = value;
33    this.stateChanged();
34  }
35
36  get selection() {
37    return this._selection;
38  }
39
40  isValid() {
41    return this.data && this.selection &&
42           (this.selection.data_view === VIEW_BY_INSTANCE_CATEGORY ||
43            this.selection.data_view === VIEW_BY_INSTANCE_TYPE);
44    ;
45  }
46
47  hide() {
48    this.$('#container').style.display = 'none';
49  }
50
51  show() {
52    this.$('#container').style.display = 'block';
53  }
54
55  getOverallValue() {
56    switch (this.selection.data_view) {
57      case VIEW_BY_FIELD_TYPE:
58        return NaN;
59      case VIEW_BY_INSTANCE_CATEGORY:
60        return this.getPropertyForCategory('overall');
61      case VIEW_BY_INSTANCE_TYPE:
62      default:
63        return this.getPropertyForInstanceTypes('overall');
64    }
65  }
66
67  stateChanged() {
68    if (this.isValid()) {
69      const overall_bytes = this.getOverallValue();
70      this.$('#overall').innerHTML = `Overall: ${overall_bytes / KB} KB`;
71      this.drawChart();
72    } else {
73      this.hide();
74    }
75  }
76
77  get selectedData() {
78    console.assert(this.data, 'invalid data');
79    console.assert(this.selection, 'invalid selection');
80    return this.data[this.selection.isolate]
81        .gcs[this.selection.gc][this.selection.data_set];
82  }
83
84  get selectedInstanceTypes() {
85    console.assert(this.selection, 'invalid selection');
86    return Object.values(this.selection.categories)
87        .reduce((accu, current) => accu.concat(current), []);
88  }
89
90  getPropertyForCategory(property) {
91    return Object.values(this.selection.categories)
92        .reduce(
93            (outer_accu, instance_types) => outer_accu +
94                instance_types.reduce(
95                    (inner_accu, instance_type) => inner_accu +
96                        this.selectedData
97                            .instance_type_data[instance_type][property],
98                    0),
99            0);
100  }
101
102  getPropertyForInstanceTypes(property) {
103    return this.selectedInstanceTypes.reduce(
104        (accu, instance_type) => accu +
105            this.selectedData.instance_type_data[instance_type][property],
106        0);
107  }
108
109  formatBytes(bytes) {
110    const units = ['B', 'KiB', 'MiB'];
111    const divisor = 1024;
112    let index = 0;
113    while (index < units.length && bytes >= divisor) {
114      index++;
115      bytes /= divisor;
116    }
117    return bytes + units[index];
118  }
119
120  getCategoryData() {
121    const labels = [
122      'Bucket',
123      ...Object.keys(this.selection.categories)
124          .map(k => this.selection.category_names.get(k))
125    ];
126    const data = this.selectedData.bucket_sizes.map(
127        (bucket_size, index) =>
128            [`<${this.formatBytes(bucket_size)}`,
129             ...Object.values(this.selection.categories)
130                 .map(
131                     instance_types =>
132                         instance_types
133                             .map(
134                                 instance_type =>
135                                     this.selectedData
136                                         .instance_type_data[instance_type]
137                                         .histogram[index])
138                             .reduce((accu, current) => accu + current, 0))]);
139    // Adjust last histogram bucket label.
140    data[data.length - 1][0] = 'rest';
141    return [labels, ...data];
142  }
143
144  getInstanceTypeData() {
145    const instance_types = this.selectedInstanceTypes;
146    const labels = ['Bucket', ...instance_types];
147    const data = this.selectedData.bucket_sizes.map(
148        (bucket_size, index) =>
149            [`<${bucket_size}`,
150             ...instance_types.map(
151                 instance_type =>
152                     this.selectedData.instance_type_data[instance_type]
153                         .histogram[index])]);
154    // Adjust last histogram bucket label.
155    data[data.length - 1][0] = 'rest';
156    return [labels, ...data];
157  }
158
159  getChartData() {
160    switch (this.selection.data_view) {
161      case VIEW_BY_FIELD_TYPE:
162        return this.getFieldData();
163      case VIEW_BY_INSTANCE_CATEGORY:
164        return this.getCategoryData();
165      case VIEW_BY_INSTANCE_TYPE:
166      default:
167        return this.getInstanceTypeData();
168    }
169  }
170
171  drawChart() {
172    const chart_data = this.getChartData();
173    const data = google.visualization.arrayToDataTable(chart_data);
174    const options = {
175      legend: {position: 'top', maxLines: '1'},
176      chartArea: {width: '85%', height: '85%'},
177      bar: {groupWidth: '80%'},
178      hAxis: {
179        title: 'Count',
180        minValue: 0
181      },
182      explorer: {},
183    };
184    const chart = new google.visualization.BarChart(this.$('#chart'));
185    this.show();
186    chart.draw(data, options);
187  }
188}
189
190customElements.define('histogram-viewer', HistogramViewer);
191