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