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 {NUM, STR} from '../../trace_processor/query_result'; 16import {Trace} from '../../public/trace'; 17import {PerfettoPlugin} from '../../public/plugin'; 18import {addDebugSliceTrack} from '../../components/tracks/debug_tracks'; 19 20export default class implements PerfettoPlugin { 21 static readonly id = 'dev.perfetto.AndroidClientServer'; 22 async onTraceLoad(ctx: Trace): Promise<void> { 23 ctx.commands.registerCommand({ 24 id: 'dev.perfetto.AndroidClientServer#ThreadRuntimeIPC', 25 name: 'Show dependencies in client server model', 26 callback: async (sliceId) => { 27 if (sliceId === undefined) { 28 sliceId = prompt('Enter a slice id', ''); 29 if (sliceId === null) return; 30 } 31 await ctx.engine.query(` 32 include perfetto module android.binder; 33 include perfetto module graphs.search; 34 35 create or replace perfetto table __binder_for_slice_${sliceId} as 36 with s as materialized ( 37 select slice.id, ts, ts + dur as ts_end, dur, upid 38 from thread_slice slice 39 where slice.id = ${sliceId} 40 ), 41 child_binder_txns_for_slice as materialized ( 42 select 43 (select id from s) as source_node_id, 44 binder_txn_id as dest_node_id 45 from descendant_slice((select id from s)) as desc 46 join android_binder_txns txns on desc.id = txns.binder_txn_id 47 ), 48 binder_txns_in_slice_intervals as materialized ( 49 select 50 binder_txn_id as source_node_id, 51 binder_reply_id as dest_node_id 52 from android_binder_txns 53 where client_ts > (select ts from s) 54 and client_ts < (select ts + dur from s) 55 ), 56 nested_binder_txns_in_slice_interval as materialized ( 57 select 58 parent.binder_reply_id as source_node_id, 59 child.binder_txn_id as dest_node_id 60 from android_binder_txns parent 61 join descendant_slice(parent.binder_reply_id) desc 62 join android_binder_txns child on desc.id = child.binder_txn_id 63 where parent.server_ts > (select ts from s) 64 and parent.server_ts < (select ts + dur from s) 65 ), 66 all_binder_txns_considered as materialized ( 67 select * from child_binder_txns_for_slice 68 union 69 select * from binder_txns_in_slice_intervals 70 union 71 select * from nested_binder_txns_in_slice_interval 72 ) 73 select 74 dfs.node_id as id, 75 coalesce(client.client_ts, server.client_ts, slice.ts) as ts, 76 coalesce(client.client_dur, server.client_dur, slice.dur) as dur, 77 coalesce( 78 client.aidl_name, 79 server.aidl_name, 80 iif( 81 server.binder_reply_id is not null, 82 coalesce( 83 server.server_process, 84 server.server_thread, 85 'Unknown server' 86 ), 87 slice.name 88 ) 89 ) name, 90 coalesce( 91 client.client_utid, 92 server.server_utid, 93 thread_track.utid 94 ) as utid, 95 case 96 when client.binder_txn_id is not null then 'client' 97 when server.binder_reply_id is not null then 'server' 98 else 'slice' 99 end as slice_type, 100 coalesce(client.is_sync, server.is_sync, true) as is_sync 101 from graph_reachable_dfs!( 102 all_binder_txns_considered, 103 (select id as node_id from s) 104 ) dfs 105 join slice on dfs.node_id = slice.id 106 join thread_track on slice.track_id = thread_track.id 107 left join android_binder_txns client on dfs.node_id = client.binder_txn_id 108 left join android_binder_txns server on dfs.node_id = server.binder_reply_id 109 order by ts; 110 `); 111 await ctx.engine.query(` 112 include perfetto module intervals.intersect; 113 114 create or replace perfetto table __enhanced_binder_for_slice_${sliceId} as 115 with foo as ( 116 select 117 bfs.id as binder_id, 118 bfs.name as binder_name, 119 ii.ts, 120 ii.dur, 121 tstate.utid, 122 thread.upid, 123 tstate.cpu, 124 tstate.state, 125 tstate.io_wait, 126 ( 127 select name 128 from thread_slice tslice 129 where tslice.utid = tstate.utid and tslice.ts < ii.ts 130 order by ts desc 131 limit 1 132 ) as enclosing_slice_name 133 from _interval_intersect!( 134 ( 135 select id, ts, dur 136 from __binder_for_slice_${sliceId} 137 where slice_type IN ('slice', 'server') 138 and is_sync 139 and dur > 0 140 ), 141 ( 142 select id, ts, dur 143 from thread_state tstate 144 where 145 tstate.utid in ( 146 select distinct utid 147 from __binder_for_slice_${sliceId} 148 where 149 slice_type IN ('slice', 'server') 150 and is_sync 151 and dur > 0 152 ) 153 and dur > 0 154 ), 155 () 156 ) ii 157 join __binder_for_slice_${sliceId} bfs on ii.id_0 = bfs.id 158 join thread_state tstate on ii.id_1 = tstate.id 159 join thread using (utid) 160 where bfs.utid = tstate.utid 161 ) 162 select 163 *, 164 case 165 when state = 'S' and enclosing_slice_name = 'binder transaction' then 'Waiting for server' 166 when state = 'S' and enclosing_slice_name GLOB 'Lock*' then 'Waiting for lock' 167 when state = 'S' and enclosing_slice_name GLOB 'Monitor*' then 'Waiting for contention' 168 when state = 'S' then 'Sleeping' 169 when state = 'R' then 'Waiting for CPU' 170 when state = 'Running' then 'Running on CPU ' || foo.cpu 171 when state GLOB 'R*' then 'Runnable' 172 when state GLOB 'D*' and io_wait then 'IO' 173 when state GLOB 'D*' and not io_wait then 'Unint-sleep' 174 end as name 175 from foo 176 order by binder_id; 177 `); 178 179 const res = await ctx.engine.query(` 180 select id, name 181 from __binder_for_slice_${sliceId} bfs 182 where slice_type IN ('slice', 'server') 183 and dur > 0 184 order by ts 185 `); 186 const it = res.iter({ 187 id: NUM, 188 name: STR, 189 }); 190 for (; it.valid(); it.next()) { 191 await addDebugSliceTrack({ 192 trace: ctx, 193 data: { 194 sqlSource: ` 195 SELECT ts, dur, name 196 FROM __enhanced_binder_for_slice_${sliceId} 197 WHERE binder_id = ${it.id} 198 `, 199 }, 200 title: it.name, 201 }); 202 } 203 }, 204 }); 205 } 206} 207