xref: /aosp_15_r20/external/perfetto/ui/src/plugins/dev.perfetto.AndroidClientServer/index.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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