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, Sorting, ThreadStateExtra} from '../../public/aggregation'; 16import {AreaSelection} from '../../public/selection'; 17import {Engine} from '../../trace_processor/engine'; 18import { 19 LONG, 20 NUM, 21 NUM_NULL, 22 STR, 23 STR_NULL, 24} from '../../trace_processor/query_result'; 25import {AreaSelectionAggregator} from '../../public/selection'; 26import {Dataset} from '../../trace_processor/dataset'; 27import {translateState} from '../../components/sql_utils/thread_state'; 28 29export class ThreadStateSelectionAggregator implements AreaSelectionAggregator { 30 readonly id = 'thread_state_aggregation'; 31 32 readonly schema = { 33 dur: LONG, 34 io_wait: NUM_NULL, 35 state: STR, 36 utid: NUM, 37 } as const; 38 39 async createAggregateView( 40 engine: Engine, 41 area: AreaSelection, 42 dataset?: Dataset, 43 ) { 44 if (dataset === undefined) return false; 45 46 await engine.query(` 47 create or replace perfetto table ${this.id} as 48 select 49 process.name as process_name, 50 process.pid, 51 thread.name as thread_name, 52 thread.tid, 53 tstate.state || ',' || ifnull(tstate.io_wait, 'NULL') as concat_state, 54 sum(tstate.dur) AS total_dur, 55 sum(tstate.dur) / count() as avg_dur, 56 count() as occurrences 57 from (${dataset.query()}) tstate 58 join thread using (utid) 59 left join process using (upid) 60 where 61 ts + dur > ${area.start} 62 and ts < ${area.end} 63 group by utid, concat_state 64 `); 65 66 return true; 67 } 68 69 async getExtra( 70 engine: Engine, 71 area: AreaSelection, 72 dataset?: Dataset, 73 ): Promise<ThreadStateExtra | void> { 74 if (dataset === undefined) return; 75 76 const query = ` 77 select 78 state, 79 io_wait as ioWait, 80 sum(dur) as totalDur 81 from (${dataset.query()}) tstate 82 join thread using (utid) 83 where tstate.ts + tstate.dur > ${area.start} 84 and tstate.ts < ${area.end} 85 group by state, io_wait 86 `; 87 const result = await engine.query(query); 88 89 const it = result.iter({ 90 state: STR_NULL, 91 ioWait: NUM_NULL, 92 totalDur: NUM, 93 }); 94 95 let totalMs = 0; 96 const values = new Float64Array(result.numRows()); 97 const states = []; 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 states.push(translateState(state, ioWait)); 102 const ms = it.totalDur / 1000000; 103 values[i] = ms; 104 totalMs += ms; 105 } 106 return { 107 kind: 'THREAD_STATE', 108 states, 109 values, 110 totalMs, 111 }; 112 } 113 114 getColumnDefinitions(): ColumnDef[] { 115 return [ 116 { 117 title: 'Process', 118 kind: 'STRING', 119 columnConstructor: Uint16Array, 120 columnId: 'process_name', 121 }, 122 { 123 title: 'PID', 124 kind: 'NUMBER', 125 columnConstructor: Uint16Array, 126 columnId: 'pid', 127 }, 128 { 129 title: 'Thread', 130 kind: 'STRING', 131 columnConstructor: Uint16Array, 132 columnId: 'thread_name', 133 }, 134 { 135 title: 'TID', 136 kind: 'NUMBER', 137 columnConstructor: Uint16Array, 138 columnId: 'tid', 139 }, 140 { 141 title: 'State', 142 kind: 'STATE', 143 columnConstructor: Uint16Array, 144 columnId: 'concat_state', 145 }, 146 { 147 title: 'Wall duration (ms)', 148 kind: 'TIMESTAMP_NS', 149 columnConstructor: Float64Array, 150 columnId: 'total_dur', 151 sum: true, 152 }, 153 { 154 title: 'Avg Wall duration (ms)', 155 kind: 'TIMESTAMP_NS', 156 columnConstructor: Float64Array, 157 columnId: 'avg_dur', 158 }, 159 { 160 title: 'Occurrences', 161 kind: 'NUMBER', 162 columnConstructor: Uint16Array, 163 columnId: 'occurrences', 164 sum: true, 165 }, 166 ]; 167 } 168 169 getTabName() { 170 return 'Thread States'; 171 } 172 173 getDefaultSorting(): Sorting { 174 return {column: 'total_dur', direction: 'DESC'}; 175 } 176} 177