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