blob: 99191fe2bfb85072dfa9a3c2a2a3ee72be05c3c8 [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 * as m from 'mithril';
import {Actions} from '../common/actions';
import {drawDoubleHeadedArrow} from '../common/canvas_utils';
import {translateState} from '../common/thread_state';
import {timeToCode} from '../common/time';
import {globals} from './globals';
import {Panel, PanelSize} from './panel';
export class SliceDetailsPanel extends Panel {
view() {
const sliceInfo = globals.sliceDetails;
if (sliceInfo.utid === undefined) return;
const threadInfo = globals.threads.get(sliceInfo.utid);
if (threadInfo && sliceInfo.ts !== undefined &&
sliceInfo.dur !== undefined) {
return m(
'.details-panel',
m('.details-panel-heading', `Slice Details:`),
m(
'.details-table',
[m('table',
[
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',
{
onclick: () => this.goToThread(),
title: 'Go to thread'
},
'call_made'))),
m('tr',
m('th', `Start time`),
m('td', `${timeToCode(sliceInfo.ts)}`)),
m('tr',
m('th', `Duration`),
m('td', `${timeToCode(sliceInfo.dur)}`)),
m('tr', m('th', `Prio`), m('td', `${sliceInfo.priority}`)),
m('tr',
m('th', `End State`),
m('td', `${translateState(sliceInfo.endState)}`))
])],
));
} else {
return m(
'.details-panel',
m(
'.details-panel-heading',
`Slice Details:`,
));
}
}
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;
}
globals.makeSelection(Actions.selectThreadState({
utid: threadInfo.utid,
ts: sliceInfo.ts + globals.state.traceTime.startSec,
dur: sliceInfo.dur,
state: 'Running',
cpu: sliceInfo.cpu,
}));
let trackId: string|number|undefined;
let trackGroupId;
for (const track of Object.values(globals.state.tracks)) {
if (track.kind === 'ThreadStateTrack' &&
(track.config as {utid: number}).utid === threadInfo.utid) {
trackGroupId = track.trackGroup;
trackId = track.id;
}
}
// After the track exists in the dom, it will be scrolled to.
globals.frontendLocalState.scrollToTrackId = trackId;
if (trackGroupId && globals.state.trackGroups[trackGroupId].collapsed) {
globals.dispatch(Actions.toggleTrackGroupCollapsed({trackGroupId}));
}
}
renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
const details = globals.sliceDetails;
// Show expanded details on the scheduling of the currently selected slice.
if (details.wakeupTs && details.wakerUtid !== undefined) {
const threadInfo = globals.threads.get(details.wakerUtid);
// Draw separation line.
ctx.fillStyle = '#3c4b5d';
ctx.fillRect(size.width / 2, 10, 1, size.height - 10);
ctx.font = '16px Google Sans';
ctx.fillText('Scheduling Latency:', size.width / 2 + 30, 30);
// Draw diamond and vertical line.
const startDraw = {x: size.width / 2 + 30, y: 52};
ctx.beginPath();
ctx.moveTo(startDraw.x, startDraw.y + 28);
ctx.fillStyle = 'black';
ctx.lineTo(startDraw.x + 6, startDraw.y + 20);
ctx.lineTo(startDraw.x, startDraw.y + 12);
ctx.lineTo(startDraw.x - 6, startDraw.y + 20);
ctx.fill();
ctx.closePath();
ctx.fillRect(startDraw.x - 1, startDraw.y, 2, 100);
// Wakeup explanation text.
ctx.font = '13px Google Sans';
ctx.fillStyle = '#3c4b5d';
if (threadInfo) {
const displayText = `Wakeup @ ${
timeToCode(
details.wakeupTs - globals.state.traceTime.startSec)} on CPU ${
details.wakerCpu} by`;
const processText = `P: ${threadInfo.procName} [${threadInfo.pid}]`;
const threadText = `T: ${threadInfo.threadName} [${threadInfo.tid}]`;
ctx.fillText(displayText, startDraw.x + 20, startDraw.y + 20);
ctx.fillText(processText, startDraw.x + 20, startDraw.y + 37);
ctx.fillText(threadText, startDraw.x + 20, startDraw.y + 55);
}
// Draw latency arrow and explanation text.
drawDoubleHeadedArrow(ctx, startDraw.x, startDraw.y + 80, 60, true);
if (details.ts) {
const displayLatency = `Scheduling latency: ${
timeToCode(
details.ts -
(details.wakeupTs - globals.state.traceTime.startSec))}`;
ctx.fillText(displayLatency, startDraw.x + 70, startDraw.y + 86);
const explain1 =
'This is the interval from when the task became eligible to run';
const explain2 =
'(e.g. because of notifying a wait queue it was suspended on) to';
const explain3 = 'when it started running.';
ctx.font = '10px Google Sans';
ctx.fillText(explain1, startDraw.x + 70, startDraw.y + 86 + 16);
ctx.fillText(explain2, startDraw.x + 70, startDraw.y + 86 + 16 + 12);
ctx.fillText(explain3, startDraw.x + 70, startDraw.y + 86 + 16 + 24);
}
}
}
}