1// Copyright (C) 2019 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 * as m from 'mithril';
16
17import {Actions} from '../common/actions';
18import {drawDoubleHeadedArrow} from '../common/canvas_utils';
19import {translateState} from '../common/thread_state';
20import {timeToCode, toNs} from '../common/time';
21
22import {globals, SliceDetails, ThreadDesc} from './globals';
23import {Panel, PanelSize} from './panel';
24import {scrollToTrackAndTs} from './scroll_helper';
25
26export class SliceDetailsPanel extends Panel {
27  view() {
28    const sliceInfo = globals.sliceDetails;
29    if (sliceInfo.utid === undefined) return;
30    const threadInfo = globals.threads.get(sliceInfo.utid);
31
32    return m(
33        '.details-panel',
34        m('.details-panel-heading',
35          m('h2.split', `Slice Details`),
36          (sliceInfo.wakeupTs && sliceInfo.wakerUtid) ?
37              m('h2.split', 'Scheduling Latency') :
38              ''),
39        this.getDetails(sliceInfo, threadInfo));
40  }
41
42  getDetails(sliceInfo: SliceDetails, threadInfo: ThreadDesc|undefined) {
43    if (!threadInfo || sliceInfo.ts === undefined ||
44        sliceInfo.dur === undefined) {
45      return null;
46    } else {
47      return m(
48          '.details-table',
49          m('table.half-width',
50            [
51              m('tr',
52                m('th', `Process`),
53                m('td', `${threadInfo.procName} [${threadInfo.pid}]`)),
54              m('tr',
55                m('th', `Thread`),
56                m('td',
57                  `${threadInfo.threadName} [${threadInfo.tid}]`,
58                  m('i.material-icons.grey',
59                    {onclick: () => this.goToThread(), title: 'Go to thread'},
60                    'call_made'))),
61              m('tr',
62                m('th', `Start time`),
63                m('td', `${timeToCode(sliceInfo.ts)}`)),
64              m('tr',
65                m('th', `Duration`),
66                m('td', `${timeToCode(sliceInfo.dur)}`)),
67              m('tr', m('th', `Prio`), m('td', `${sliceInfo.priority}`)),
68              m('tr',
69                m('th', `End State`),
70                m('td', `${translateState(sliceInfo.endState)}`))
71            ]),
72      );
73    }
74  }
75
76  goToThread() {
77    const sliceInfo = globals.sliceDetails;
78    if (sliceInfo.utid === undefined) return;
79    const threadInfo = globals.threads.get(sliceInfo.utid);
80
81    if (sliceInfo.id === undefined || sliceInfo.ts === undefined ||
82        sliceInfo.dur === undefined || sliceInfo.cpu === undefined ||
83        threadInfo === undefined) {
84      return;
85    }
86
87    let trackId: string|number|undefined;
88    for (const track of Object.values(globals.state.tracks)) {
89      if (track.kind === 'ThreadStateTrack' &&
90          (track.config as {utid: number}).utid === threadInfo.utid) {
91        trackId = track.id;
92      }
93    }
94
95    if (trackId) {
96      globals.makeSelection(Actions.selectThreadState({
97        utid: threadInfo.utid,
98        ts: sliceInfo.ts + globals.state.traceTime.startSec,
99        dur: sliceInfo.dur,
100        state: 'Running',
101        cpu: sliceInfo.cpu,
102        trackId: trackId.toString(),
103      }));
104
105      scrollToTrackAndTs(
106          trackId, toNs(sliceInfo.ts + globals.state.traceTime.startSec), true);
107    }
108  }
109
110
111  renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
112    const details = globals.sliceDetails;
113    // Show expanded details on the scheduling of the currently selected slice.
114    if (details.wakeupTs && details.wakerUtid !== undefined) {
115      const threadInfo = globals.threads.get(details.wakerUtid);
116      // Draw diamond and vertical line.
117      const startDraw = {x: size.width / 2 + 20, y: 52};
118      ctx.beginPath();
119      ctx.moveTo(startDraw.x, startDraw.y + 28);
120      ctx.fillStyle = 'black';
121      ctx.lineTo(startDraw.x + 6, startDraw.y + 20);
122      ctx.lineTo(startDraw.x, startDraw.y + 12);
123      ctx.lineTo(startDraw.x - 6, startDraw.y + 20);
124      ctx.fill();
125      ctx.closePath();
126      ctx.fillRect(startDraw.x - 1, startDraw.y, 2, 100);
127
128      // Wakeup explanation text.
129      ctx.font = '13px Roboto Condensed';
130      ctx.fillStyle = '#3c4b5d';
131      if (threadInfo) {
132        const displayText = `Wakeup @ ${
133            timeToCode(
134                details.wakeupTs - globals.state.traceTime.startSec)} on CPU ${
135            details.wakerCpu} by`;
136        const processText = `P: ${threadInfo.procName} [${threadInfo.pid}]`;
137        const threadText = `T: ${threadInfo.threadName} [${threadInfo.tid}]`;
138        ctx.fillText(displayText, startDraw.x + 20, startDraw.y + 20);
139        ctx.fillText(processText, startDraw.x + 20, startDraw.y + 37);
140        ctx.fillText(threadText, startDraw.x + 20, startDraw.y + 55);
141      }
142
143      // Draw latency arrow and explanation text.
144      drawDoubleHeadedArrow(ctx, startDraw.x, startDraw.y + 80, 60, true);
145      if (details.ts) {
146        const displayLatency = `Scheduling latency: ${
147            timeToCode(
148                details.ts -
149                (details.wakeupTs - globals.state.traceTime.startSec))}`;
150        ctx.fillText(displayLatency, startDraw.x + 70, startDraw.y + 86);
151        const explain1 =
152            'This is the interval from when the task became eligible to run';
153        const explain2 =
154            '(e.g. because of notifying a wait queue it was suspended on) to';
155        const explain3 = 'when it started running.';
156        ctx.font = '10px Roboto Condensed';
157        ctx.fillText(explain1, startDraw.x + 70, startDraw.y + 86 + 16);
158        ctx.fillText(explain2, startDraw.x + 70, startDraw.y + 86 + 16 + 12);
159        ctx.fillText(explain3, startDraw.x + 70, startDraw.y + 86 + 16 + 24);
160      }
161    }
162  }
163}
164