blob: b08610489193d73e7a42653c4360450d3da17804 [file] [log] [blame]
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use size file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import m from 'mithril';
import {TPTime, tpTimeToCode} from '../common/time';
import {Anchor} from './anchor';
import {BottomTab, bottomTabRegistry, NewBottomTabArgs} from './bottom_tab';
import {globals} from './globals';
import {asTPTimestamp, SchedSqlId, ThreadStateSqlId} from './sql_types';
import {
getProcessName,
getThreadName,
ThreadInfo,
} from './thread_and_process_info';
import {getThreadState, goToSchedSlice, ThreadState} from './thread_state';
import {DetailsShell} from './widgets/details_shell';
import {GridLayout} from './widgets/grid_layout';
import {Section} from './widgets/section';
import {SqlRef} from './widgets/sql_ref';
import {Timestamp} from './widgets/timestamp';
import {Tree, TreeNode} from './widgets/tree';
interface ThreadStateTabConfig {
// Id into |thread_state| sql table.
readonly id: ThreadStateSqlId;
}
export class ThreadStateTab extends BottomTab<ThreadStateTabConfig> {
static readonly kind = 'org.perfetto.ThreadStateTab';
state?: ThreadState;
loaded: boolean = false;
static create(args: NewBottomTabArgs): ThreadStateTab {
return new ThreadStateTab(args);
}
constructor(args: NewBottomTabArgs) {
super(args);
getThreadState(this.engine, this.config.id).then((state?: ThreadState) => {
this.loaded = true;
this.state = state;
globals.rafScheduler.scheduleFullRedraw();
});
}
getTitle() {
// TODO(altimin): Support dynamic titles here.
return 'Current Selection';
}
viewTab() {
// TODO(altimin/stevegolton): Differentiate between "Current Selection" and
// "Pinned" views in DetailsShell.
return m(
DetailsShell,
{title: 'Thread State', description: this.renderLoadingText()},
m(GridLayout,
m(
Section,
{title: 'Details'},
this.state && this.renderTree(this.state),
)),
);
}
private renderLoadingText() {
if (!this.loaded) {
return 'Loading';
}
if (!this.state) {
return `Thread state ${this.config.id} does not exist`;
}
// TODO(stevegolton): Return something intelligent here.
return this.config.id;
}
private renderTree(state: ThreadState) {
const thread = state.thread;
const process = state.thread?.process;
return m(
Tree,
m(TreeNode, {
left: 'Start time',
right: m(Timestamp, {ts: asTPTimestamp(state.ts)}),
}),
m(TreeNode, {
left: 'Duration',
right: tpTimeToCode(state.dur),
}),
m(TreeNode, {
left: 'State',
right: this.renderState(
state.state, state.cpu, state.schedSqlId, state.ts),
}),
state.blockedFunction && m(TreeNode, {
left: 'Blocked function',
right: state.blockedFunction,
}),
process && m(TreeNode, {
left: 'Process',
right: getProcessName(process),
}),
thread && m(TreeNode, {left: 'Thread', right: getThreadName(thread)}),
state.wakerThread && this.renderWakerThread(state.wakerThread),
m(TreeNode, {
left: 'SQL ID',
right: m(SqlRef, {table: 'thread_state', id: state.threadStateSqlId}),
}),
);
}
private renderState(
state: string, cpu: number|undefined, id: SchedSqlId|undefined,
ts: TPTime): m.Children {
if (!state) {
return null;
}
if (id === undefined || cpu === undefined) {
return state;
}
return m(
Anchor,
{
title: 'Go to CPU slice',
icon: 'call_made',
onclick: () => goToSchedSlice(cpu, id, ts),
},
`${state} on CPU ${cpu}`);
}
private renderWakerThread(wakerThread: ThreadInfo) {
return m(
TreeNode,
{left: 'Waker'},
m(TreeNode,
{left: 'Process', right: getProcessName(wakerThread.process)}),
m(TreeNode, {left: 'Thread', right: getThreadName(wakerThread)}),
);
}
isLoading() {
return this.state === undefined;
}
renderTabCanvas(): void {}
}
bottomTabRegistry.register(ThreadStateTab);