xref: /aosp_15_r20/external/perfetto/ui/src/components/sql_utils/thread_state.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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