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 m from 'mithril'; 16 17import {Actions} from '../common/actions'; 18import {translateState} from '../common/thread_state'; 19import {tpTimeToCode} from '../common/time'; 20import {globals, SliceDetails, ThreadDesc} from './globals'; 21import {scrollToTrackAndTs} from './scroll_helper'; 22import {SlicePanel} from './slice_panel'; 23 24export class SliceDetailsPanel extends SlicePanel { 25 view() { 26 const sliceInfo = globals.sliceDetails; 27 if (sliceInfo.utid === undefined) return; 28 const threadInfo = globals.threads.get(sliceInfo.utid); 29 30 return m( 31 '.details-panel', 32 m( 33 '.details-panel-heading', 34 m('h2.split', `Slice Details`), 35 this.hasSchedLatencyInfo(sliceInfo) && 36 m('h2.split', 'Scheduling Latency'), 37 ), 38 this.renderDetails(sliceInfo, threadInfo)); 39 } 40 41 private renderSchedLatencyInfo(sliceInfo: SliceDetails): m.Children { 42 if (!this.hasSchedLatencyInfo(sliceInfo)) { 43 return null; 44 } 45 return m( 46 '.half-width-panel.slice-details-latency-panel', 47 m('img.slice-details-image', { 48 src: `${globals.root}assets/scheduling_latency.png`, 49 }), 50 this.renderWakeupText(sliceInfo), 51 this.renderDisplayLatencyText(sliceInfo), 52 ); 53 } 54 55 private renderWakeupText(sliceInfo: SliceDetails): m.Children { 56 if (sliceInfo.wakerUtid === undefined) { 57 return null; 58 } 59 const threadInfo = globals.threads.get(sliceInfo.wakerUtid!); 60 if (!threadInfo) { 61 return null; 62 } 63 const timestamp = 64 tpTimeToCode(sliceInfo.wakeupTs! - globals.state.traceTime.start); 65 return m( 66 '.slice-details-wakeup-text', 67 m('', `Wakeup @ ${timestamp} on CPU ${sliceInfo.wakerCpu} by`), 68 m('', `P: ${threadInfo.procName} [${threadInfo.pid}]`), 69 m('', `T: ${threadInfo.threadName} [${threadInfo.tid}]`), 70 ); 71 } 72 73 private renderDisplayLatencyText(sliceInfo: SliceDetails): m.Children { 74 if (sliceInfo.ts === undefined || sliceInfo.wakeupTs === undefined) { 75 return null; 76 } 77 78 const latency = tpTimeToCode(sliceInfo.ts - sliceInfo.wakeupTs); 79 return m( 80 '.slice-details-latency-text', 81 m('', `Scheduling latency: ${latency}`), 82 m('.text-detail', 83 `This is the interval from when the task became eligible to run 84 (e.g. because of notifying a wait queue it was suspended on) to 85 when it started running.`), 86 ); 87 } 88 89 private hasSchedLatencyInfo({wakeupTs, wakerUtid}: SliceDetails): boolean { 90 return wakeupTs !== undefined && wakerUtid !== undefined; 91 } 92 93 private renderDetails(sliceInfo: SliceDetails, threadInfo?: ThreadDesc): 94 m.Children { 95 if (!threadInfo || sliceInfo.ts === undefined || 96 sliceInfo.dur === undefined) { 97 return null; 98 } else { 99 const tableRows = [ 100 m('tr', 101 m('th', `Process`), 102 m('td', `${threadInfo.procName} [${threadInfo.pid}]`)), 103 m('tr', 104 m('th', `Thread`), 105 m('td', 106 `${threadInfo.threadName} [${threadInfo.tid}]`, 107 m('i.material-icons.grey', 108 {onclick: () => this.goToThread(), title: 'Go to thread'}, 109 'call_made'))), 110 m('tr', m('th', `Cmdline`), m('td', threadInfo.cmdline)), 111 m('tr', 112 m('th', `Start time`), 113 m('td', 114 `${tpTimeToCode(sliceInfo.ts - globals.state.traceTime.start)}`)), 115 m('tr', 116 m('th', `Duration`), 117 m('td', this.computeDuration(sliceInfo.ts, sliceInfo.dur))), 118 (sliceInfo.threadDur === undefined || 119 sliceInfo.threadTs === undefined) ? 120 '' : 121 m('tr', 122 m('th', 'Thread duration'), 123 m('td', 124 this.computeDuration(sliceInfo.threadTs, sliceInfo.threadDur))), 125 m('tr', m('th', `Prio`), m('td', `${sliceInfo.priority}`)), 126 m('tr', 127 m('th', `End State`), 128 m('td', translateState(sliceInfo.endState))), 129 m('tr', 130 m('th', `Slice ID`), 131 m('td', 132 (sliceInfo.id !== undefined) ? sliceInfo.id.toString() : 133 'Unknown')), 134 ]; 135 136 for (const [key, value] of this.getProcessThreadDetails(sliceInfo)) { 137 if (value !== undefined) { 138 tableRows.push(m('tr', m('th', key), m('td', value))); 139 } 140 } 141 142 return m( 143 '.details-table-multicolumn', 144 m('table.half-width-panel', tableRows), 145 this.renderSchedLatencyInfo(sliceInfo), 146 ); 147 } 148 } 149 150 goToThread() { 151 const sliceInfo = globals.sliceDetails; 152 if (sliceInfo.utid === undefined) return; 153 const threadInfo = globals.threads.get(sliceInfo.utid); 154 155 if (sliceInfo.id === undefined || sliceInfo.ts === undefined || 156 sliceInfo.dur === undefined || sliceInfo.cpu === undefined || 157 threadInfo === undefined) { 158 return; 159 } 160 161 let trackId: string|number|undefined; 162 for (const track of Object.values(globals.state.tracks)) { 163 if (track.kind === 'ThreadStateTrack' && 164 (track.config as {utid: number}).utid === threadInfo.utid) { 165 trackId = track.id; 166 } 167 } 168 169 if (trackId && sliceInfo.threadStateId) { 170 globals.makeSelection(Actions.selectThreadState({ 171 id: sliceInfo.threadStateId, 172 trackId: trackId.toString(), 173 })); 174 175 scrollToTrackAndTs(trackId, sliceInfo.ts, true); 176 } 177 } 178 179 renderCanvas() {} 180} 181