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 android.frames.timeline; 19 20INCLUDE PERFETTO MODULE slices.with_context; 21 22CREATE PERFETTO VIEW _startups_with_upid AS 23WITH 24 joined_with_processes AS ( 25 SELECT 26 s.*, 27 p.upid 28 FROM android_startups AS s 29 LEFT JOIN android_startup_processes AS p 30 USING (startup_id) 31 ), 32 fallback AS ( 33 SELECT 34 s.*, 35 upid 36 FROM android_startups AS s 37 JOIN process AS p 38 ON ( 39 p.name GLOB s.package 40 ) 41 ) 42SELECT 43 j.startup_id, 44 j.ts, 45 j.ts_end, 46 j.dur, 47 j.package, 48 j.startup_type, 49 coalesce(j.upid, f.upid) AS upid 50FROM joined_with_processes AS j 51LEFT JOIN fallback AS f 52 ON ( 53 j.upid IS NULL AND j.startup_id = f.startup_id 54 ); 55 56-- Get Time To Initial Display of the startup calculated as time between the 57-- startup started and the first frame that was started by Choreographer on the 58-- UI thread of the startup finished drawing. 59-- TTID (https://developer.android.com/topic/performance/vitals/launch-time#time-initial) 60-- Googlers: see go/android-performance-metrics-glossary for details. 61CREATE PERFETTO TABLE _ttid AS 62WITH 63 frames_with_upid AS ( 64 SELECT 65 f.* 66 FROM android_frames AS f 67 JOIN thread AS t 68 ON ( 69 f.ui_thread_utid = t.utid 70 ) 71 ), 72 -- First `DrawFrame` on Render Thread after the startup. 73 first_frame_for_startup AS ( 74 SELECT 75 startup_id, 76 frame_id, 77 s.ts AS startup_ts, 78 draw_frame_id, 79 s.upid 80 FROM _startups_with_upid AS s 81 JOIN frames_with_upid AS f 82 ON ( 83 s.upid = f.upid AND s.ts <= f.ts 84 ) 85 GROUP BY 86 startup_id 87 ORDER BY 88 startup_id, 89 f.ts 90 ) 91SELECT 92 startup_id, 93 frame_id, 94 draw_frame_id, 95 ts + dur - startup_ts AS ttid, 96 upid 97FROM first_frame_for_startup 98JOIN slice 99 ON ( 100 slice.id = draw_frame_id 101 ); 102 103-- Get Time To Full Display of the startup calculated as time between the 104-- startup started and the first frame that was started by Choreographer after 105-- or containing the `reportFullyDrawn()` slice on the UI thread of the startup 106-- finished drawing. 107-- TTFD (https://developer.android.com/topic/performance/vitals/launch-time#retrieve-TTFD) 108-- Googlers: see go/android-performance-metrics-glossary for details. 109CREATE PERFETTO TABLE _ttfd AS 110-- First `reportFullyDrawn` slice for each startup. 111WITH 112 first_report_fully_drawn_for_startup AS ( 113 SELECT 114 startup_id, 115 s.ts AS startup_ts, 116 t.ts AS report_fully_drawn_ts, 117 t.utid, 118 s.upid 119 FROM _startups_with_upid AS s 120 JOIN thread_slice AS t 121 ON ( 122 s.upid = t.upid AND t.ts >= s.ts 123 ) 124 WHERE 125 name GLOB "reportFullyDrawn*" AND t.is_main_thread = 1 126 GROUP BY 127 startup_id 128 ORDER BY 129 startup_id, 130 t.ts 131 ), 132 -- After the first `reportFullyDrawn` find the first `Choreographer#DoFrame` on 133 -- the UI thread and it's first `DrawFrame` on Render Thread. 134 first_frame_after_report_for_startup AS ( 135 SELECT 136 startup_id, 137 frame_id, 138 startup_ts, 139 draw_frame_id, 140 s.upid 141 FROM first_report_fully_drawn_for_startup AS s 142 JOIN android_frames AS f 143 ON ( 144 s.utid = f.ui_thread_utid 145 -- We are looking for the first DrawFrame that was started by the first 146 -- "Choreographer#DoFrame" on UI thread after or containing 147 -- reportFullyDrawn. In Android UIs, it's common to have UI code happen 148 -- either before a frame, or during it, and generally non-trivial amounts 149 -- of "update UI model" code doesn't try to differentiate these. We account 150 -- for both of these by looking for the first UI slice that ends after the 151 -- "reportFullyDrawnSlice" begins. 152 AND report_fully_drawn_ts < ( 153 f.ts + f.dur 154 ) 155 ) 156 GROUP BY 157 startup_id 158 ORDER BY 159 startup_id, 160 f.ts 161 ) 162-- Get TTFD as the difference between the start of the startup and the end of 163-- `DrawFrame` slice we previously found. 164SELECT 165 startup_id, 166 frame_id, 167 draw_frame_id, 168 ts + dur - startup_ts AS ttfd, 169 upid 170FROM first_frame_after_report_for_startup 171JOIN slice 172 ON ( 173 slice.id = draw_frame_id 174 ); 175 176-- Startup metric defintions, which focus on the observable time range: 177-- TTID - Time To Initial Display 178-- * https://developer.android.com/topic/performance/vitals/launch-time#time-initial 179-- * end of first RenderThread.DrawFrame - bindApplication 180-- TTFD - Time To Full Display 181-- * https://developer.android.com/topic/performance/vitals/launch-time#retrieve-TTFD 182-- * end of next RT.DrawFrame, after reportFullyDrawn called - bindApplication 183-- Googlers: see go/android-performance-metrics-glossary for details. 184CREATE PERFETTO TABLE android_startup_time_to_display ( 185 -- Startup id. 186 startup_id LONG, 187 -- Time to initial display (TTID) 188 time_to_initial_display LONG, 189 -- Time to full display (TTFD) 190 time_to_full_display LONG, 191 -- `android_frames.frame_id` of frame for initial display 192 ttid_frame_id LONG, 193 -- `android_frames.frame_id` of frame for full display 194 ttfd_frame_id LONG, 195 -- `process.upid` of the startup 196 upid JOINID(process.id) 197) AS 198SELECT 199 startup_id, 200 ttid AS time_to_initial_display, 201 ttfd AS time_to_full_display, 202 _ttid.frame_id AS ttid_frame_id, 203 _ttfd.frame_id AS ttfd_frame_id, 204 _ttid.upid 205FROM android_startups 206LEFT JOIN _ttid 207 USING (startup_id) 208LEFT JOIN _ttfd 209 USING (startup_id); 210