1// Copyright (C) 2023 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 { 16 addDebugSliceTrack, 17 Plugin, 18 PluginContextTrace, 19 PluginDescriptor, 20} from '../../public'; 21 22class AndroidPerf implements Plugin { 23 async addAppProcessStartsDebugTrack( 24 ctx: PluginContextTrace, 25 reason: string, 26 sliceName: string, 27 ): Promise<void> { 28 const sliceColumns = [ 29 'id', 30 'ts', 31 'dur', 32 'reason', 33 'process_name', 34 'intent', 35 'table_name', 36 ]; 37 await addDebugSliceTrack( 38 ctx, 39 { 40 sqlSource: ` 41 SELECT 42 start_id AS id, 43 proc_start_ts AS ts, 44 total_dur AS dur, 45 reason, 46 process_name, 47 intent, 48 'slice' AS table_name 49 FROM _android_app_process_starts 50 WHERE reason = '${reason}' 51 `, 52 columns: sliceColumns, 53 }, 54 'app_' + sliceName + '_start reason: ' + reason, 55 {ts: 'ts', dur: 'dur', name: sliceName}, 56 sliceColumns, 57 ); 58 } 59 60 async onTraceLoad(ctx: PluginContextTrace): Promise<void> { 61 ctx.registerCommand({ 62 id: 'dev.perfetto.AndroidPerf#BinderSystemServerIncoming', 63 name: 'Run query: system_server incoming binder graph', 64 callback: () => 65 ctx.tabs.openQuery( 66 `INCLUDE PERFETTO MODULE android.binder; 67 SELECT * FROM android_binder_incoming_graph((SELECT upid FROM process WHERE name = 'system_server'))`, 68 'system_server incoming binder graph', 69 ), 70 }); 71 72 ctx.registerCommand({ 73 id: 'dev.perfetto.AndroidPerf#BinderSystemServerOutgoing', 74 name: 'Run query: system_server outgoing binder graph', 75 callback: () => 76 ctx.tabs.openQuery( 77 `INCLUDE PERFETTO MODULE android.binder; 78 SELECT * FROM android_binder_outgoing_graph((SELECT upid FROM process WHERE name = 'system_server'))`, 79 'system_server outgoing binder graph', 80 ), 81 }); 82 83 ctx.registerCommand({ 84 id: 'dev.perfetto.AndroidPerf#MonitorContentionSystemServer', 85 name: 'Run query: system_server monitor_contention graph', 86 callback: () => 87 ctx.tabs.openQuery( 88 `INCLUDE PERFETTO MODULE android.monitor_contention; 89 SELECT * FROM android_monitor_contention_graph((SELECT upid FROM process WHERE name = 'system_server'))`, 90 'system_server monitor_contention graph', 91 ), 92 }); 93 94 ctx.registerCommand({ 95 id: 'dev.perfetto.AndroidPerf#BinderAll', 96 name: 'Run query: all process binder graph', 97 callback: () => 98 ctx.tabs.openQuery( 99 `INCLUDE PERFETTO MODULE android.binder; 100 SELECT * FROM android_binder_graph(-1000, 1000, -1000, 1000)`, 101 'all process binder graph', 102 ), 103 }); 104 105 ctx.registerCommand({ 106 id: 'dev.perfetto.AndroidPerf#ThreadClusterDistribution', 107 name: 'Run query: runtime cluster distribution for a thread', 108 callback: async (tid) => { 109 if (tid === undefined) { 110 tid = prompt('Enter a thread tid', ''); 111 if (tid === null) return; 112 } 113 ctx.tabs.openQuery( 114 ` 115 INCLUDE PERFETTO MODULE cpu.cpus; 116 WITH 117 total_runtime AS ( 118 SELECT sum(dur) AS total_runtime 119 FROM sched s 120 LEFT JOIN thread t 121 USING (utid) 122 WHERE t.tid = ${tid} 123 ) 124 SELECT 125 c.size AS cluster, 126 sum(dur)/1e6 AS total_dur_ms, 127 sum(dur) * 1.0 / (SELECT * FROM total_runtime) AS percentage 128 FROM sched s 129 LEFT JOIN thread t 130 USING (utid) 131 LEFT JOIN cpu_core_types c 132 ON s.cpu = c.cpu_index 133 WHERE t.tid = ${tid} 134 GROUP BY 1`, 135 `runtime cluster distrubtion for tid ${tid}`, 136 ); 137 }, 138 }); 139 140 ctx.registerCommand({ 141 id: 'dev.perfetto.AndroidPerf#SchedLatency', 142 name: 'Run query: top 50 sched latency for a thread', 143 callback: async (tid) => { 144 if (tid === undefined) { 145 tid = prompt('Enter a thread tid', ''); 146 if (tid === null) return; 147 } 148 ctx.tabs.openQuery( 149 ` 150 SELECT ts.*, t.tid, t.name, tt.id AS track_id 151 FROM thread_state ts 152 LEFT JOIN thread_track tt 153 USING (utid) 154 LEFT JOIN thread t 155 USING (utid) 156 WHERE ts.state IN ('R', 'R+') AND tid = ${tid} 157 ORDER BY dur DESC 158 LIMIT 50`, 159 `top 50 sched latency slice for tid ${tid}`, 160 ); 161 }, 162 }); 163 164 ctx.registerCommand({ 165 id: 'dev.perfetto.AndroidPerf#AppProcessStarts', 166 name: 'Add tracks: app process starts', 167 callback: async () => { 168 await ctx.engine.query( 169 `INCLUDE PERFETTO MODULE android.app_process_starts;`, 170 ); 171 172 const startReason = ['activity', 'service', 'broadcast', 'provider']; 173 for (const reason of startReason) { 174 await this.addAppProcessStartsDebugTrack(ctx, reason, 'process_name'); 175 } 176 }, 177 }); 178 179 ctx.registerCommand({ 180 id: 'dev.perfetto.AndroidPerf#AppIntentStarts', 181 name: 'Add tracks: app intent starts', 182 callback: async () => { 183 await ctx.engine.query( 184 `INCLUDE PERFETTO MODULE android.app_process_starts;`, 185 ); 186 187 const startReason = ['activity', 'service', 'broadcast']; 188 for (const reason of startReason) { 189 await this.addAppProcessStartsDebugTrack(ctx, reason, 'intent'); 190 } 191 }, 192 }); 193 } 194} 195 196export const plugin: PluginDescriptor = { 197 pluginId: 'dev.perfetto.AndroidPerf', 198 plugin: AndroidPerf, 199}; 200