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 {exists} from '../../base/utils'; 16import {ColumnDef, ThreadStateExtra} from '../../common/aggregation_data'; 17import {Area, Sorting} from '../../common/state'; 18import {translateState} from '../../common/thread_state'; 19import {THREAD_STATE_TRACK_KIND} from '../../core/track_kinds'; 20import {globals} from '../../frontend/globals'; 21import {Engine} from '../../trace_processor/engine'; 22import {NUM, NUM_NULL, STR_NULL} from '../../trace_processor/query_result'; 23 24import {AggregationController} from './aggregation_controller'; 25 26export class ThreadAggregationController extends AggregationController { 27 private utids?: number[]; 28 29 setThreadStateUtids(tracks: string[]) { 30 this.utids = []; 31 for (const trackId of tracks) { 32 const track = globals.state.tracks[trackId]; 33 // Track will be undefined for track groups. 34 if (track?.uri) { 35 const trackInfo = globals.trackManager.resolveTrackInfo(track.uri); 36 if (trackInfo?.kind === THREAD_STATE_TRACK_KIND) { 37 exists(trackInfo.utid) && this.utids.push(trackInfo.utid); 38 } 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 thread 60 JOIN thread_state USING(utid) 61 LEFT JOIN process USING(upid) 62 WHERE utid IN (${this.utids}) AND 63 thread_state.ts + thread_state.dur > ${area.start} AND 64 thread_state.ts < ${area.end} 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 as ioWait, sum(dur) as totalDur 77 FROM thread JOIN thread_state USING(utid) 78 WHERE utid IN (${this.utids}) AND thread_state.ts + thread_state.dur > ${area.start} AND 79 thread_state.ts < ${area.end} 80 GROUP BY state, io_wait`; 81 const result = await engine.query(query); 82 83 const it = result.iter({ 84 state: STR_NULL, 85 ioWait: NUM_NULL, 86 totalDur: NUM, 87 }); 88 89 const summary: ThreadStateExtra = { 90 kind: 'THREAD_STATE', 91 states: [], 92 values: new Float64Array(result.numRows()), 93 totalMs: 0, 94 }; 95 summary.totalMs = 0; 96 for (let i = 0; it.valid(); ++i, it.next()) { 97 const state = it.state == null ? undefined : it.state; 98 const ioWait = it.ioWait === null ? undefined : it.ioWait > 0; 99 summary.states.push(translateState(state, ioWait)); 100 const ms = it.totalDur / 1000000; 101 summary.values[i] = ms; 102 summary.totalMs += ms; 103 } 104 return summary; 105 } 106 107 getColumnDefinitions(): ColumnDef[] { 108 return [ 109 { 110 title: 'Process', 111 kind: 'STRING', 112 columnConstructor: Uint16Array, 113 columnId: 'process_name', 114 }, 115 { 116 title: 'PID', 117 kind: 'NUMBER', 118 columnConstructor: Uint16Array, 119 columnId: 'pid', 120 }, 121 { 122 title: 'Thread', 123 kind: 'STRING', 124 columnConstructor: Uint16Array, 125 columnId: 'thread_name', 126 }, 127 { 128 title: 'TID', 129 kind: 'NUMBER', 130 columnConstructor: Uint16Array, 131 columnId: 'tid', 132 }, 133 { 134 title: 'State', 135 kind: 'STATE', 136 columnConstructor: Uint16Array, 137 columnId: 'concat_state', 138 }, 139 { 140 title: 'Wall duration (ms)', 141 kind: 'TIMESTAMP_NS', 142 columnConstructor: Float64Array, 143 columnId: 'total_dur', 144 sum: true, 145 }, 146 { 147 title: 'Avg Wall duration (ms)', 148 kind: 'TIMESTAMP_NS', 149 columnConstructor: Float64Array, 150 columnId: 'avg_dur', 151 }, 152 { 153 title: 'Occurrences', 154 kind: 'NUMBER', 155 columnConstructor: Uint16Array, 156 columnId: 'occurrences', 157 sum: true, 158 }, 159 ]; 160 } 161 162 getTabName() { 163 return 'Thread States'; 164 } 165 166 getDefaultSorting(): Sorting { 167 return {column: 'total_dur', direction: 'DESC'}; 168 } 169} 170