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'; 16 17import {Actions} from '../common/actions'; 18import { 19 AggregateData, 20 Column, 21 ThreadStateExtra, 22} from '../common/aggregation_data'; 23import {colorForState, textColorForState} from '../common/colorizer'; 24import {translateState} from '../common/thread_state'; 25import {tpTimeToMillis} from '../common/time'; 26 27import {globals} from './globals'; 28import {Panel} from './panel'; 29 30export interface AggregationPanelAttrs { 31 data: AggregateData; 32 kind: string; 33} 34 35export class AggregationPanel extends Panel<AggregationPanelAttrs> { 36 view({attrs}: m.CVnode<AggregationPanelAttrs>) { 37 return m( 38 '.details-panel', 39 m('.details-panel-heading.aggregation', 40 attrs.data.extra !== undefined && 41 attrs.data.extra.kind === 'THREAD_STATE' ? 42 this.showStateSummary(attrs.data.extra) : 43 null, 44 this.showTimeRange(), 45 m('table', 46 m('tr', 47 attrs.data.columns.map( 48 (col) => this.formatColumnHeading(col, attrs.kind))), 49 m('tr.sum', attrs.data.columnSums.map((sum) => { 50 const sumClass = sum === '' ? 'td' : 'td.sum-data'; 51 return m(sumClass, sum); 52 })))), 53 m( 54 '.details-table.aggregation', 55 m('table', this.getRows(attrs.data)), 56 )); 57 } 58 59 formatColumnHeading(col: Column, id: string) { 60 const pref = globals.state.aggregatePreferences[id]; 61 let sortIcon = ''; 62 if (pref && pref.sorting && pref.sorting.column === col.columnId) { 63 sortIcon = pref.sorting.direction === 'DESC' ? 'arrow_drop_down' : 64 'arrow_drop_up'; 65 } 66 return m( 67 'th', 68 { 69 onclick: () => { 70 globals.dispatch( 71 Actions.updateAggregateSorting({id, column: col.columnId})); 72 }, 73 }, 74 col.title, 75 m('i.material-icons', sortIcon)); 76 } 77 78 getRows(data: AggregateData) { 79 if (data.columns.length === 0) return; 80 const rows = []; 81 for (let i = 0; i < data.columns[0].data.length; i++) { 82 const row = []; 83 for (let j = 0; j < data.columns.length; j++) { 84 row.push(m('td', this.getFormattedData(data, i, j))); 85 } 86 rows.push(m('tr', row)); 87 } 88 return rows; 89 } 90 91 getFormattedData(data: AggregateData, rowIndex: number, columnIndex: number) { 92 switch (data.columns[columnIndex].kind) { 93 case 'STRING': 94 return data.strings[data.columns[columnIndex].data[rowIndex]]; 95 case 'TIMESTAMP_NS': 96 return `${data.columns[columnIndex].data[rowIndex] / 1000000}`; 97 case 'STATE': { 98 const concatState = 99 data.strings[data.columns[columnIndex].data[rowIndex]]; 100 const split = concatState.split(','); 101 const ioWait = 102 split[1] === 'NULL' ? undefined : !!Number.parseInt(split[1], 10); 103 return translateState(split[0], ioWait); 104 } 105 case 'NUMBER': 106 default: 107 return data.columns[columnIndex].data[rowIndex]; 108 } 109 } 110 111 showTimeRange() { 112 const selection = globals.state.currentSelection; 113 if (selection === null || selection.kind !== 'AREA') return undefined; 114 const selectedArea = globals.state.areas[selection.areaId]; 115 const rangeDurationMs = 116 tpTimeToMillis(selectedArea.end - selectedArea.start); 117 return m('.time-range', `Selected range: ${rangeDurationMs.toFixed(6)} ms`); 118 } 119 120 // Thread state aggregation panel only 121 showStateSummary(data: ThreadStateExtra) { 122 if (data === undefined) return undefined; 123 const states = []; 124 for (let i = 0; i < data.states.length; i++) { 125 const color = colorForState(data.states[i]); 126 const textColor = textColorForState(data.states[i]); 127 const width = data.values[i] / data.totalMs * 100; 128 states.push( 129 m('.state', 130 { 131 style: { 132 background: `hsl(${color.h},${color.s}%,${color.l}%)`, 133 color: `${textColor}`, 134 width: `${width}%`, 135 }, 136 }, 137 `${data.states[i]}: ${data.values[i]} ms`)); 138 } 139 return m('.states', states); 140 } 141 142 renderCanvas() {} 143} 144