blob: 9b018dd9babfdb361084e9406895e292cb90fe44 [file] [log] [blame]
// Copyright (C) 2019 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 {Actions} from '../common/actions';
import {translateState} from '../common/thread_state';
import {timeToCode, toNs} from '../common/time';
import {globals, SliceDetails, ThreadDesc} from './globals';
import {scrollToTrackAndTs} from './scroll_helper';
import {SlicePanel} from './slice_panel';
export class SliceDetailsPanel extends SlicePanel {
view() {
const sliceInfo = globals.sliceDetails;
if (sliceInfo.utid === undefined) return;
const threadInfo = globals.threads.get(sliceInfo.utid);
return m(
'.details-panel',
m(
'.details-panel-heading',
m('h2.split', `Slice Details`),
this.hasSchedLatencyInfo(sliceInfo) &&
m('h2.split', 'Scheduling Latency'),
),
this.renderDetails(sliceInfo, threadInfo));
}
private renderSchedLatencyInfo(sliceInfo: SliceDetails): m.Children {
if (!this.hasSchedLatencyInfo(sliceInfo)) {
return null;
}
return m(
'.half-width-panel.slice-details-latency-panel',
m('img.slice-details-image', {
src: `${globals.root}assets/scheduling_latency.png`,
}),
this.renderWakeupText(sliceInfo),
this.renderDisplayLatencyText(sliceInfo),
);
}
private renderWakeupText(sliceInfo: SliceDetails): m.Children {
if (sliceInfo.wakerUtid === undefined) {
return null;
}
const threadInfo = globals.threads.get(sliceInfo.wakerUtid!);
if (!threadInfo) {
return null;
}
const timestamp = timeToCode(
sliceInfo.wakeupTs! - globals.state.traceTime.startSec,
);
return m(
'.slice-details-wakeup-text',
m('', `Wakeup @ ${timestamp} on CPU ${sliceInfo.wakerCpu} by`),
m('', `P: ${threadInfo.procName} [${threadInfo.pid}]`),
m('', `T: ${threadInfo.threadName} [${threadInfo.tid}]`),
);
}
private renderDisplayLatencyText(sliceInfo: SliceDetails): m.Children {
if (sliceInfo.ts === undefined || sliceInfo.wakeupTs === undefined) {
return null;
}
const latency = timeToCode(
sliceInfo.ts - (sliceInfo.wakeupTs - globals.state.traceTime.startSec),
);
return m(
'.slice-details-latency-text',
m('', `Scheduling latency: ${latency}`),
m('.text-detail',
`This is the interval from when the task became eligible to run
(e.g. because of notifying a wait queue it was suspended on) to
when it started running.`),
);
}
private hasSchedLatencyInfo({wakeupTs, wakerUtid}: SliceDetails): boolean {
return wakeupTs !== undefined && wakerUtid !== undefined;
}
private renderDetails(sliceInfo: SliceDetails, threadInfo?: ThreadDesc):
m.Children {
if (!threadInfo || sliceInfo.ts === undefined ||
sliceInfo.dur === undefined) {
return null;
} else {
const tableRows = [
m('tr',
m('th', `Process`),
m('td', `${threadInfo.procName} [${threadInfo.pid}]`)),
m('tr',
m('th', `Thread`),
m('td',
`${threadInfo.threadName} [${threadInfo.tid}]`,
m('i.material-icons.grey',
{onclick: () => this.goToThread(), title: 'Go to thread'},
'call_made'))),
m('tr', m('th', `Cmdline`), m('td', threadInfo.cmdline)),
m('tr', m('th', `Start time`), m('td', `${timeToCode(sliceInfo.ts)}`)),
m('tr',
m('th', `Duration`),
m('td', this.computeDuration(sliceInfo.ts, sliceInfo.dur))),
(sliceInfo.threadDur === undefined ||
sliceInfo.threadTs === undefined) ?
'' :
m('tr',
m('th', 'Thread duration'),
m('td',
this.computeDuration(sliceInfo.threadTs, sliceInfo.threadDur))),
m('tr', m('th', `Prio`), m('td', `${sliceInfo.priority}`)),
m('tr',
m('th', `End State`),
m('td', translateState(sliceInfo.endState))),
m('tr',
m('th', `Slice ID`),
m('td',
(sliceInfo.id !== undefined) ? sliceInfo.id.toString() :
'Unknown')),
];
for (const [key, value] of this.getProcessThreadDetails(sliceInfo)) {
if (value !== undefined) {
tableRows.push(m('tr', m('th', key), m('td', value)));
}
}
return m(
'.details-table-multicolumn',
m('table.half-width-panel', tableRows),
this.renderSchedLatencyInfo(sliceInfo),
);
}
}
goToThread() {
const sliceInfo = globals.sliceDetails;
if (sliceInfo.utid === undefined) return;
const threadInfo = globals.threads.get(sliceInfo.utid);
if (sliceInfo.id === undefined || sliceInfo.ts === undefined ||
sliceInfo.dur === undefined || sliceInfo.cpu === undefined ||
threadInfo === undefined) {
return;
}
let trackId: string|number|undefined;
for (const track of Object.values(globals.state.tracks)) {
if (track.kind === 'ThreadStateTrack' &&
(track.config as {utid: number}).utid === threadInfo.utid) {
trackId = track.id;
}
}
if (trackId && sliceInfo.threadStateId) {
globals.makeSelection(Actions.selectThreadState({
id: sliceInfo.threadStateId,
trackId: trackId.toString(),
}));
scrollToTrackAndTs(
trackId, toNs(sliceInfo.ts + globals.state.traceTime.startSec), true);
}
}
renderCanvas() {}
}