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', m('th', `Cmdline`), m('td', threadInfo.cmdline)),
62              m('tr',
63                m('th', `Start time`),
64                m('td', `${timeToCode(sliceInfo.ts)}`)),
65              m('tr',
66                m('th', `Duration`),
67                m('td', `${timeToCode(sliceInfo.dur)}`)),
68              m('tr', m('th', `Prio`), m('td', `${sliceInfo.priority}`)),
69              m('tr',
70                m('th', `End State`),
71                m('td', translateState(sliceInfo.endState)))
72            ]),
73      );
74    }
75  }
76
77  goToThread() {
78    const sliceInfo = globals.sliceDetails;
79    if (sliceInfo.utid === undefined) return;
80    const threadInfo = globals.threads.get(sliceInfo.utid);
81
82    if (sliceInfo.id === undefined || sliceInfo.ts === undefined ||
83        sliceInfo.dur === undefined || sliceInfo.cpu === undefined ||
84        threadInfo === undefined) {
85      return;
86    }
87
88    let trackId: string|number|undefined;
89    for (const track of Object.values(globals.state.tracks)) {
90      if (track.kind === 'ThreadStateTrack' &&
91          (track.config as {utid: number}).utid === threadInfo.utid) {
92        trackId = track.id;
93      }
94    }
95
96    if (trackId && sliceInfo.threadStateId) {
97      globals.makeSelection(Actions.selectThreadState({
98        id: sliceInfo.threadStateId,
99        trackId: trackId.toString(),
100      }));
101
102      scrollToTrackAndTs(
103          trackId, toNs(sliceInfo.ts + globals.state.traceTime.startSec), true);
104    }
105  }
106
107
108  renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
109    const details = globals.sliceDetails;
110    // Show expanded details on the scheduling of the currently selected slice.
111    if (details.wakeupTs && details.wakerUtid !== undefined) {
112      const threadInfo = globals.threads.get(details.wakerUtid);
113      // Draw diamond and vertical line.
114      const startDraw = {x: size.width / 2 + 20, y: 52};
115      ctx.beginPath();
116      ctx.moveTo(startDraw.x, startDraw.y + 28);
117      ctx.fillStyle = 'black';
118      ctx.lineTo(startDraw.x + 6, startDraw.y + 20);
119      ctx.lineTo(startDraw.x, startDraw.y + 12);
120      ctx.lineTo(startDraw.x - 6, startDraw.y + 20);
121      ctx.fill();
122      ctx.closePath();
123      ctx.fillRect(startDraw.x - 1, startDraw.y, 2, 100);
124
125      // Wakeup explanation text.
126      ctx.font = '13px Roboto Condensed';
127      ctx.fillStyle = '#3c4b5d';
128      if (threadInfo) {
129        const displayText = `Wakeup @ ${
130            timeToCode(
131                details.wakeupTs - globals.state.traceTime.startSec)} on CPU ${
132            details.wakerCpu} by`;
133        const processText = `P: ${threadInfo.procName} [${threadInfo.pid}]`;
134        const threadText = `T: ${threadInfo.threadName} [${threadInfo.tid}]`;
135        ctx.fillText(displayText, startDraw.x + 20, startDraw.y + 20);
136        ctx.fillText(processText, startDraw.x + 20, startDraw.y + 37);
137        ctx.fillText(threadText, startDraw.x + 20, startDraw.y + 55);
138      }
139
140      // Draw latency arrow and explanation text.
141      drawDoubleHeadedArrow(ctx, startDraw.x, startDraw.y + 80, 60, true);
142      if (details.ts) {
143        const displayLatency = `Scheduling latency: ${
144            timeToCode(
145                details.ts -
146                (details.wakeupTs - globals.state.traceTime.startSec))}`;
147        ctx.fillText(displayLatency, startDraw.x + 70, startDraw.y + 86);
148        const explain1 =
149            'This is the interval from when the task became eligible to run';
150        const explain2 =
151            '(e.g. because of notifying a wait queue it was suspended on) to';
152        const explain3 = 'when it started running.';
153        ctx.font = '10px Roboto Condensed';
154        ctx.fillText(explain1, startDraw.x + 70, startDraw.y + 86 + 16);
155        ctx.fillText(explain2, startDraw.x + 70, startDraw.y + 86 + 16 + 12);
156        ctx.fillText(explain3, startDraw.x + 70, startDraw.y + 86 + 16 + 24);
157      }
158    }
159  }
160}
161