• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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