1// Copyright (C) 2023 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use size 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 {duration, Time, time} from '../../base/time'; 16import {Engine} from '../../trace_processor/engine'; 17import { 18 LONG, 19 NUM, 20 NUM_NULL, 21 STR_NULL, 22} from '../../trace_processor/query_result'; 23import { 24 constraintsToQuerySuffix, 25 fromNumNull, 26 SQLConstraints, 27} from '../../trace_processor/sql_utils'; 28import { 29 asThreadStateSqlId, 30 asUtid, 31 SchedSqlId, 32 ThreadStateSqlId, 33 Utid, 34} from './core_types'; 35import {getThreadInfo, ThreadInfo} from './thread'; 36 37const states: {[key: string]: string} = { 38 'R': 'Runnable', 39 'S': 'Sleeping', 40 'D': 'Uninterruptible Sleep', 41 'T': 'Stopped', 42 't': 'Traced', 43 'X': 'Exit (Dead)', 44 'Z': 'Exit (Zombie)', 45 'x': 'Task Dead', 46 'I': 'Idle', 47 'K': 'Wake Kill', 48 'W': 'Waking', 49 'P': 'Parked', 50 'N': 'No Load', 51 '+': '(Preempted)', 52}; 53 54export function translateState( 55 state: string | undefined | null, 56 ioWait: boolean | undefined = undefined, 57) { 58 if (state === undefined) return ''; 59 60 // Self describing states 61 switch (state) { 62 case 'Running': 63 case 'Initialized': 64 case 'Deferred Ready': 65 case 'Transition': 66 case 'Stand By': 67 case 'Waiting': 68 return state; 69 } 70 71 if (state === null) { 72 return 'Unknown'; 73 } 74 let result = states[state[0]]; 75 if (ioWait === true) { 76 result += ' (IO)'; 77 } else if (ioWait === false) { 78 result += ' (non-IO)'; 79 } 80 for (let i = 1; i < state.length; i++) { 81 result += state[i] === '+' ? ' ' : ' + '; 82 result += states[state[i]]; 83 } 84 // state is some string we don't know how to translate. 85 if (result === undefined) return state; 86 87 return result; 88} 89 90// Single thread state slice, corresponding to a row of |thread_slice| table. 91export interface ThreadState { 92 // Id into |thread_state| table. 93 id: ThreadStateSqlId; 94 // Id of the corresponding entry in the |sched| table. 95 schedSqlId?: SchedSqlId; 96 // Timestamp of the beginning of this thread state in nanoseconds. 97 ts: time; 98 // Duration of this thread state in nanoseconds. 99 dur: duration; 100 // CPU id if this thread state corresponds to a thread running on the CPU. 101 cpu?: number; 102 // Human-readable name of this thread state. 103 state: string; 104 // Kernel function where the thread has suspended. 105 blockedFunction?: string; 106 // Description of the thread itself. 107 thread?: ThreadInfo; 108 // Thread that was running when this thread was woken up. 109 wakerUtid?: Utid; 110 // Active thread state at the time of the wakeup. 111 wakerId?: ThreadStateSqlId; 112 // Was the wakeup from an interrupt context? It is possible for this to be 113 // unset even for runnable states, if the trace was recorded without 114 // interrupt information. 115 wakerInterruptCtx?: boolean; 116 // Kernel priority of this thread state. 117 priority?: number; 118} 119 120// Gets a list of thread state objects from Trace Processor with given 121// constraints. 122export async function getThreadStateFromConstraints( 123 engine: Engine, 124 constraints: SQLConstraints, 125): Promise<ThreadState[]> { 126 const query = await engine.query(` 127 WITH raw AS ( 128 SELECT 129 ts.id, 130 sched.id AS sched_id, 131 ts.ts, 132 ts.dur, 133 ts.cpu, 134 ts.state, 135 ts.blocked_function, 136 ts.io_wait, 137 ts.utid, 138 ts.waker_utid, 139 ts.waker_id, 140 ts.irq_context, 141 sched.priority 142 FROM thread_state ts 143 LEFT JOIN sched USING (utid, ts) 144 ) 145 SELECT * FROM raw 146 147 ${constraintsToQuerySuffix(constraints)}`); 148 const it = query.iter({ 149 id: NUM, 150 sched_id: NUM_NULL, 151 ts: LONG, 152 dur: LONG, 153 cpu: NUM_NULL, 154 state: STR_NULL, 155 blocked_function: STR_NULL, 156 io_wait: NUM_NULL, 157 utid: NUM, 158 waker_utid: NUM_NULL, 159 waker_id: NUM_NULL, 160 irq_context: NUM_NULL, 161 priority: NUM_NULL, 162 }); 163 164 const result: ThreadState[] = []; 165 166 for (; it.valid(); it.next()) { 167 const ioWait = it.io_wait === null ? undefined : it.io_wait > 0; 168 169 // TODO(altimin): Consider fetching thread / process info using a single 170 // query instead of one per row. 171 result.push({ 172 id: it.id as ThreadStateSqlId, 173 schedSqlId: fromNumNull(it.sched_id) as SchedSqlId | undefined, 174 ts: Time.fromRaw(it.ts), 175 dur: it.dur, 176 cpu: fromNumNull(it.cpu), 177 state: translateState(it.state ?? undefined, ioWait), 178 blockedFunction: it.blocked_function ?? undefined, 179 thread: await getThreadInfo(engine, asUtid(it.utid)), 180 wakerUtid: asUtid(it.waker_utid ?? undefined), 181 wakerId: asThreadStateSqlId(it.waker_id ?? undefined), 182 wakerInterruptCtx: fromNumNull(it.irq_context) as boolean | undefined, 183 priority: fromNumNull(it.priority), 184 }); 185 } 186 return result; 187} 188 189export async function getThreadState( 190 engine: Engine, 191 id: number, 192): Promise<ThreadState | undefined> { 193 const result = await getThreadStateFromConstraints(engine, { 194 filters: [`id=${id}`], 195 }); 196 if (result.length > 1) { 197 throw new Error(`thread_state table has more than one row with id ${id}`); 198 } 199 if (result.length === 0) { 200 return undefined; 201 } 202 return result[0]; 203} 204