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