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