1// Copyright (C) 2024 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this 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 {Duration, duration, Time, time} from '../../base/time'; 16import {LONG, NUM, STR_NULL} from '../../trace_processor/query_result'; 17import m from 'mithril'; 18import {DetailsShell} from '../../widgets/details_shell'; 19import {GridLayout} from '../../widgets/grid_layout'; 20import {Section} from '../../widgets/section'; 21import {Tree, TreeNode} from '../../widgets/tree'; 22import {Timestamp} from '../../components/widgets/timestamp'; 23import {DurationWidget} from '../../components/widgets/duration'; 24import {Anchor} from '../../widgets/anchor'; 25import {Engine} from '../../trace_processor/engine'; 26import {TrackEventDetailsPanel} from '../../public/details_panel'; 27import {TrackEventSelection} from '../../public/selection'; 28import {Trace} from '../../public/trace'; 29import {ThreadMap} from '../dev.perfetto.Thread/threads'; 30 31interface SuspendResumeEventDetails { 32 ts: time; 33 dur: duration; 34 utid: number; 35 cpu: number; 36 event_type: string; 37 device_name: string; 38 driver_name: string; 39 callback_phase: string; 40 thread_state_id: number; 41} 42 43export class SuspendResumeDetailsPanel implements TrackEventDetailsPanel { 44 private suspendResumeEventDetails?: SuspendResumeEventDetails; 45 46 constructor( 47 private readonly trace: Trace, 48 private readonly threads: ThreadMap, 49 ) {} 50 51 async load({eventId}: TrackEventSelection) { 52 this.suspendResumeEventDetails = await loadSuspendResumeEventDetails( 53 this.trace.engine, 54 eventId, 55 ); 56 } 57 58 render() { 59 const eventDetails = this.suspendResumeEventDetails; 60 if (eventDetails) { 61 const threadInfo = this.threads.get(eventDetails.utid); 62 if (!threadInfo) { 63 return null; 64 } 65 return m( 66 DetailsShell, 67 {title: 'Suspend / Resume Event'}, 68 m( 69 GridLayout, 70 m( 71 Section, 72 {title: 'Properties'}, 73 m( 74 Tree, 75 m(TreeNode, { 76 left: 'Device Name', 77 right: eventDetails.device_name, 78 }), 79 m(TreeNode, { 80 left: 'Start time', 81 right: m(Timestamp, {ts: eventDetails.ts}), 82 }), 83 m(TreeNode, { 84 left: 'Duration', 85 right: m(DurationWidget, {dur: eventDetails.dur}), 86 }), 87 m(TreeNode, { 88 left: 'Driver Name', 89 right: eventDetails.driver_name, 90 }), 91 m(TreeNode, { 92 left: 'Callback Phase', 93 right: eventDetails.callback_phase, 94 }), 95 m(TreeNode, { 96 left: 'Thread', 97 right: m( 98 Anchor, 99 { 100 icon: 'call_made', 101 onclick: () => { 102 this.goToThread(eventDetails.thread_state_id); 103 }, 104 }, 105 `${threadInfo.threadName} [${threadInfo.tid}]`, 106 ), 107 }), 108 m(TreeNode, {left: 'CPU', right: eventDetails.cpu}), 109 m(TreeNode, {left: 'Event Type', right: eventDetails.event_type}), 110 ), 111 ), 112 ), 113 ); 114 } else { 115 return m(DetailsShell, { 116 title: 'Suspend / Resume Event', 117 description: 'Loading...', 118 }); 119 } 120 } 121 122 isLoading(): boolean { 123 return this.suspendResumeEventDetails === undefined; 124 } 125 126 goToThread(threadStateId: number) { 127 this.trace.selection.selectSqlEvent('thread_state', threadStateId, { 128 scrollToSelection: true, 129 }); 130 } 131} 132 133async function loadSuspendResumeEventDetails( 134 engine: Engine, 135 id: number, 136): Promise<SuspendResumeEventDetails> { 137 const suspendResumeDetailsQuery = ` 138 SELECT 139 ts, 140 dur, 141 EXTRACT_ARG(arg_set_id, 'utid') as utid, 142 EXTRACT_ARG(arg_set_id, 'cpu') as cpu, 143 EXTRACT_ARG(arg_set_id, 'event_type') as event_type, 144 EXTRACT_ARG(arg_set_id, 'device_name') as device_name, 145 EXTRACT_ARG(arg_set_id, 'driver_name') as driver_name, 146 EXTRACT_ARG(arg_set_id, 'callback_phase') as callback_phase 147 FROM slice 148 WHERE slice_id = ${id}; 149 `; 150 151 const suspendResumeDetailsResult = await engine.query( 152 suspendResumeDetailsQuery, 153 ); 154 const suspendResumeEventRow = suspendResumeDetailsResult.iter({ 155 ts: LONG, 156 dur: LONG, 157 utid: NUM, 158 cpu: NUM, 159 event_type: STR_NULL, 160 device_name: STR_NULL, 161 driver_name: STR_NULL, 162 callback_phase: STR_NULL, 163 }); 164 if (!suspendResumeEventRow.valid()) { 165 return { 166 ts: Time.fromRaw(0n), 167 dur: Duration.fromRaw(0n), 168 utid: 0, 169 cpu: 0, 170 event_type: 'Error', 171 device_name: 'Error', 172 driver_name: 'Error', 173 callback_phase: 'Error', 174 thread_state_id: 0, 175 }; 176 } 177 178 const threadStateQuery = ` 179 SELECT t.id as threadStateId 180 FROM thread_state t 181 WHERE 182 t.utid = ${suspendResumeEventRow.utid} 183 AND t.ts <= ${suspendResumeEventRow.ts} 184 AND t.ts + t.dur > ${suspendResumeEventRow.ts}; 185 `; 186 const threadStateResult = await engine.query(threadStateQuery); 187 let threadStateId = 0; 188 if (threadStateResult.numRows() > 0) { 189 const threadStateRow = threadStateResult.firstRow({ 190 threadStateId: NUM, 191 }); 192 threadStateId = threadStateRow.threadStateId; 193 } 194 195 return { 196 ts: Time.fromRaw(suspendResumeEventRow.ts), 197 dur: Duration.fromRaw(suspendResumeEventRow.dur), 198 utid: suspendResumeEventRow.utid, 199 cpu: suspendResumeEventRow.cpu, 200 event_type: 201 suspendResumeEventRow.event_type !== null 202 ? suspendResumeEventRow.event_type 203 : 'N/A', 204 device_name: 205 suspendResumeEventRow.device_name !== null 206 ? suspendResumeEventRow.device_name 207 : 'N/A', 208 driver_name: 209 suspendResumeEventRow.driver_name !== null 210 ? suspendResumeEventRow.driver_name 211 : 'N/A', 212 callback_phase: 213 suspendResumeEventRow.callback_phase !== null 214 ? suspendResumeEventRow.callback_phase 215 : 'N/A', 216 thread_state_id: threadStateId, 217 }; 218} 219