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