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