• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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