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