1// Copyright (C) 2019 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use size file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import m from 'mithril'; 16import { 17 AggregateData, 18 Column, 19 Sorting, 20 ThreadStateExtra, 21} from '../public/aggregation'; 22import {colorForState} from './colorizer'; 23import {DurationWidget} from './widgets/duration'; 24import {translateState} from './sql_utils/thread_state'; 25import {Trace} from '../public/trace'; 26 27export interface AggregationPanelAttrs { 28 readonly trace: Trace; 29 readonly data: AggregateData; 30 readonly model: AggState; 31} 32 33export interface AggState { 34 getSortingPrefs(): Sorting | undefined; 35 toggleSortingColumn(column: string): void; 36} 37 38export class AggregationPanel 39 implements m.ClassComponent<AggregationPanelAttrs> 40{ 41 private trace: Trace; 42 43 constructor({attrs}: m.CVnode<AggregationPanelAttrs>) { 44 this.trace = attrs.trace; 45 } 46 47 view({attrs}: m.CVnode<AggregationPanelAttrs>) { 48 return m( 49 '.details-panel', 50 m( 51 '.details-panel-heading.aggregation', 52 attrs.data.extra !== undefined && 53 attrs.data.extra.kind === 'THREAD_STATE' 54 ? this.showStateSummary(attrs.data.extra) 55 : null, 56 this.showTimeRange(), 57 m( 58 'table', 59 m( 60 'tr', 61 attrs.data.columns.map((col) => 62 this.formatColumnHeading(col, attrs.model), 63 ), 64 ), 65 m( 66 'tr.sum', 67 attrs.data.columnSums.map((sum) => { 68 const sumClass = sum === '' ? 'td' : 'td.sum-data'; 69 return m(sumClass, sum); 70 }), 71 ), 72 ), 73 ), 74 m('.details-table.aggregation', m('table', this.getRows(attrs.data))), 75 ); 76 } 77 78 formatColumnHeading(col: Column, model: AggState) { 79 const pref = model.getSortingPrefs(); 80 let sortIcon = ''; 81 if (pref && pref.column === col.columnId) { 82 sortIcon = 83 pref.direction === 'DESC' ? 'arrow_drop_down' : 'arrow_drop_up'; 84 } 85 return m( 86 'th', 87 { 88 onclick: () => { 89 model.toggleSortingColumn(col.columnId); 90 }, 91 }, 92 col.title, 93 m('i.material-icons', sortIcon), 94 ); 95 } 96 97 getRows(data: AggregateData) { 98 if (data.columns.length === 0) return; 99 const rows = []; 100 for (let i = 0; i < data.columns[0].data.length; i++) { 101 const row = []; 102 for (let j = 0; j < data.columns.length; j++) { 103 row.push(m('td', this.getFormattedData(data, i, j))); 104 } 105 rows.push(m('tr', row)); 106 } 107 return rows; 108 } 109 110 getFormattedData(data: AggregateData, rowIndex: number, columnIndex: number) { 111 switch (data.columns[columnIndex].kind) { 112 case 'STRING': 113 return data.strings[data.columns[columnIndex].data[rowIndex]]; 114 case 'TIMESTAMP_NS': 115 return `${data.columns[columnIndex].data[rowIndex] / 1000000}`; 116 case 'STATE': { 117 const concatState = 118 data.strings[data.columns[columnIndex].data[rowIndex]]; 119 const split = concatState.split(','); 120 const ioWait = 121 split[1] === 'NULL' ? undefined : !!Number.parseInt(split[1], 10); 122 return translateState(split[0], ioWait); 123 } 124 case 'NUMBER': 125 default: 126 return data.columns[columnIndex].data[rowIndex]; 127 } 128 } 129 130 showTimeRange() { 131 const selection = this.trace.selection.selection; 132 if (selection.kind !== 'area') return undefined; 133 const duration = selection.end - selection.start; 134 return m( 135 '.time-range', 136 'Selected range: ', 137 m(DurationWidget, {dur: duration}), 138 ); 139 } 140 141 // Thread state aggregation panel only 142 showStateSummary(data: ThreadStateExtra) { 143 if (data === undefined) return undefined; 144 const states = []; 145 for (let i = 0; i < data.states.length; i++) { 146 const colorScheme = colorForState(data.states[i]); 147 const width = (data.values[i] / data.totalMs) * 100; 148 states.push( 149 m( 150 '.state', 151 { 152 style: { 153 background: colorScheme.base.cssString, 154 color: colorScheme.textBase.cssString, 155 width: `${width}%`, 156 }, 157 }, 158 `${data.states[i]}: ${data.values[i]} ms`, 159 ), 160 ); 161 } 162 return m('.states', states); 163 } 164} 165