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