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 {NUM, NUM_NULL, STR_NULL} from '../../common/query_result'; 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 = 77 `select state, io_wait as ioWait, sum(dur) as totalDur from process 78 JOIN thread USING(upid) 79 JOIN thread_state USING(utid) 80 WHERE utid IN (${this.utids}) AND thread_state.ts + thread_state.dur > ${ 81 toNs(area.startSec)} AND 82 thread_state.ts < ${toNs(area.endSec)} 83 GROUP BY state, io_wait`; 84 const result = await engine.query(query); 85 86 const it = result.iter({ 87 state: STR_NULL, 88 ioWait: NUM_NULL, 89 totalDur: NUM, 90 }); 91 92 const summary: ThreadStateExtra = { 93 kind: 'THREAD_STATE', 94 states: [], 95 values: new Float64Array(result.numRows()), 96 totalMs: 0 97 }; 98 summary.totalMs = 0; 99 for (let i = 0; it.valid(); ++i, it.next()) { 100 const state = it.state == null ? undefined : it.state; 101 const ioWait = it.ioWait === null ? undefined : it.ioWait > 0; 102 summary.states.push(translateState(state, ioWait)); 103 const ms = it.totalDur / 1000000; 104 summary.values[i] = ms; 105 summary.totalMs += ms; 106 } 107 return summary; 108 } 109 110 getColumnDefinitions(): ColumnDef[] { 111 return [ 112 { 113 title: 'Process', 114 kind: 'STRING', 115 columnConstructor: Uint16Array, 116 columnId: 'process_name', 117 }, 118 { 119 title: 'PID', 120 kind: 'NUMBER', 121 columnConstructor: Uint16Array, 122 columnId: 'pid' 123 }, 124 { 125 title: 'Thread', 126 kind: 'STRING', 127 columnConstructor: Uint16Array, 128 columnId: 'thread_name' 129 }, 130 { 131 title: 'TID', 132 kind: 'NUMBER', 133 columnConstructor: Uint16Array, 134 columnId: 'tid' 135 }, 136 { 137 title: 'State', 138 kind: 'STATE', 139 columnConstructor: Uint16Array, 140 columnId: 'concat_state' 141 }, 142 { 143 title: 'Wall duration (ms)', 144 kind: 'TIMESTAMP_NS', 145 columnConstructor: Float64Array, 146 columnId: 'total_dur', 147 sum: true 148 }, 149 { 150 title: 'Avg Wall duration (ms)', 151 kind: 'TIMESTAMP_NS', 152 columnConstructor: Float64Array, 153 columnId: 'avg_dur' 154 }, 155 { 156 title: 'Occurrences', 157 kind: 'NUMBER', 158 columnConstructor: Uint16Array, 159 columnId: 'occurrences', 160 sum: true 161 } 162 ]; 163 } 164 165 getTabName() { 166 return 'Thread States'; 167 } 168 169 getDefaultSorting(): Sorting { 170 return {column: 'total_dur', direction: 'DESC'}; 171 } 172} 173