1-- Isolate the SurfaceFlinger process Id 2DROP TABLE IF EXISTS android_sysui_cuj_sf_process; 3CREATE TABLE android_sysui_cuj_sf_process AS 4SELECT name, upid FROM process 5WHERE process.name='/system/bin/surfaceflinger' 6LIMIT 1; 7 8DROP VIEW IF EXISTS android_sysui_cuj_sf_actual_frame_timeline_slice; 9CREATE VIEW android_sysui_cuj_sf_actual_frame_timeline_slice AS 10SELECT 11 actual.*, 12 actual.ts + actual.dur AS ts_end, 13 CAST(actual.name AS integer) AS vsync 14FROM actual_frame_timeline_slice actual JOIN android_sysui_cuj_sf_process USING (upid); 15 16DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_main_thread; 17CREATE TABLE android_sysui_cuj_surfaceflinger_main_thread AS 18 SELECT android_sysui_cuj_sf_process.name AS process_name, thread.utid 19 FROM thread JOIN android_sysui_cuj_sf_process USING (upid) 20 WHERE thread.is_main_thread; 21 22DROP TABLE IF EXISTS android_sysui_cuj_sf_main_thread_track; 23CREATE TABLE android_sysui_cuj_sf_main_thread_track AS 24SELECT thread_track.id 25FROM thread_track 26JOIN android_sysui_cuj_surfaceflinger_main_thread thread USING (utid); 27 28DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_gpu_completion_thread; 29CREATE VIEW android_sysui_cuj_surfaceflinger_gpu_completion_thread AS 30 SELECT android_sysui_cuj_sf_process.name AS process_name, thread.utid 31 FROM thread JOIN android_sysui_cuj_sf_process USING (upid) 32 WHERE thread.name = 'GPU completion'; 33 34DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_renderengine_thread; 35CREATE VIEW android_sysui_cuj_surfaceflinger_renderengine_thread AS 36 SELECT android_sysui_cuj_sf_process.name AS process_name, thread.utid 37 FROM thread JOIN android_sysui_cuj_sf_process USING (upid) 38 WHERE thread.name = 'RenderEngine'; 39 40DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_gpu_completion_slices; 41CREATE VIEW android_sysui_cuj_surfaceflinger_gpu_completion_slices AS 42 SELECT 43 process_name, 44 thread.utid, 45 slice.*, 46 slice.ts + slice.dur AS ts_end, 47 -- Extracts 1234 from 'waiting for GPU completion 1234' 48 CAST(STR_SPLIT(slice.name, ' ', 4) AS INTEGER) AS idx 49 FROM slice 50 JOIN thread_track ON slice.track_id = thread_track.id 51 JOIN android_sysui_cuj_surfaceflinger_gpu_completion_thread thread USING (utid) 52 WHERE slice.name GLOB 'waiting for GPU completion *' 53 AND dur > 0; 54 55-- Find flows between actual frame slices from app process to surfaceflinger, allowing us to 56-- correlate vsyncs. 57DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_app_flow_vsyncs; 58CREATE VIEW android_sysui_cuj_surfaceflinger_app_flow_vsyncs AS 59SELECT 60 app_slice.name AS app_vsync, 61 app_slice.id AS app_slice_id, 62 cuj_process.name AS app_process, 63 sf_slice.name AS sf_vsync, 64 sf_slice.id AS sf_slice_id 65FROM android_sysui_cuj_sf_actual_frame_timeline_slice sf_slice 66JOIN directly_connected_flow(sf_slice.id) flow 67JOIN actual_frame_timeline_slice app_slice ON slice_in = app_slice.id 68JOIN android_sysui_cuj_last_cuj cuj_process ON app_slice.upid = cuj_process.upid 69GROUP BY app_vsync, sf_vsync; 70 71-- Filter to those SF frames which flow from app frames that are within the app vsync boundaries of 72-- the CUJ 73DROP TABLE IF EXISTS android_sysui_cuj_sf_frames_in_cuj; 74CREATE TABLE android_sysui_cuj_sf_frames_in_cuj AS 75SELECT 76 sf_frame.ts, 77 sf_frame.dur, 78 sf_frame.jank_type, 79 sf_frame.ts + sf_frame.dur AS ts_end, 80 flows.sf_vsync, 81 flows.app_vsync 82-- This table contains only the frame timeline slices within the CUJ app vsync boundaries 83FROM android_sysui_cuj_frame_timeline_events app_frames 84-- Find the matching SF frame via flow 85JOIN android_sysui_cuj_surfaceflinger_app_flow_vsyncs flows ON app_frames.vsync = flows.app_vsync 86JOIN android_sysui_cuj_sf_actual_frame_timeline_slice sf_frame ON sf_frame.id = flows.sf_slice_id 87GROUP BY flows.sf_vsync; 88 89-- Take the min and max vsync to define the SurfaceFlinger boundaries 90DROP TABLE IF EXISTS android_sysui_cuj_sf_vsync_boundaries; 91CREATE TABLE android_sysui_cuj_sf_vsync_boundaries AS 92SELECT MIN(sf_vsync) AS vsync_min, MAX(sf_vsync) AS vsync_max 93FROM android_sysui_cuj_sf_frames_in_cuj; 94 95-- Find just the commit slices, within the CUJ (by vsync) 96DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_commit_slices_in_cuj; 97CREATE TABLE android_sysui_cuj_surfaceflinger_commit_slices_in_cuj AS 98SELECT * FROM 99 (SELECT 100 -- Extract the vsync number from name like 'commit 235991 vsyncIn 15.992ms' 101 CAST(STR_SPLIT(slice.name, ' ', 1) AS INTEGER) AS vsync, 102 CAST(CAST(STR_SPLIT(slice.name, ' ', 3) AS NUMBER) * 1e6 + slice.ts AS INTEGER) AS expected_vsync_ts, 103 slice.name, 104 slice.ts, 105 slice.dur, 106 slice.ts + slice.dur AS ts_end 107 FROM slice 108 JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id 109 WHERE slice.dur > 0 AND slice.name GLOB 'commit *') 110JOIN android_sysui_cuj_sf_vsync_boundaries cuj_boundaries 111WHERE vsync >= cuj_boundaries.vsync_min AND vsync <= cuj_boundaries.vsync_max; 112 113-- Find just the onMessageInvalidate slices, within the CUJ (by vsync) 114DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_on_message_invalidate_slices_in_cuj; 115CREATE VIEW android_sysui_cuj_surfaceflinger_on_message_invalidate_slices_in_cuj AS 116 WITH on_msg AS (SELECT 117 -- Extract the vsync number from name like 'onMessageInvalidate 235991 vsyncIn 15.992ms' 118 CAST(STR_SPLIT(slice.name, ' ', 1) AS INTEGER) AS vsync, 119 CAST(CAST(STR_SPLIT(slice.name, ' ', 3) AS NUMBER) * 1e6 + slice.ts AS INTEGER) AS expected_vsync_ts, 120 slice.ts, 121 slice.ts + slice.dur AS ts_end, 122 slice.dur 123 FROM slice 124 JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id 125 WHERE slice.name GLOB 'onMessageInvalidate *' 126 ) 127SELECT 128 on_msg.vsync, 129 on_msg.ts, 130 on_msg.ts_end, 131 on_msg.dur, 132 on_msg.expected_vsync_ts, 133 lag(on_msg.ts_end) OVER (ORDER BY on_msg.ts_end ASC) AS ts_prev_frame_end, 134 lead(on_msg.ts) OVER (ORDER BY on_msg.ts ASC) AS ts_next_frame_start 135FROM on_msg 136JOIN android_sysui_cuj_sf_vsync_boundaries cuj_boundaries 137WHERE on_msg.vsync >= cuj_boundaries.vsync_min AND on_msg.vsync <= cuj_boundaries.vsync_max; 138 139-- Find just the composite slices 140DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_composite_slices; 141CREATE TABLE android_sysui_cuj_surfaceflinger_composite_slices AS 142 SELECT 143 slice.name, 144 slice.ts, 145 slice.dur, 146 slice.ts + slice.dur AS ts_end 147 FROM slice 148 JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id 149 WHERE slice.dur > 0 AND slice.name = 'composite'; 150 151DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_commit_composite_frames_in_cuj; 152CREATE VIEW android_sysui_cuj_surfaceflinger_commit_composite_frames_in_cuj AS 153 WITH commit_to_composite AS ( 154 SELECT 155 commits.vsync, 156 commits.ts AS commit_ts, 157 commits.name AS name, 158 commits.expected_vsync_ts, 159 min(composite.ts) AS composite_ts 160 FROM android_sysui_cuj_surfaceflinger_commit_slices_in_cuj commits 161 JOIN android_sysui_cuj_surfaceflinger_composite_slices composite ON composite.ts > commits.ts_end 162 GROUP BY commits.vsync 163 ) 164 SELECT 165 vsync, 166 match.commit_ts AS ts, 167 composite.ts_end AS ts_end, 168 composite.ts_end - match.commit_ts AS dur, 169 match.expected_vsync_ts, 170 lag(composite.ts_end) OVER (ORDER BY composite.ts_end ASC) AS ts_prev_frame_end, 171 lead(match.commit_ts) OVER (ORDER BY match.commit_ts ASC) AS ts_next_frame_start 172 FROM commit_to_composite match 173 JOIN android_sysui_cuj_surfaceflinger_composite_slices composite ON match.composite_ts = composite.ts; 174 175-- All SF frames in the CUJ 176DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_main_thread_frames; 177CREATE TABLE android_sysui_cuj_surfaceflinger_main_thread_frames AS 178SELECT * FROM android_sysui_cuj_surfaceflinger_on_message_invalidate_slices_in_cuj 179UNION ALL 180SELECT * FROM android_sysui_cuj_surfaceflinger_commit_composite_frames_in_cuj; 181 182-- Our timestamp boundaries are the earliest ts and latest ts_end of the main thread frames (e.g. 183-- onMessageInvalidate or commit/composite pair) which flow from app frames within the CUJ 184DROP TABLE IF EXISTS android_sysui_cuj_sf_ts_boundaries; 185CREATE TABLE android_sysui_cuj_sf_ts_boundaries AS 186SELECT ts, ts_end - ts AS dur, ts_end 187FROM (SELECT MIN(ts) AS ts, MAX(ts_end) AS ts_end FROM android_sysui_cuj_surfaceflinger_main_thread_frames); 188 189DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_main_thread_slices_in_cuj; 190CREATE TABLE android_sysui_cuj_surfaceflinger_main_thread_slices_in_cuj AS 191 SELECT 192 slice.*, 193 slice.ts + slice.dur AS ts_end 194 FROM slice 195 JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id 196 JOIN android_sysui_cuj_sf_ts_boundaries cuj_boundaries 197 ON slice.ts >= cuj_boundaries.ts AND slice.ts <= cuj_boundaries.ts_end 198 WHERE slice.dur > 0; 199 200-- Find SurfaceFlinger GPU completions that are within the CUJ 201DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_gpu_completion_slices_in_cuj; 202CREATE TABLE android_sysui_cuj_surfaceflinger_gpu_completion_slices_in_cuj AS 203SELECT slice.* FROM android_sysui_cuj_surfaceflinger_gpu_completion_slices slice 204JOIN android_sysui_cuj_sf_ts_boundaries cuj_boundaries 205ON slice.ts >= cuj_boundaries.ts AND slice.ts <= cuj_boundaries.ts_end; 206 207DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_renderengine_slices_in_cuj; 208CREATE TABLE android_sysui_cuj_surfaceflinger_renderengine_slices_in_cuj AS 209 SELECT 210 process_name, 211 thread.utid, 212 slice.*, 213 slice.ts + slice.dur AS ts_end 214 FROM slice 215 JOIN thread_track ON slice.track_id = thread_track.id 216 JOIN android_sysui_cuj_surfaceflinger_renderengine_thread thread USING (utid) 217 JOIN android_sysui_cuj_sf_ts_boundaries cuj_boundaries 218 ON slice.ts >= cuj_boundaries.ts AND slice.ts <= cuj_boundaries.ts_end 219 WHERE slice.dur > 0; 220 221DROP VIEW IF EXISTS android_sysui_cuj_gcs_to_mt_match; 222CREATE VIEW android_sysui_cuj_gcs_to_mt_match AS 223-- Match Mainthread slice with the first GPU Completion that begins during it 224SELECT 225gcs.ts AS gcs_ts, 226gcs.ts_end AS gcs_ts_end, 227gcs.dur AS gcs_dur, 228gcs.idx AS idx, 229mtf.ts AS mts_ts, 230mtf.vsync AS vsync 231FROM android_sysui_cuj_surfaceflinger_gpu_completion_slices_in_cuj gcs 232-- join with all previous render frames but take latest start time 233JOIN android_sysui_cuj_surfaceflinger_main_thread_frames mtf ON gcs.ts > mtf.ts AND gcs.ts < mtf.ts + mtf.dur 234GROUP BY gcs.ts, gcs.ts_end, gcs.dur, gcs.idx; 235 236-- Those SurfaceFlinger Frames where we missed the deadline 237-- To avoid overlap - which could result in counting janky slices more than once - we limit the 238-- definition of each frame to: 239-- * beginning when the shared timeline actual frame starts, or - if later - 240-- when the previous main thread computation ended 241-- * ending when the next main thread computation begins, but no later than the 242-- shared timeline actual frame ends 243DROP TABLE IF EXISTS android_sysui_cuj_sf_missed_frames; 244CREATE TABLE android_sysui_cuj_sf_missed_frames AS 245SELECT 246 CAST(frame.name AS integer) AS frame_number, 247 CAST(frame.name AS integer) AS vsync, 248 max(mtf.ts_prev_frame_end, frame.ts) AS ts, 249 min(mtf.ts_next_frame_start, frame.ts_end) AS ts_end, 250 min(mtf.ts_next_frame_start, frame.ts_end) - max(mtf.ts_prev_frame_end, frame.ts) AS dur, 251 -- Needed for compatibility with downstream scripts 252 CAST(min(mtf.ts_next_frame_start, frame.ts_end) - max(mtf.ts_prev_frame_end, frame.ts) AS INTEGER) AS dur_frame, 253 gcs.gcs_ts, 254 gcs.gcs_ts_end, 255 gcs.gcs_dur, 256 CAST(1 AS INTEGER) AS app_missed 257FROM android_sysui_cuj_sf_actual_frame_timeline_slice frame 258JOIN android_sysui_cuj_surfaceflinger_main_thread_frames mtf ON frame.name = mtf.vsync 259JOIN android_sysui_cuj_gcs_to_mt_match gcs ON gcs.vsync = frame.name 260WHERE frame.jank_type != 'None'; 261