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