• 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
17SELECT RUN_METRIC('chrome/chrome_processes.sql') AS suppress_query_output;
18SELECT RUN_METRIC('chrome/chrome_event_metadata.sql') AS suppress_query_output;
19
20-- Priority order for RAIL modes where response has the highest priority and
21-- idle has the lowest.
22DROP TABLE IF EXISTS rail_modes;
23CREATE TABLE rail_modes (
24  mode TEXT UNIQUE,
25  ordering INT,
26  short_name TEXT
27);
28
29-- RAIL_MODE_IDLE is used when no frames are visible in the renderer and so this
30-- interprets that as background.
31-- RAIL_MODE_LOAD is for the time from a navigation until the first meaningful
32-- paint (assuming there are no user interactions).
33-- RAIL_MODE_RESPONSE is used when the main thread is dealing with a
34-- user-interaction (but not for instance for scrolls which may be handled by
35-- the compositor).
36-- RAIL_MODE_ANIMATION is used when none of the above apply.
37-- The enum in chrome is defined in:
38-- https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/platform/scheduler/public/rail_mode_observer.h
39INSERT INTO rail_modes
40VALUES ('RAIL_MODE_IDLE', 0, 'background'),
41  ('RAIL_MODE_ANIMATION', 1, "animation"),
42  ('RAIL_MODE_LOAD', 2, "load"),
43  ('RAIL_MODE_RESPONSE', 3, "response");
44
45
46-- Find the max ts + dur for every process
47DROP TABLE IF EXISTS max_ts_per_process;
48CREATE TABLE max_ts_per_process AS
49 -- MAX(dur, 0) means unclosed slices just contribute their start time.
50SELECT upid,
51  MAX(ts + MAX(dur, 0)) AS ts
52FROM (
53    SELECT upid,
54      ts,
55      dur
56    FROM process_track t
57      JOIN slice s
58    WHERE s.track_id = t.id
59    UNION ALL
60    SELECT upid,
61      ts,
62      dur
63    FROM thread_track t
64      JOIN thread
65      JOIN slice
66    WHERE slice.track_id = t.id
67      AND thread.utid = t.utid
68  )
69GROUP BY upid;
70
71-- View containing all Scheduler.RAILMode slices across all Chrome renderer
72-- processes.
73DROP VIEW IF EXISTS original_rail_mode_slices;
74CREATE VIEW original_rail_mode_slices AS
75SELECT slice.id,
76  slice.ts,
77  CASE
78    -- Add 1 to the duration to ensure you cannot get a zero-sized RAIL mode
79    -- slice, which can throw off the later queries.
80    WHEN dur == -1 THEN max_ts_per_process.ts - slice.ts + 1
81    ELSE dur
82  END AS dur,
83  track_id,
84  EXTRACT_ARG(
85    slice.arg_set_id,
86    "chrome_renderer_scheduler_state.rail_mode"
87  ) AS rail_mode
88FROM max_ts_per_process,
89  slice,
90  process_track
91WHERE slice.name = "Scheduler.RAILMode"
92  AND slice.track_id = process_track.id
93  AND process_track.upid = max_ts_per_process.upid;
94
95-- Detect if the trace has an unrealistic length (10 minutes) that probably
96-- means some trace events have faulty timestamps and which could throw off any
97-- metrics that use the trace.
98DROP VIEW IF EXISTS trace_has_realistic_length;
99CREATE VIEW trace_has_realistic_length AS
100SELECT (end_ts - start_ts) < 1e9 * 60 * 10 AS value
101FROM trace_bounds;
102
103-- RAIL_MODE_LOAD seems to get stuck which makes it not very useful so remap it
104-- to RAIL_MODE_ANIMATION so it doesn't dominate the overall RAIL mode.
105DROP VIEW IF EXISTS rail_mode_slices;
106CREATE VIEW rail_mode_slices AS
107SELECT ts, dur, track_id,
108    CASE
109      WHEN rail_mode == "RAIL_MODE_LOAD" THEN "RAIL_MODE_ANIMATION"
110      ELSE rail_mode
111    END AS rail_mode
112FROM original_rail_mode_slices;
113
114-- View containing a collapsed view of rail_mode_slices where there is only one
115-- RAIL mode active at a given time. The mode is derived using the priority
116-- order in rail_modes.
117DROP VIEW IF EXISTS overall_rail_mode_slices;
118CREATE VIEW overall_rail_mode_slices AS
119SELECT s.ts,
120  s.end_ts,
121  rail_modes.short_name AS rail_mode,
122  MAX(rail_modes.ordering)
123FROM (
124    SELECT ts,
125      LEAD(ts, 1, (SELECT MAX(ts + dur) FROM rail_mode_slices)) OVER (
126        ORDER BY ts
127      ) AS end_ts
128    FROM (
129        SELECT DISTINCT ts
130        FROM rail_mode_slices
131      ) start_times
132  ) s,
133  rail_mode_slices r,
134  rail_modes,
135  trace_has_realistic_length
136WHERE (
137    (
138      s.ts >= r.ts AND s.ts < r.ts + r.dur
139    )
140    OR (
141      s.end_ts > r.ts AND s.end_ts <= r.ts + r.dur
142    )
143  )
144  AND r.rail_mode == rail_modes.mode
145  AND trace_has_realistic_length.value
146GROUP BY s.ts;
147
148-- Contains the same data as overall_rail_mode_slices except adjacent slices
149-- with the same RAIL mode are combined.
150DROP TABLE IF EXISTS combined_overall_rail_slices;
151CREATE TABLE combined_overall_rail_slices AS
152SELECT ROW_NUMBER() OVER () AS id,
153  ts,
154  end_ts - ts AS dur,
155  rail_mode
156FROM (
157    SELECT lag(l.end_ts, 1, FIRST) OVER (
158        ORDER BY l.ts
159      ) AS ts,
160      l.end_ts,
161      l.rail_mode
162    FROM (
163        SELECT ts,
164          end_ts,
165          rail_mode
166        FROM overall_rail_mode_slices s
167        WHERE NOT EXISTS (
168            SELECT NULL
169            FROM overall_rail_mode_slices s2
170            WHERE s.rail_mode = s2.rail_mode
171              AND s.end_ts = s2.ts
172          )
173      ) AS l,
174      (
175        SELECT min(ts) AS FIRST
176        FROM overall_rail_mode_slices
177      )
178  );
179
180-- Now we have the RAIL Mode, use other trace events to create a modified RAIL
181-- mode that more accurately reflects what the browser/user are doing.
182
183-- First create slices for when there's no animation as indicated by a large gap
184-- between vsync events (since it's easier to find gaps than runs of adjacent
185-- vsyncs).
186
187-- Mark any large gaps between vsyncs.
188-- The value in "not_animating" is always 1. It's just there to be a non-NULL
189-- value so the later SPAN_JOIN can find the set-difference.
190DROP VIEW IF EXISTS not_animating_slices;
191CREATE VIEW not_animating_slices AS
192WITH const (vsync_padding, large_gap) AS (
193  SELECT
194    -- Pad 50ms either side of a vsync
195    50000000,
196    -- A gap of >200ms between the adjacent vsyncs is treated as a gap in
197    -- animation.
198    200000000
199)
200SELECT ts + const.vsync_padding AS ts,
201  gap_to_next_vsync - const.vsync_padding * 2 AS dur, 1 AS not_animating
202FROM const, (SELECT name,
203    ts,
204    lead(ts) OVER () - ts AS gap_to_next_vsync,
205    dur
206  FROM slice
207  WHERE name = "VSync")
208WHERE gap_to_next_vsync > const.large_gap
209UNION
210-- Insert a slice between start_ts and the first vsync (or the end of the trace
211-- if there are none).
212SELECT
213  ts,
214  dur,
215  1
216FROM (SELECT start_ts AS ts,
217          COALESCE((
218              SELECT MIN(ts)
219              FROM slice
220              WHERE name = "VSync"
221            ) - start_ts - const.vsync_padding,
222            end_ts - start_ts
223          ) AS dur
224FROM trace_bounds, const)
225WHERE dur > 0
226UNION
227-- Insert a slice between the last vsync and end_ts
228SELECT last_vsync AS ts,
229  end_ts - last_vsync AS dur,
230  1
231FROM (
232    SELECT MAX(ts) + const.vsync_padding AS last_vsync
233    FROM slice, const
234    WHERE name = "VSync"
235  ),
236  trace_bounds
237WHERE last_vsync < end_ts;
238
239-- There are two types of InputLatency:: events:
240-- 1) Simple ones that begin at ts and end at ts+dur
241-- 2) Paired ones that begin with a "begin" slice and end at an "end" slice.
242--
243-- Paired events are even trickier because we can't guarantee that the "begin"
244-- slice will even be in the trace and because it's possible for multiple begin
245-- slices to appear without an intervening end slice.
246
247-- Table of begin and end events along with the increment/decrement to be
248-- applied to the appropriate counter (one for each type of paired event). Final
249-- column dur_multiplier is used to find the timestamp to mark the event at in
250-- the equation event_ts = ts + dur * dur_multiplier. End events have
251-- dur_multiplier of 1, which makes their ts the end of the slice rather than
252-- the start.
253DROP TABLE IF EXISTS input_latency_begin_end_names;
254CREATE TABLE input_latency_begin_end_names
255(
256  full_name TEXT UNIQUE,
257  prefix TEXT,
258  scroll_increment INT,
259  pinch_increment INT,
260  touch_increment INT,
261  fling_increment INT,
262  pointer_increment INT,
263  dur_multiplier INT
264);
265
266INSERT
267  OR IGNORE INTO input_latency_begin_end_names
268VALUES
269  ("InputLatency::GestureScrollBegin",
270     "InputLatency::GestureScroll", 1, 0, 0, 0, 0, 0),
271  ("InputLatency::GestureScrollEnd",
272     "InputLatency::GestureScroll", -1, 0, 0, 0, 0, 1),
273  ("InputLatency::GesturePinchBegin",
274     "InputLatency::GesturePinch", 0, 1, 0, 0, 0, 0),
275  ("InputLatency::GesturePinchEnd",
276     "InputLatency::GesturePinch", 0, -1, 0, 0, 0, 1),
277  ("InputLatency::TouchStart",
278     "InputLatency::Touch", 0, 0, 1, 0, 0, 0),
279  ("InputLatency::TouchEnd",
280     "InputLatency::Touch", 0, 0, -1, 0, 0, 1),
281  ("InputLatency::GestureFlingStart",
282     "InputLatency::GestureFling", 0, 0, 0, 1, 0, 0),
283  ("InputLatency::GestureFlingCancel",
284     "InputLatency::GestureFling", 0, 0, 0, -1, 0, 1),
285  ("InputLatency::PointerDown",
286     "InputLatency::Pointer", 0, 0, 0, 0, 1, 0),
287  ("InputLatency::PointerUp",
288     "InputLatency::Pointer", 0, 0, 0, 0, -1, 1),
289  ("InputLatency::PointerCancel",
290     "InputLatency::Pointer", 0, 0, 0, 0, -1, 1);
291
292-- Find all the slices that have split "begin" and "end" slices and maintain a
293-- running total for each type, where >0 means that type of input event is
294-- ongoing.
295DROP VIEW IF EXISTS input_begin_end_slices;
296CREATE VIEW input_begin_end_slices AS
297SELECT prefix,
298  -- Mark the change at the start of "start" slices and the end of "end" slices.
299  ts + dur * dur_multiplier AS ts,
300  scroll_increment,
301  pinch_increment,
302  touch_increment,
303  fling_increment,
304  pointer_increment
305FROM slice
306JOIN input_latency_begin_end_names ON name = full_name
307ORDER BY ts;
308
309-- Combine all the paired input events to get an indication of when any paired
310-- input event is ongoing.
311DROP VIEW IF EXISTS unified_input_pair_increments;
312CREATE VIEW unified_input_pair_increments AS
313SELECT ts,
314  scroll_increment +
315  pinch_increment +
316  touch_increment +
317  fling_increment +
318  pointer_increment AS increment
319FROM input_begin_end_slices;
320
321-- It's possible there's an end slice without a start slice (as it occurred
322-- before the trace started) which would result in (starts - ends) going
323-- negative at some point. So find an offset that shifts up all counts so the
324-- lowest values becomes zero. It's possible this could still do the wrong thing
325-- if there were start AND end slices that are outside the trace bounds, in
326-- which case it should count as covering the entire trace, but it's impossible
327-- to compensate for that without augmenting the trace events themselves.
328DROP VIEW IF EXISTS initial_paired_increment;
329CREATE VIEW initial_paired_increment AS
330SELECT ts,
331  MIN(0, MIN(scroll_total)) +
332  MIN(0, MIN(pinch_total))  +
333  MIN(0, MIN(touch_total))  +
334  MIN(0, MIN(fling_total))  +
335  MIN(0, MIN(pointer_total)) AS offset
336FROM (
337    SELECT ts,
338      SUM(scroll_increment) OVER(ROWS UNBOUNDED PRECEDING) AS scroll_total,
339      SUM(pinch_increment)  OVER(ROWS UNBOUNDED PRECEDING) AS pinch_total,
340      SUM(touch_increment)  OVER(ROWS UNBOUNDED PRECEDING) AS touch_total,
341      SUM(fling_increment)  OVER(ROWS UNBOUNDED PRECEDING) AS fling_total,
342      SUM(pointer_increment)  OVER(ROWS UNBOUNDED PRECEDING) AS pointer_total
343    FROM input_begin_end_slices
344  );
345
346-- Now find all the simple input slices that fully enclose the input they're
347-- marking (i.e. not the start or end of a pair).
348DROP VIEW IF EXISTS simple_input_slices;
349CREATE VIEW simple_input_slices AS
350SELECT id,
351  name,
352  ts,
353  dur
354FROM slice s
355WHERE name GLOB "InputLatency::*"
356  AND NOT EXISTS (
357    SELECT 1
358    FROM slice
359      JOIN input_latency_begin_end_names
360    WHERE s.name == full_name
361  );
362
363-- Turn the simple input slices into +1s and -1s at the start and end of each
364-- slice.
365DROP VIEW IF EXISTS simple_input_increments;
366CREATE VIEW simple_input_increments AS
367SELECT ts,
368  1 AS increment
369FROM simple_input_slices
370UNION ALL
371SELECT ts + dur,
372  -1
373FROM simple_input_slices
374ORDER BY ts;
375
376-- Combine simple and paired inputs into one, summing all the increments at a
377-- given ts.
378DROP VIEW IF EXISTS all_input_increments;
379CREATE VIEW all_input_increments AS
380SELECT ts,
381  SUM(increment) AS increment
382FROM (
383    SELECT *
384    FROM simple_input_increments
385    UNION ALL
386    SELECT *
387    FROM unified_input_pair_increments
388    ORDER BY ts
389  )
390GROUP BY ts;
391
392-- Now calculate the cumulative sum of the increments as each ts, giving the
393-- total number of outstanding input events at a given time.
394DROP VIEW IF EXISTS all_input_totals;
395CREATE VIEW all_input_totals AS
396SELECT ts,
397  SUM(increment) OVER(ROWS UNBOUNDED PRECEDING) > 0 AS input_total
398FROM all_input_increments;
399
400-- Now find the transitions from and to 0 and use that to create slices where
401-- input events were occurring. The input_active column always contains 1, but
402-- is there so that the SPAN_JOIN_LEFT can put NULL in it for RAIL Mode slices
403-- that do not have corresponding input events.
404DROP VIEW IF EXISTS all_input_slices;
405CREATE VIEW all_input_slices AS
406SELECT ts,
407  dur,
408  input_active
409FROM (
410    SELECT ts,
411      lead(ts, 1, end_ts) OVER() - ts AS dur,
412      input_active
413    FROM trace_bounds,
414      (
415        SELECT ts,
416          input_total > 0 AS input_active
417        FROM (
418            SELECT ts,
419              input_total,
420              lag(input_total) OVER() AS prev_input_total
421            FROM all_input_totals
422          )
423        WHERE (input_total > 0 <> prev_input_total > 0)
424          OR prev_input_total IS NULL
425      )
426  )
427WHERE input_active > 0;
428
429-- Since the scheduler defaults to animation when none of the other RAIL modes
430-- apply, animation overestimates the amount of time that actual animation is
431-- occurring.
432-- So instead we try to divide up animation in other buckets based on other
433-- trace events.
434DROP VIEW IF EXISTS rail_mode_animation_slices;
435CREATE VIEW rail_mode_animation_slices AS
436SELECT * FROM combined_overall_rail_slices WHERE rail_mode = "animation";
437
438-- Left-join rail mode animation slices with all_input_slices to find all
439-- "animation" slices that should actually be labelled "response".
440DROP TABLE IF EXISTS rail_mode_join_inputs;
441CREATE VIRTUAL TABLE rail_mode_join_inputs
442USING SPAN_LEFT_JOIN(rail_mode_animation_slices, all_input_slices);
443
444-- Left-join rail mode animation slices with not_animating_slices which is
445-- based on the gaps between vsync events.
446DROP TABLE IF EXISTS rail_mode_join_inputs_join_animation;
447CREATE VIRTUAL TABLE rail_mode_join_inputs_join_animation
448USING SPAN_LEFT_JOIN(rail_mode_join_inputs, not_animating_slices);
449
450DROP VIEW IF EXISTS has_modified_rail_slices;
451CREATE VIEW has_modified_rail_slices AS
452SELECT (
453    SELECT value
454    FROM chrome_event_metadata
455    WHERE name == "os-name"
456  ) == "Android" AS value;
457
458-- Mapping to allow CamelCased names to be produced from the modified rail
459-- modes.
460DROP TABLE IF EXISTS modified_rail_mode_prettier;
461CREATE TABLE modified_rail_mode_prettier (
462  orig_name TEXT UNIQUE,
463  pretty_name TEXT
464);
465INSERT INTO modified_rail_mode_prettier
466VALUES ("background", "Background"),
467  ("foreground_idle", "ForegroundIdle"),
468  ("animation", "Animation"),
469  ("load", "Load"),
470  ("response", "Response");
471
472-- When the RAIL mode is animation, use input/vsync data to conditionally change
473-- the mode to response or foreground_idle.
474DROP VIEW IF EXISTS unmerged_modified_rail_slices;
475CREATE VIEW unmerged_modified_rail_slices AS
476SELECT ROW_NUMBER() OVER () AS id,
477  ts,
478  dur,
479  mode
480FROM (
481    SELECT ts,
482      dur,
483      CASE
484        WHEN input_active IS NOT NULL THEN "response"
485        WHEN not_animating IS NULL THEN "animation"
486        ELSE "foreground_idle"
487      END AS mode
488    FROM rail_mode_join_inputs_join_animation
489    UNION
490    SELECT ts,
491      dur,
492      rail_mode AS mode
493    FROM combined_overall_rail_slices
494    WHERE rail_mode <> "animation"
495  )
496  -- Since VSync events are only emitted on Android (and the concept of a
497  -- unified RAIL mode only makes sense if there's just a single Chrome window),
498  -- don't output anything on other platforms. This will result in all the power
499  -- and cpu time tables being empty rather than containing bogus results.
500WHERE (
501    SELECT value
502    FROM has_modified_rail_slices
503  );
504
505-- The previous query creating unmerged_modified_rail_slices, can create
506-- adjacent slices with the same mode. This merges them together as well as
507-- adding a unique id to each slice. Rather than directly merging slices
508-- together, this instead looks for all the transitions and uses this to
509-- reconstruct the slices that should occur between them.
510DROP TABLE IF EXISTS modified_rail_slices;
511CREATE TABLE modified_rail_slices AS
512WITH const (end_ts) AS (SELECT ts + dur
513              FROM unmerged_modified_rail_slices
514              ORDER BY ts DESC
515              LIMIT 1)
516SELECT ROW_NUMBER() OVER () AS id, lag(next_ts) OVER() AS ts,
517  ts + dur - lag(next_ts) OVER() AS dur,
518  mode AS mode
519FROM (
520    -- For each row in the original table, create a new row with the information
521    -- from the following row, since you can't use lag/lead in WHERE clause.
522    --
523    -- Transition row at the beginning. "mode" is invalid, so a transition will
524    -- always be recorded.
525    SELECT *
526    FROM (SELECT
527          0 AS ts,
528          ts AS dur,
529          "" AS mode,
530          ts AS next_ts,
531          dur AS next_dur,
532          mode AS next_mode
533        FROM unmerged_modified_rail_slices
534        LIMIT 1
535      )
536    UNION ALL
537    SELECT ts,
538      dur,
539      mode,
540      lead(ts, 1, end_ts) OVER() AS next_ts,
541      lead(dur) OVER() AS next_dur,
542      lead(mode) OVER() AS next_mode
543    FROM unmerged_modified_rail_slices, const
544    UNION ALL
545    -- Transition row at the end. "next_mode" is invalid, so a transition will
546    -- always be recorded.
547    SELECT *
548    FROM (SELECT
549          ts + dur AS ts,
550          0 AS dur,
551          mode,
552          ts + dur AS next_ts,
553          0,
554          "" AS next_mode
555        FROM unmerged_modified_rail_slices
556        ORDER BY ts DESC
557        LIMIT 1
558      )
559  )
560WHERE mode <> next_mode
561-- Retrieve all but the first row.
562LIMIT -1 OFFSET 1;
563