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