1-- 2-- Copyright 2020 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-- 16-- A collection of metrics related to GestureScrollUpdate events. 17-- 18-- We define a GestureScrollUpdate to be janky if comparing forwards or 19-- backwards (ignoring coalesced updates) a given GestureScrollUpdate exceeds 20-- the duration of its predecessor or successor by 50% of a vsync interval 21-- (defaulted to 60 FPS). 22-- 23-- WARNING: This metric should not be used as a source of truth. It is under 24-- active development and the values & meaning might change without 25-- notice. 26 27-- Get all chrome processes and threads tables set up. 28SELECT RUN_METRIC('chrome/chrome_processes.sql'); 29 30-- When working on GestureScrollUpdate events we need to ensure we have all the 31-- events from the browser, renderer, and GPU processes. This query isn't quite 32-- perfect. In system tracing we could have 3 browser processes all in the 33-- background and this would match, but for now its the best we can do (renderer 34-- and GPU names on android are quite complicated, but this should filter 99% ( 35-- citation needed) of what we want. 36-- 37-- See b/151077536 for historical context. 38DROP VIEW IF EXISTS sufficient_chrome_processes; 39CREATE VIEW sufficient_chrome_processes AS 40 SELECT 41 CASE WHEN ( 42 SELECT COUNT(*) FROM chrome_process) = 0 43 THEN 44 FALSE 45 ELSE ( 46 SELECT COUNT(*) >= 3 FROM ( 47 SELECT name FROM chrome_process 48 WHERE 49 name LIKE "Browser" OR 50 name LIKE "Renderer" OR 51 name LIKE "Gpu" OR 52 name LIKE 'com.android.chrome%' OR 53 name LIKE 'com.chrome.beta%' OR 54 name LIKE 'com.chrome.dev%' OR 55 name LIKE 'com.chrome.canary%' OR 56 name LIKE 'com.google.android.apps.chrome%' OR 57 name LIKE 'org.chromium.chrome%' 58 GROUP BY name 59 )) END AS have_enough_chrome_processes; 60 61-- A simple table that checks the time between VSync (this can be used to 62-- determine if we're scrolling at 90 FPS or 60 FPS. 63-- 64-- Note: In traces without the "Java" category there will be no VSync 65-- TraceEvents and this table will be empty. 66-- 67-- Note: Must be a TABLE because it uses a window function which can behave 68-- strangely in views. 69DROP TABLE IF EXISTS vsync_intervals; 70CREATE TABLE vsync_intervals AS 71 SELECT 72 slice_id, 73 ts, 74 dur, 75 track_id, 76 LEAD(ts) OVER(PARTITION BY track_id ORDER BY ts) - ts AS time_to_next_vsync 77 FROM slice 78 WHERE name = "VSync" 79 ORDER BY track_id, ts; 80 81-- Get all the GestureScrollBegin and GestureScrollEnd events. We take their 82-- IDs to group them together into scrolls later and the timestamp and duration 83-- to compute the duration of the scroll. 84DROP VIEW IF EXISTS scroll_begin_and_end; 85CREATE VIEW scroll_begin_and_end AS 86 SELECT 87 slice.name, 88 slice.id, 89 slice.ts, 90 slice.dur, 91 slice.track_id, 92 EXTRACT_ARG(arg_set_id, 'chrome_latency_info.gesture_scroll_id') 93 AS gesture_scroll_id, 94 EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id 95 FROM 96 slice 97 WHERE 98 slice.name IN ( 99 'InputLatency::GestureScrollBegin', 100 'InputLatency::GestureScrollEnd' 101 ) 102 ORDER BY ts; 103 104-- Now we take the GestureScrollBegin and the GestureScrollEnd events and join 105-- the information into a single row per scroll. We also compute the average 106-- Vysnc interval of the scroll (hopefully this would be either 60 FPS for the 107-- whole scroll or 90 FPS but that isn't always the case). If the trace doesn't 108-- contain the VSync TraceEvent we just fall back on assuming its 60 FPS (this 109-- is the 1.6e+7 in the COALESCE which corresponds to 16 ms or 60 FPS). 110DROP VIEW IF EXISTS joined_scroll_begin_and_end; 111CREATE VIEW joined_scroll_begin_and_end AS 112 SELECT 113 begin.id AS begin_id, 114 begin.ts AS begin_ts, 115 begin.dur AS begin_dur, 116 begin.track_id AS begin_track_id, 117 begin.trace_id AS begin_trace_id, 118 COALESCE(begin.gesture_scroll_id, begin.trace_id) 119 AS begin_gesture_scroll_id, 120 end.ts AS end_ts, 121 end.ts + end.dur AS end_ts_and_dur, 122 end.trace_id AS end_trace_id, 123 COALESCE(( 124 SELECT 125 CAST(AVG(time_to_next_vsync) AS FLOAT) 126 FROM vsync_intervals in_query 127 WHERE 128 time_to_next_vsync IS NOT NULL AND 129 in_query.ts > begin.ts AND 130 in_query.ts < end.ts 131 ), 1.6e+7) AS avg_vsync_interval 132 FROM scroll_begin_and_end begin JOIN scroll_begin_and_end end ON 133 begin.trace_id < end.trace_id AND 134 begin.name = 'InputLatency::GestureScrollBegin' AND 135 end.name = 'InputLatency::GestureScrollEnd' AND ( 136 ( 137 begin.gesture_scroll_id IS NULL AND 138 end.trace_id = ( 139 SELECT MIN(trace_id) 140 FROM scroll_begin_and_end in_query 141 WHERE 142 name = 'InputLatency::GestureScrollEnd' AND 143 in_query.trace_id > begin.trace_id 144 ) 145 ) OR 146 end.gesture_scroll_id = begin.gesture_scroll_id 147 ) 148 ORDER BY begin.ts; 149 150-- Get the GestureScrollUpdate events by name ordered by the 151-- |gesture_scroll_id|, and timestamp. Then compute the number of frames ( 152-- relative to vsync interval) that each event took. 1.6e+7 is 16 ms in 153-- nanoseconds and is used in case there are no VSync events to default to 60 154-- fps. We join each GestureScrollUpdate event to the information about it' 155-- begin and end events for easy computation later. 156-- 157-- We remove updates with |dur| == -1 because this means we have no end event 158-- and can't reasonably determine what it should be. We have separate tracking 159-- to ensure this only happens at the end of the trace where its expected. 160-- 161-- Note: Must be a TABLE because it uses a window function which can behave 162-- strangely in views. 163DROP TABLE IF EXISTS gesture_scroll_update; 164CREATE TABLE gesture_scroll_update AS 165 SELECT 166 ROW_NUMBER() OVER ( 167 ORDER BY gesture_scroll_id ASC, ts ASC) AS row_number, 168 begin_id, 169 begin_ts, 170 begin_dur, 171 begin_track_id, 172 begin_trace_id, 173 COALESCE(gesture_scroll_id, begin_trace_id) AS gesture_scroll_id, 174 CASE WHEN 175 end_ts_and_dur > ts + dur THEN 176 end_ts_and_dur 177 ELSE 178 ts + dur 179 END AS maybe_scroll_end, 180 id, 181 ts, 182 dur, 183 track_id, 184 trace_id, 185 dur/avg_vsync_interval AS scroll_frames_exact 186 FROM joined_scroll_begin_and_end begin_and_end JOIN ( 187 SELECT 188 EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id, 189 EXTRACT_ARG(arg_set_id, 'chrome_latency_info.gesture_scroll_id') 190 AS gesture_scroll_id, 191 * 192 FROM 193 slice JOIN track ON slice.track_id = track.id 194 WHERE 195 slice.name = 'InputLatency::GestureScrollUpdate' AND 196 slice.dur != -1 AND 197 NOT COALESCE( 198 EXTRACT_ARG(arg_set_id, "chrome_latency_info.is_coalesced"), 199 TRUE) 200 ) scroll_update ON 201 scroll_update.ts <= begin_and_end.end_ts AND 202 scroll_update.ts >= begin_and_end.begin_ts AND 203 scroll_update.trace_id > begin_and_end.begin_trace_id AND 204 scroll_update.trace_id < begin_and_end.end_trace_id AND ( 205 scroll_update.gesture_scroll_id IS NULL OR 206 scroll_update.gesture_scroll_id = begin_and_end.begin_gesture_scroll_id 207 ); 208 209-- This takes the GestureScrollUpdate events and joins it to the previous 210-- GestureScrollUpdate event (previous row and NULL if there isn't one) and the 211-- next GestureScrollUpdate event (next row and again NULL if there isn't one). 212-- Then we compute the duration of the event (relative to fps) and see if it 213-- increased by more then 0.5 (which is 1/2 of 16 ms at 60 fps, and so on). 214-- 215-- We only compare a GestureScrollUpdate event to another event within the same 216-- scroll (gesture_scroll_id == prev/next gesture_scroll_id). This controls 217-- somewhat for variability of scrolls. 218-- 219-- Note: Must be a TABLE because it uses a window function which can behave 220-- strangely in views. 221DROP TABLE IF EXISTS scroll_jank_maybe_null_prev_and_next; 222CREATE TABLE scroll_jank_maybe_null_prev_and_next AS 223 SELECT 224 currprev.*, 225 CASE WHEN 226 currprev.gesture_scroll_id != prev_gesture_scroll_id OR 227 prev_ts IS NULL OR 228 prev_ts < currprev.begin_ts OR 229 prev_ts > currprev.maybe_scroll_end 230 THEN 231 FALSE 232 ELSE 233 currprev.scroll_frames_exact > prev_scroll_frames_exact + 0.5 234 END AS prev_jank, 235 CASE WHEN 236 currprev.gesture_scroll_id != next.gesture_scroll_id OR 237 next.ts IS NULL OR 238 next.ts < currprev.begin_ts OR 239 next.ts > currprev.maybe_scroll_end 240 THEN 241 FALSE 242 ELSE 243 currprev.scroll_frames_exact > next.scroll_frames_exact + 0.5 244 END AS next_jank, 245 next.scroll_frames_exact AS next_scroll_frames_exact 246 FROM ( 247 SELECT 248 curr.*, 249 curr.maybe_scroll_end - curr.begin_ts AS scroll_dur, 250 prev.ts AS prev_ts, 251 prev.gesture_scroll_id AS prev_gesture_scroll_id, 252 prev.scroll_frames_exact AS prev_scroll_frames_exact 253 FROM 254 gesture_scroll_update curr LEFT JOIN 255 gesture_scroll_update prev ON prev.row_number + 1 = curr.row_number 256 ) currprev LEFT JOIN 257 gesture_scroll_update next ON currprev.row_number + 1 = next.row_number 258 ORDER BY currprev.gesture_scroll_id ASC, currprev.ts ASC; 259 260-- This just uses prev_jank and next_jank to see if each GestureScrollUpdate 261-- event is a jank. 262DROP VIEW IF EXISTS scroll_jank; 263CREATE VIEW scroll_jank AS 264 SELECT 265 id AS slice_id, 266 (next_jank IS NOT NULL AND next_jank) OR 267 (prev_jank IS NOT NULL AND prev_jank) 268 AS jank, 269 * 270 FROM scroll_jank_maybe_null_prev_and_next 271 ORDER BY gesture_scroll_id ASC, ts ASC; 272 273DROP VIEW IF EXISTS scroll_jank_ms; 274 275DROP VIEW IF EXISTS scroll_jank_output; 276CREATE VIEW scroll_jank_output AS 277 SELECT 278 ScrollJank( 279 'scroll_jank_percentage', ( 280 SELECT 281 ( 282 SUM(CASE WHEN jank THEN dur ELSE 0 END)/CAST(SUM(dur) AS REAL) 283 ) * 100.0 284 FROM scroll_jank 285 ), 286 'scroll_ms', ( 287 SELECT 288 CAST(SUM(scroll_dur)/1e6 AS REAL) 289 FROM ( 290 SELECT 291 MAX(scroll_dur) AS scroll_dur 292 FROM scroll_jank 293 GROUP BY gesture_scroll_id 294 ) 295 ), 296 'scroll_processing_ms', CAST(SUM(dur)/1e6 AS REAL), 297 'scroll_jank_processing_ms', ( 298 SELECT CAST(SUM(dur)/1e6 AS REAL) FROM scroll_jank WHERE jank 299 ), 300 'num_scroll_update_count', COUNT(*), 301 'num_scroll_update_jank_count', SUM(jank) 302 ) 303 FROM scroll_jank; 304