1// Copyright (C) 2020 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this 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 {ColumnDef, ThreadStateExtra} from '../../common/aggregation_data'; 16import {Engine} from '../../common/engine'; 17import {slowlyCountRows} from '../../common/query_iterator'; 18import {Area, Sorting} from '../../common/state'; 19import {translateState} from '../../common/thread_state'; 20import {toNs} from '../../common/time'; 21import { 22 Config, 23 THREAD_STATE_TRACK_KIND 24} from '../../tracks/thread_state/common'; 25import {globals} from '../globals'; 26 27import {AggregationController} from './aggregation_controller'; 28 29export class ThreadAggregationController extends AggregationController { 30 private utids?: number[]; 31 32 setThreadStateUtids(tracks: string[]) { 33 this.utids = []; 34 for (const trackId of tracks) { 35 const track = globals.state.tracks[trackId]; 36 // Track will be undefined for track groups. 37 if (track !== undefined && track.kind === THREAD_STATE_TRACK_KIND) { 38 this.utids.push((track.config as Config).utid); 39 } 40 } 41 } 42 43 async createAggregateView(engine: Engine, area: Area) { 44 await engine.query(`drop view if exists ${this.kind};`); 45 this.setThreadStateUtids(area.tracks); 46 if (this.utids === undefined || this.utids.length === 0) return false; 47 48 const query = ` 49 create view ${this.kind} as 50 SELECT 51 process.name as process_name, 52 pid, 53 thread.name as thread_name, 54 tid, 55 state || ',' || IFNULL(io_wait, 'NULL') as concat_state, 56 sum(dur) AS total_dur, 57 sum(dur)/count(1) as avg_dur, 58 count(1) as occurrences 59 FROM process 60 JOIN thread USING(upid) 61 JOIN thread_state USING(utid) 62 WHERE utid IN (${this.utids}) AND 63 thread_state.ts + thread_state.dur > ${toNs(area.startSec)} AND 64 thread_state.ts < ${toNs(area.endSec)} 65 GROUP BY utid, concat_state 66 `; 67 68 await engine.query(query); 69 return true; 70 } 71 72 async getExtra(engine: Engine, area: Area): Promise<ThreadStateExtra|void> { 73 this.setThreadStateUtids(area.tracks); 74 if (this.utids === undefined || this.utids.length === 0) return; 75 76 const query = `select state, io_wait, sum(dur) as total_dur from process 77 JOIN thread USING(upid) 78 JOIN thread_state USING(utid) 79 WHERE utid IN (${this.utids}) AND thread_state.ts + thread_state.dur > ${ 80 toNs(area.startSec)} AND 81 thread_state.ts < ${toNs(area.endSec)} 82 GROUP BY state, io_wait`; 83 const result = await engine.query(query); 84 const numRows = slowlyCountRows(result); 85 86 const summary: ThreadStateExtra = { 87 kind: 'THREAD_STATE', 88 states: [], 89 values: new Float64Array(numRows), 90 totalMs: 0 91 }; 92 for (let row = 0; row < numRows; row++) { 93 const state = result.columns[0].stringValues![row]; 94 const ioWait = result.columns[1].isNulls![row] ? 95 undefined : 96 !!result.columns[1].longValues![row]; 97 summary.states.push(translateState(state, ioWait)); 98 summary.values[row] = result.columns[2].longValues![row] / 1000000; // ms 99 } 100 summary.totalMs = summary.values.reduce((a, b) => a + b, 0); 101 return summary; 102 } 103 104 getColumnDefinitions(): ColumnDef[] { 105 return [ 106 { 107 title: 'Process', 108 kind: 'STRING', 109 columnConstructor: Uint16Array, 110 columnId: 'process_name', 111 }, 112 { 113 title: 'PID', 114 kind: 'NUMBER', 115 columnConstructor: Uint16Array, 116 columnId: 'pid' 117 }, 118 { 119 title: 'Thread', 120 kind: 'STRING', 121 columnConstructor: Uint16Array, 122 columnId: 'thread_name' 123 }, 124 { 125 title: 'TID', 126 kind: 'NUMBER', 127 columnConstructor: Uint16Array, 128 columnId: 'tid' 129 }, 130 { 131 title: 'State', 132 kind: 'STATE', 133 columnConstructor: Uint16Array, 134 columnId: 'concat_state' 135 }, 136 { 137 title: 'Wall duration (ms)', 138 kind: 'TIMESTAMP_NS', 139 columnConstructor: Float64Array, 140 columnId: 'total_dur', 141 sum: true 142 }, 143 { 144 title: 'Avg Wall duration (ms)', 145 kind: 'TIMESTAMP_NS', 146 columnConstructor: Float64Array, 147 columnId: 'avg_dur' 148 }, 149 { 150 title: 'Occurrences', 151 kind: 'NUMBER', 152 columnConstructor: Uint16Array, 153 columnId: 'occurrences', 154 sum: true 155 } 156 ]; 157 } 158 159 getTabName() { 160 return 'Thread States'; 161 } 162 163 getDefaultSorting(): Sorting { 164 return {column: 'total_dur', direction: 'DESC'}; 165 } 166} 167