• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1--
2-- Copyright 2024 The Android Open Source Project
3--
4-- Licensed under the Apache License, Version 2.0 (the "License");
5-- you may not use this file except in compliance with the License.
6-- You may obtain a copy of the License at
7--
8--     https://www.apache.org/licenses/LICENSE-2.0
9--
10-- Unless required by applicable law or agreed to in writing, software
11-- distributed under the License is distributed on an "AS IS" BASIS,
12-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-- See the License for the specific language governing permissions and
14-- limitations under the License.
15
16INCLUDE PERFETTO MODULE android.startup.startups;
17
18INCLUDE PERFETTO MODULE intervals.overlap;
19
20INCLUDE PERFETTO MODULE slices.hierarchy;
21
22INCLUDE PERFETTO MODULE slices.with_context;
23
24-- Maps slice names with common prefixes to a static string key.
25-- Returns NULL if there's no mapping.
26CREATE PERFETTO FUNCTION _normalize_android_string(
27    name STRING
28)
29RETURNS STRING AS
30SELECT
31  CASE
32    WHEN $name = 'mm_vmscan_direct_reclaim'
33    THEN 'kernel_memory_reclaim'
34    WHEN $name GLOB 'GC: Wait For*'
35    THEN 'userspace_memory_reclaim'
36    WHEN (
37      $name GLOB 'monitor contention*'
38      OR $name GLOB 'Lock contention on a monitor lock*'
39    )
40    THEN 'monitor_contention'
41    WHEN $name GLOB 'Lock contention*'
42    THEN 'art_lock_contention'
43    WHEN (
44      $name = 'binder transaction' OR $name = 'binder reply'
45    )
46    THEN 'binder'
47    WHEN $name = 'Contending for pthread mutex'
48    THEN 'mutex_contention'
49    WHEN $name GLOB 'dlopen*'
50    THEN 'dlopen'
51    WHEN $name GLOB 'VerifyClass*'
52    THEN 'verify_class'
53    WHEN $name = 'inflate'
54    THEN 'inflate'
55    WHEN $name GLOB 'Choreographer#doFrame*'
56    THEN 'choreographer_do_frame'
57    WHEN $name GLOB 'OpenDexFilesFromOat*'
58    THEN 'open_dex_files_from_oat'
59    WHEN $name = 'ResourcesManager#getResources'
60    THEN 'resources_manager_get_resources'
61    WHEN $name = 'bindApplication'
62    THEN 'bind_application'
63    WHEN $name = 'activityStart'
64    THEN 'activity_start'
65    WHEN $name = 'activityResume'
66    THEN 'activity_resume'
67    WHEN $name = 'activityRestart'
68    THEN 'activity_restart'
69    WHEN $name = 'clientTransactionExecuted'
70    THEN 'client_transaction_executed'
71    ELSE NULL
72  END AS name;
73
74-- Derives a startup reason from a slice name and some thread_state columns.
75CREATE PERFETTO FUNCTION _startup_breakdown_reason(
76    name STRING,
77    state STRING,
78    io_wait LONG,
79    irq_context LONG
80)
81RETURNS STRING AS
82SELECT
83  CASE
84    WHEN $io_wait = 1
85    THEN 'io'
86    WHEN $name IS NOT NULL
87    THEN $name
88    WHEN $irq_context = 1
89    THEN 'irq'
90    ELSE $state
91  END AS name;
92
93-- List of startups with unique ids for each possible upid. The existing
94-- startup_ids are not necessarily unique (because of multiuser).
95CREATE PERFETTO TABLE _startup_root_slices AS
96SELECT
97  (
98    SELECT
99      max(id)
100    FROM slice
101  ) + row_number() OVER () AS id,
102  android_startups.dur AS dur,
103  android_startups.ts AS ts,
104  android_startups.startup_id,
105  android_startups.startup_type,
106  process.name AS process_name,
107  thread.utid AS utid
108FROM android_startup_processes AS startup
109JOIN android_startups
110  USING (startup_id)
111JOIN thread
112  ON thread.upid = process.upid AND thread.is_main_thread
113JOIN process
114  ON process.upid = startup.upid
115WHERE
116  android_startups.dur > 0
117ORDER BY
118  ts;
119
120-- All relevant startup slices normalized with _normalize_android_string.
121CREATE PERFETTO TABLE _startup_normalized_slices AS
122WITH
123  relevant_startup_slices AS (
124    SELECT
125      slice.*
126    FROM thread_slice AS slice
127    JOIN _startup_root_slices AS startup
128      ON slice.utid = startup.utid
129      AND max(slice.ts, startup.ts) < min(slice.ts + slice.dur, startup.ts + startup.dur)
130  )
131SELECT
132  p.id,
133  p.parent_id,
134  p.depth,
135  p.name,
136  thread_slice.ts,
137  thread_slice.dur,
138  thread_slice.utid
139FROM _slice_remove_nulls_and_reparent
140    !(
141      (
142        SELECT id, parent_id, depth, _normalize_android_string(name) AS name
143        FROM relevant_startup_slices
144        WHERE dur > 0
145      ),
146      name) AS p
147JOIN thread_slice
148  USING (id);
149
150-- Subset of _startup_normalized_slices that occurred during any app startups on the main thread.
151-- Their timestamps and durations are chopped to fit within the respective app startup duration.
152CREATE PERFETTO TABLE _startup_slices_breakdown AS
153SELECT
154  *
155FROM _intervals_merge_root_and_children_by_intersection !(_startup_root_slices, _startup_normalized_slices, utid);
156
157-- Flattened slice version of _startup_slices_breakdown. This selects the leaf slice at every region
158-- of the slice stack.
159CREATE PERFETTO TABLE _startup_flat_slices_breakdown AS
160SELECT
161  i.ts,
162  i.dur,
163  i.root_id,
164  s.id AS slice_id,
165  s.name
166FROM _intervals_flatten !(_startup_slices_breakdown) AS i
167JOIN _startup_normalized_slices AS s
168  USING (id);
169
170-- Subset of thread_states that occurred during any app startups on the main thread.
171CREATE PERFETTO TABLE _startup_thread_states_breakdown AS
172SELECT
173  i.ts,
174  i.dur,
175  i.root_id,
176  t.id AS thread_state_id,
177  t.state,
178  t.io_wait,
179  t.irq_context
180FROM _intervals_merge_root_and_children_by_intersection!(_startup_root_slices,
181                                                           (SELECT *, NULL AS parent_id FROM thread_state),
182                                                           utid) AS i
183JOIN thread_state AS t
184  USING (id);
185
186-- Intersection of _startup_flat_slices_breakdown and _startup_thread_states_breakdown.
187-- A left intersection is used since some parts of the slice stack may not have any slices
188-- but will have thread states.
189CREATE VIRTUAL TABLE _startup_thread_states_and_slices_breakdown_sp USING SPAN_LEFT_JOIN (
190    _startup_thread_states_breakdown PARTITIONED root_id,
191    _startup_flat_slices_breakdown PARTITIONED root_id);
192
193-- Blended thread state and slice breakdown blocking app startups.
194--
195-- Each row blames a unique period during an app startup with a reason
196-- derived from the slices and thread states on the main thread.
197--
198-- Some helpful events to enables are binder transactions, ART, am and view.
199CREATE PERFETTO TABLE android_startup_opinionated_breakdown (
200  -- Startup id.
201  startup_id JOINID(android_startups.startup_id),
202  -- Id of relevant slice blocking startup.
203  slice_id JOINID(slice.id),
204  -- Id of thread_state blocking startup.
205  thread_state_id JOINID(thread_state.id),
206  -- Timestamp of an exclusive interval during the app startup with a single latency reason.
207  ts TIMESTAMP,
208  -- Duration of an exclusive interval during the app startup with a single latency reason.
209  dur DURATION,
210  -- Cause of delay during an exclusive interval of the app startup.
211  reason STRING
212) AS
213SELECT
214  b.ts,
215  b.dur,
216  startup.startup_id,
217  b.slice_id,
218  b.thread_state_id,
219  _startup_breakdown_reason(name, state, io_wait, irq_context) AS reason
220FROM _startup_thread_states_and_slices_breakdown_sp AS b
221JOIN _startup_root_slices AS startup
222  ON startup.id = b.root_id
223UNION ALL
224-- Augment the existing startup breakdown with an artificial slice accounting for
225-- any launch delays before the app starts handling startup on its main thread
226SELECT
227  _startup_root_slices.ts,
228  min(_startup_thread_states_breakdown.ts) - _startup_root_slices.ts AS dur,
229  startup_id,
230  NULL AS slice_id,
231  NULL AS thread_state_id,
232  'launch_delay' AS reason
233FROM _startup_thread_states_breakdown
234JOIN _startup_root_slices
235  ON _startup_root_slices.id = root_id
236GROUP BY
237  root_id
238HAVING
239  min(_startup_thread_states_breakdown.ts) - _startup_root_slices.ts > 0;
240