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