blob: 26dcb573993976a5731711482ead8717a3760c23 [file] [log] [blame]
Alexander Timin332f29a2023-02-21 17:48:05 +00001// 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
Steve Golton50388462023-04-05 16:14:38 +010015import m from 'mithril';
Alexander Timin332f29a2023-02-21 17:48:05 +000016
Steve Golton13dc3b62023-11-14 18:16:10 +000017import {Time, time} from '../base/time';
Zim497e9092023-08-01 02:09:18 +010018import {runQuery} from '../common/queries';
Hector Dearmanba396db2023-07-03 18:25:08 +010019import {raf} from '../core/raf_scheduler';
Steve Golton6caf3c02023-09-07 08:09:06 +010020import {Anchor} from '../widgets/anchor';
21import {Button} from '../widgets/button';
22import {DetailsShell} from '../widgets/details_shell';
Steve Golton6caf3c02023-09-07 08:09:06 +010023import {GridLayout} from '../widgets/grid_layout';
24import {Section} from '../widgets/section';
25import {SqlRef} from '../widgets/sql_ref';
26import {Tree, TreeNode} from '../widgets/tree';
Steve Goltonca6f4312023-06-01 07:03:05 +000027
Steve Golton6559f9e2024-03-22 12:32:27 +000028import {BottomTab, NewBottomTabArgs} from './bottom_tab';
Steve Goltonb3a389d2023-07-10 11:03:17 +010029import {SchedSqlId, ThreadStateSqlId} from './sql_types';
Steve Goltonca6f4312023-06-01 07:03:05 +000030import {
Alexander Timin9075a352023-06-19 18:26:48 +010031 getFullThreadName,
Steve Goltonca6f4312023-06-01 07:03:05 +000032 getProcessName,
33 getThreadName,
34 ThreadInfo,
35} from './thread_and_process_info';
Alexander Timin9075a352023-06-19 18:26:48 +010036import {
37 getThreadState,
38 getThreadStateFromConstraints,
39 goToSchedSlice,
40 ThreadState,
41 ThreadStateRef,
42} from './thread_state';
Steve Golton13dc3b62023-11-14 18:16:10 +000043import {DurationWidget, renderDuration} from './widgets/duration';
Steve Golton9248aed2023-06-01 15:20:01 +010044import {Timestamp} from './widgets/timestamp';
Steve Golton6fc2bdc2024-01-25 16:56:22 +000045import {addDebugSliceTrack} from './debug_tracks';
Alexander Timin332f29a2023-02-21 17:48:05 +000046
47interface ThreadStateTabConfig {
48 // Id into |thread_state| sql table.
49 readonly id: ThreadStateSqlId;
50}
51
Alexander Timin9075a352023-06-19 18:26:48 +010052interface RelatedThreadStates {
53 prev?: ThreadState;
54 next?: ThreadState;
55 waker?: ThreadState;
56 wakee?: ThreadState[];
57}
58
Alexander Timin332f29a2023-02-21 17:48:05 +000059export class ThreadStateTab extends BottomTab<ThreadStateTabConfig> {
Hector Dearmand3ccdc82023-06-19 14:34:14 +010060 static readonly kind = 'dev.perfetto.ThreadStateTab';
Alexander Timin332f29a2023-02-21 17:48:05 +000061
62 state?: ThreadState;
Alexander Timin9075a352023-06-19 18:26:48 +010063 relatedStates?: RelatedThreadStates;
Alexander Timin332f29a2023-02-21 17:48:05 +000064 loaded: boolean = false;
65
Steve Goltonc4115e82024-01-19 16:02:05 +000066 static create(args: NewBottomTabArgs<ThreadStateTabConfig>): ThreadStateTab {
Alexander Timin332f29a2023-02-21 17:48:05 +000067 return new ThreadStateTab(args);
68 }
69
Steve Goltonc4115e82024-01-19 16:02:05 +000070 constructor(args: NewBottomTabArgs<ThreadStateTabConfig>) {
Alexander Timin332f29a2023-02-21 17:48:05 +000071 super(args);
72
Alexander Timin9075a352023-06-19 18:26:48 +010073 this.load().then(() => {
Alexander Timin332f29a2023-02-21 17:48:05 +000074 this.loaded = true;
Hector Dearmanba396db2023-07-03 18:25:08 +010075 raf.scheduleFullRedraw();
Alexander Timin332f29a2023-02-21 17:48:05 +000076 });
77 }
78
Alexander Timin9075a352023-06-19 18:26:48 +010079 async load() {
80 this.state = await getThreadState(this.engine, this.config.id);
81
82 if (!this.state) {
83 return;
84 }
85
86 const relatedStates: RelatedThreadStates = {};
Steve Golton7e63f422024-03-18 14:17:32 +000087 relatedStates.prev = (
88 await getThreadStateFromConstraints(this.engine, {
Alexander Timin9075a352023-06-19 18:26:48 +010089 filters: [
Steve Golton7e63f422024-03-18 14:17:32 +000090 `ts + dur = ${this.state.ts}`,
91 `utid = ${this.state.thread?.utid}`,
Alexander Timin9075a352023-06-19 18:26:48 +010092 ],
Steve Golton7e63f422024-03-18 14:17:32 +000093 limit: 1,
94 })
95 )[0];
96 relatedStates.next = (
97 await getThreadStateFromConstraints(this.engine, {
98 filters: [
99 `ts = ${this.state.ts + this.state.dur}`,
100 `utid = ${this.state.thread?.utid}`,
101 ],
102 limit: 1,
103 })
104 )[0];
105 if (this.state.wakerThread?.utid !== undefined) {
106 relatedStates.waker = (
107 await getThreadStateFromConstraints(this.engine, {
108 filters: [
109 `utid = ${this.state.wakerThread?.utid}`,
110 `ts <= ${this.state.ts}`,
111 `ts + dur >= ${this.state.ts}`,
112 ],
113 })
114 )[0];
Alexander Timin9075a352023-06-19 18:26:48 +0100115 }
116 relatedStates.wakee = await getThreadStateFromConstraints(this.engine, {
117 filters: [
118 `waker_utid = ${this.state.thread?.utid}`,
119 `state = 'R'`,
120 `ts >= ${this.state.ts}`,
121 `ts <= ${this.state.ts + this.state.dur}`,
122 ],
123 });
124
125 this.relatedStates = relatedStates;
126 }
127
Alexander Timin332f29a2023-02-21 17:48:05 +0000128 getTitle() {
129 // TODO(altimin): Support dynamic titles here.
130 return 'Current Selection';
131 }
132
Steve Goltonca6f4312023-06-01 07:03:05 +0000133 viewTab() {
134 // TODO(altimin/stevegolton): Differentiate between "Current Selection" and
135 // "Pinned" views in DetailsShell.
136 return m(
Steve Golton5dbacf52024-01-25 09:20:51 +0000137 DetailsShell,
138 {title: 'Thread State', description: this.renderLoadingText()},
Steve Golton7e63f422024-03-18 14:17:32 +0000139 m(
140 GridLayout,
Steve Golton5dbacf52024-01-25 09:20:51 +0000141 m(
142 Section,
143 {title: 'Details'},
144 this.state && this.renderTree(this.state),
145 ),
Steve Golton7e63f422024-03-18 14:17:32 +0000146 m(
147 Section,
Steve Golton5dbacf52024-01-25 09:20:51 +0000148 {title: 'Related thread states'},
Steve Golton7e63f422024-03-18 14:17:32 +0000149 this.renderRelatedThreadStates(),
150 ),
151 ),
Steve Goltonca6f4312023-06-01 07:03:05 +0000152 );
Alexander Timin332f29a2023-02-21 17:48:05 +0000153 }
154
Steve Goltonca6f4312023-06-01 07:03:05 +0000155 private renderLoadingText() {
156 if (!this.loaded) {
157 return 'Loading';
158 }
159 if (!this.state) {
160 return `Thread state ${this.config.id} does not exist`;
161 }
162 // TODO(stevegolton): Return something intelligent here.
163 return this.config.id;
164 }
165
166 private renderTree(state: ThreadState) {
167 const thread = state.thread;
168 const process = state.thread?.process;
Alexander Timin332f29a2023-02-21 17:48:05 +0000169 return m(
Steve Golton5dbacf52024-01-25 09:20:51 +0000170 Tree,
171 m(TreeNode, {
172 left: 'Start time',
173 right: m(Timestamp, {ts: state.ts}),
174 }),
175 m(TreeNode, {
176 left: 'Duration',
177 right: m(DurationWidget, {dur: state.dur}),
178 }),
179 m(TreeNode, {
180 left: 'State',
181 right: this.renderState(
Steve Golton7e63f422024-03-18 14:17:32 +0000182 state.state,
183 state.cpu,
184 state.schedSqlId,
185 state.ts,
186 ),
Steve Golton5dbacf52024-01-25 09:20:51 +0000187 }),
Steve Golton7e63f422024-03-18 14:17:32 +0000188 state.blockedFunction &&
189 m(TreeNode, {
190 left: 'Blocked function',
191 right: state.blockedFunction,
192 }),
193 process &&
194 m(TreeNode, {
195 left: 'Process',
196 right: getProcessName(process),
197 }),
Steve Golton5dbacf52024-01-25 09:20:51 +0000198 thread && m(TreeNode, {left: 'Thread', right: getThreadName(thread)}),
199 state.wakerThread && this.renderWakerThread(state.wakerThread),
200 m(TreeNode, {
201 left: 'SQL ID',
202 right: m(SqlRef, {table: 'thread_state', id: state.threadStateSqlId}),
203 }),
Steve Goltonca6f4312023-06-01 07:03:05 +0000204 );
205 }
206
207 private renderState(
Steve Golton7e63f422024-03-18 14:17:32 +0000208 state: string,
209 cpu: number | undefined,
210 id: SchedSqlId | undefined,
211 ts: time,
212 ): m.Children {
Steve Goltonca6f4312023-06-01 07:03:05 +0000213 if (!state) {
214 return null;
215 }
216 if (id === undefined || cpu === undefined) {
217 return state;
218 }
219 return m(
Steve Golton5dbacf52024-01-25 09:20:51 +0000220 Anchor,
221 {
222 title: 'Go to CPU slice',
223 icon: 'call_made',
224 onclick: () => goToSchedSlice(cpu, id, ts),
225 },
Steve Golton7e63f422024-03-18 14:17:32 +0000226 `${state} on CPU ${cpu}`,
227 );
Steve Goltonca6f4312023-06-01 07:03:05 +0000228 }
229
230 private renderWakerThread(wakerThread: ThreadInfo) {
231 return m(
Steve Golton5dbacf52024-01-25 09:20:51 +0000232 TreeNode,
233 {left: 'Waker'},
Steve Golton7e63f422024-03-18 14:17:32 +0000234 m(TreeNode, {
235 left: 'Process',
236 right: getProcessName(wakerThread.process),
237 }),
Steve Golton5dbacf52024-01-25 09:20:51 +0000238 m(TreeNode, {left: 'Thread', right: getThreadName(wakerThread)}),
Steve Goltonca6f4312023-06-01 07:03:05 +0000239 );
Alexander Timin332f29a2023-02-21 17:48:05 +0000240 }
241
Alexander Timin9075a352023-06-19 18:26:48 +0100242 private renderRelatedThreadStates(): m.Children {
243 if (this.state === undefined || this.relatedStates === undefined) {
244 return 'Loading';
245 }
246 const startTs = this.state.ts;
Steve Golton7e63f422024-03-18 14:17:32 +0000247 const renderRef = (state: ThreadState, name?: string) =>
248 m(ThreadStateRef, {
249 id: state.threadStateSqlId,
250 ts: state.ts,
251 dur: state.dur,
252 utid: state.thread!.utid,
253 name,
254 });
Zim56f67dc2023-08-27 01:50:30 +0100255
256 const sliceColumns = {ts: 'ts', dur: 'dur', name: 'name'};
Steve Golton7e63f422024-03-18 14:17:32 +0000257 const sliceColumnNames = ['id', 'utid', 'ts', 'dur', 'name', 'table_name'];
Zim51b77c02023-08-04 16:49:24 +0100258
Zim56f67dc2023-08-27 01:50:30 +0100259 const sliceLiteColumns = {ts: 'ts', dur: 'dur', name: 'thread_name'};
260 const sliceLiteColumnNames = [
Zim51b77c02023-08-04 16:49:24 +0100261 'id',
262 'utid',
Zim51b77c02023-08-04 16:49:24 +0100263 'ts',
264 'dur',
Zim51b77c02023-08-04 16:49:24 +0100265 'thread_name',
266 'process_name',
Zim51b77c02023-08-04 16:49:24 +0100267 'table_name',
268 ];
Alexander Timin9075a352023-06-19 18:26:48 +0100269
270 const nameForNextOrPrev = (state: ThreadState) =>
Steve Golton5dbacf52024-01-25 09:20:51 +0000271 `${state.state} for ${renderDuration(state.dur)}`;
Steve Golton7e63f422024-03-18 14:17:32 +0000272 return [
273 m(
Lalit Maganti52826e82024-04-02 14:16:12 +0100274 Tree,
275 this.relatedStates.waker &&
276 m(TreeNode, {
Steve Golton7e63f422024-03-18 14:17:32 +0000277 left: 'Waker',
278 right: renderRef(
Lalit Maganti52826e82024-04-02 14:16:12 +0100279 this.relatedStates.waker,
280 getFullThreadName(this.relatedStates.waker.thread),
281 ),
Steve Golton7e63f422024-03-18 14:17:32 +0000282 }),
Lalit Maganti52826e82024-04-02 14:16:12 +0100283 this.relatedStates.prev &&
284 m(TreeNode, {
Steve Golton7e63f422024-03-18 14:17:32 +0000285 left: 'Previous state',
286 right: renderRef(
Lalit Maganti52826e82024-04-02 14:16:12 +0100287 this.relatedStates.prev,
288 nameForNextOrPrev(this.relatedStates.prev),
289 ),
Steve Golton7e63f422024-03-18 14:17:32 +0000290 }),
Lalit Maganti52826e82024-04-02 14:16:12 +0100291 this.relatedStates.next &&
292 m(TreeNode, {
Steve Golton7e63f422024-03-18 14:17:32 +0000293 left: 'Next state',
294 right: renderRef(
Lalit Maganti52826e82024-04-02 14:16:12 +0100295 this.relatedStates.next,
296 nameForNextOrPrev(this.relatedStates.next),
297 ),
Steve Golton7e63f422024-03-18 14:17:32 +0000298 }),
Lalit Maganti52826e82024-04-02 14:16:12 +0100299 this.relatedStates.wakee &&
300 this.relatedStates.wakee.length > 0 &&
301 m(
302 TreeNode,
303 {
304 left: 'Woken threads',
305 },
306 this.relatedStates.wakee.map((state) =>
307 m(TreeNode, {
308 left: m(Timestamp, {
309 ts: state.ts,
310 display: [
311 'Start+',
312 m(DurationWidget, {dur: Time.sub(state.ts, startTs)}),
313 ],
314 }),
315 right: renderRef(state, getFullThreadName(state.thread)),
316 }),
317 ),
Steve Golton7e63f422024-03-18 14:17:32 +0000318 ),
Lalit Maganti52826e82024-04-02 14:16:12 +0100319 ),
Steve Golton7e63f422024-03-18 14:17:32 +0000320 m(Button, {
Steve Golton5dbacf52024-01-25 09:20:51 +0000321 label: 'Critical path lite',
Steve Golton7e63f422024-03-18 14:17:32 +0000322 onclick: () =>
Lalit Maganti52826e82024-04-02 14:16:12 +0100323 runQuery(
324 `INCLUDE PERFETTO MODULE sched.thread_executing_span;`,
325 this.engine,
326 ).then(() =>
327 addDebugSliceTrack(
328 this.engine,
329 {
330 sqlSource: `
Zim56f67dc2023-08-27 01:50:30 +0100331 SELECT
332 cr.id,
333 cr.utid,
334 cr.ts,
335 cr.dur,
336 thread.name AS thread_name,
337 process.name AS process_name,
338 'thread_state' AS table_name
339 FROM
Lalit Maganti3e16bfd2024-01-30 23:06:26 +0000340 _thread_executing_span_critical_path(
Zim56f67dc2023-08-27 01:50:30 +0100341 ${this.state?.thread?.utid},
342 trace_bounds.start_ts,
Zim7e5df7f2023-11-13 23:23:31 +0000343 trace_bounds.end_ts - trace_bounds.start_ts) cr,
Zim56f67dc2023-08-27 01:50:30 +0100344 trace_bounds
345 JOIN thread USING(utid)
346 JOIN process USING(upid)
347 `,
Lalit Maganti52826e82024-04-02 14:16:12 +0100348 columns: sliceLiteColumnNames,
349 },
350 `${this.state?.thread?.name}`,
351 sliceLiteColumns,
352 sliceLiteColumnNames,
353 ),
354 ),
Steve Golton7e63f422024-03-18 14:17:32 +0000355 }),
356 m(Button, {
Steve Golton5dbacf52024-01-25 09:20:51 +0000357 label: 'Critical path',
Steve Golton7e63f422024-03-18 14:17:32 +0000358 onclick: () =>
Lalit Maganti52826e82024-04-02 14:16:12 +0100359 runQuery(
360 `INCLUDE PERFETTO MODULE sched.thread_executing_span_with_slice;`,
361 this.engine,
362 ).then(() =>
363 addDebugSliceTrack(
364 this.engine,
365 {
366 sqlSource: `
Zim56f67dc2023-08-27 01:50:30 +0100367 SELECT cr.id, cr.utid, cr.ts, cr.dur, cr.name, cr.table_name
368 FROM
Lalit Maganti3e16bfd2024-01-30 23:06:26 +0000369 _thread_executing_span_critical_path_stack(
Zim56f67dc2023-08-27 01:50:30 +0100370 ${this.state?.thread?.utid},
371 trace_bounds.start_ts,
Zim7e5df7f2023-11-13 23:23:31 +0000372 trace_bounds.end_ts - trace_bounds.start_ts) cr,
Zim56f67dc2023-08-27 01:50:30 +0100373 trace_bounds WHERE name IS NOT NULL
Zim497e9092023-08-01 02:09:18 +0100374 `,
Lalit Maganti52826e82024-04-02 14:16:12 +0100375 columns: sliceColumnNames,
376 },
377 `${this.state?.thread?.name}`,
378 sliceColumns,
379 sliceColumnNames,
380 ),
381 ),
Steve Golton7e63f422024-03-18 14:17:32 +0000382 }),
383 ];
Alexander Timin9075a352023-06-19 18:26:48 +0100384 }
385
Alexander Timin00035a02023-03-17 15:32:45 +0000386 isLoading() {
Alexander Timin9075a352023-06-19 18:26:48 +0100387 return this.state === undefined || this.relatedStates === undefined;
Alexander Timin00035a02023-03-17 15:32:45 +0000388 }
Alexander Timin332f29a2023-02-21 17:48:05 +0000389}