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 16SELECT RUN_METRIC('chrome/chrome_thread_slice.sql'); 17SELECT RUN_METRIC('chrome/scroll_flow_event_queuing_delay.sql'); 18 19-- See b/184134310 why we remove ThreadController active. 20DROP VIEW IF EXISTS blocking_tasks_no_threadcontroller_active; 21CREATE PERFETTO VIEW blocking_tasks_no_threadcontroller_active AS 22SELECT 23 slice.*, 24 ancestor.id AS task_ancestor_id, 25 ancestor.name AS task_ancestor_name 26FROM 27 chrome_thread_slice AS slice LEFT JOIN 28 ancestor_slice(slice.id) AS ancestor ON ancestor.id = slice.parent_id 29WHERE 30 slice.name != "ThreadController active" 31 AND (slice.depth = 0 OR ancestor.name = "ThreadController active"); 32 33-- Sort track ids to optimize joining with slices 34-- as engine doesn't do the sort to join in O(LogN) 35-- per row by default 36-- TODO(243897379): switch this back to a view once we understand why rolling SQLite to 37-- 3.39.2 causes slowdowns. 38DROP TABLE IF EXISTS chrome_annotated_threads_and_processes; 39CREATE PERFETTO TABLE chrome_annotated_threads_and_processes AS 40SELECT 41 thread_track.id AS track_id, 42 chrome_thread.canonical_name AS thread_name, 43 chrome_process.process_type AS process_name 44FROM 45 thread_track JOIN 46 chrome_thread JOIN 47 chrome_process ON 48 thread_track.utid = chrome_thread.utid 49 AND chrome_thread.upid = chrome_process.upid 50ORDER BY 51 track_id ASC; 52 53-- See b/166441398 & crbug/1094361 for why we remove threadpool (originally 54-- the -to-End step). In essence -to-End is often reported on the ThreadPool 55-- after the fact with explicit timestamps so it being blocked isn't noteworthy. 56-- TODO(243897379): switch this back to a view once we understand why rolling SQLite to 57-- 3.39.2 causes slowdowns. 58DROP TABLE IF EXISTS blocking_chrome_tasks_without_threadpool; 59CREATE PERFETTO TABLE blocking_chrome_tasks_without_threadpool AS 60SELECT 61 slice.*, 62 annotations.thread_name AS thread_name, 63 annotations.process_name AS process_name 64FROM 65 blocking_tasks_no_threadcontroller_active AS slice JOIN 66 chrome_annotated_threads_and_processes AS annotations ON 67 annotations.track_id = slice.track_id 68WHERE 69 NOT(thread_name GLOB "*ThreadPool*"); 70 71-- This view grabs any slice that could have prevented any GestureScrollUpdate 72-- flow event from being run (queuing delays). For RunTask we know that its 73-- generic (and thus hard to figure out whats the cause) so we grab the src 74-- location to make it more meaningful. 75-- 76-- See b/184134310 for why we allow depth = 1 and ancestor.id is null (which 77-- implies its a "ThreadController active" slice because we removed it 78-- previously). 79DROP TABLE IF EXISTS blocking_tasks_queuing_delay; 80CREATE PERFETTO TABLE blocking_tasks_queuing_delay AS 81SELECT 82 EXTRACT_ARG(slice.arg_set_id, "task.posted_from.file_name") AS file, 83 EXTRACT_ARG(slice.arg_set_id, "task.posted_from.function_name") AS function, 84 trace_id, 85 queuing_time_ns, 86 avg_vsync_interval, 87 next_track_id, 88 CASE WHEN queuing.ancestor_end <= slice.ts THEN 89 CASE WHEN slice.ts + slice.dur <= queuing.maybe_next_ancestor_ts THEN 90 slice.dur 91 ELSE 92 queuing.maybe_next_ancestor_ts - slice.ts 93 END 94 ELSE 95 CASE WHEN slice.ts + slice.dur <= queuing.maybe_next_ancestor_ts THEN 96 slice.ts + slice.dur - queuing.ancestor_end 97 ELSE 98 queuing.maybe_next_ancestor_ts - queuing.ancestor_end 99 END 100 END AS dur_overlapping_ns, 101 description, 102 scroll_slice_id, 103 scroll_ts, 104 scroll_dur, 105 scroll_track_id, 106 jank, 107 slice.* 108FROM 109 scroll_flow_event_queuing_delay queuing JOIN 110 blocking_chrome_tasks_without_threadpool AS slice ON 111 slice.ts + slice.dur > queuing.ancestor_end 112 AND queuing.maybe_next_ancestor_ts > slice.ts 113 AND slice.track_id = queuing.next_track_id 114WHERE 115 queuing_time_ns IS NOT NULL 116 AND queuing_time_ns > 0; 117 118-- Now for each toplevel task (depth = 0 from above) we want to grab all their 119-- children slices. This is done by joining on descendant_slice which is a 120-- trace processor defined operator. This will results in 1 row for every 121-- descendant slice. So all fields in base.* will be repeated ONCE for each 122-- child, but if it has no slice it will occur only once but all the 123-- |descendant_.*| fields will be NULL because of the LEFT JOIN. 124-- Additionally for mojo events we replace the descendant_name with just the 125-- "interface_name" since that is more descriptive for our jank purposes. 126DROP VIEW IF EXISTS all_descendant_blocking_tasks_queuing_delay; 127CREATE PERFETTO VIEW all_descendant_blocking_tasks_queuing_delay AS 128SELECT 129 descendant.id AS descendant_id, 130 descendant.ts AS descendant_ts, 131 descendant.dur AS descendant_dur, 132 COALESCE( 133 IIF(descendant.arg_set_id IS NOT NULL, 134 EXTRACT_ARG(descendant.arg_set_id, 135 "chrome_mojo_event_info.watcher_notify_interface_tag"), 136 NULL), 137 IIF(descendant.arg_set_id IS NOT NULL, 138 EXTRACT_ARG(descendant.arg_set_id, 139 "chrome_mojo_event_info.mojo_interface_tag"), 140 NULL), 141 descendant.name) AS descendant_name, 142 EXTRACT_ARG(descendant.arg_set_id, 143 "chrome_mojo_event_info.ipc_hash") AS descendant_ipc_hash, 144 descendant.parent_id AS descendant_parent_id, 145 descendant.depth AS descendant_depth, 146 descendant.category AS descendant_category, 147 base.* 148FROM 149 blocking_tasks_queuing_delay base LEFT JOIN 150 descendant_slice(base.id) AS descendant; 151 152DROP TABLE IF EXISTS all_descendant_blocking_tasks_queuing_delay_with_cpu_time; 153CREATE PERFETTO TABLE all_descendant_blocking_tasks_queuing_delay_with_cpu_time AS 154SELECT 155 cpu.thread_dur AS descendant_thread_dur, 156 CAST(cpu.thread_dur AS REAL) / descendant.thread_dur 157 AS descendant_cpu_percentage, 158 CAST(cpu.thread_dur AS REAL) 159 / (descendant.thread_dur 160 / (1 << (descendant.descendant_depth - 1))) > 0.5 161 AS descendant_cpu_time_above_relative_threshold, 162 descendant_dur / descendant.dur AS descendant_dur_percentage, 163 descendant_dur 164 / (descendant.dur / (1 << (descendant.descendant_depth - 1))) > 0.5 165 AS descendant_dur_above_relative_threshold, 166 descendant.* 167FROM 168 all_descendant_blocking_tasks_queuing_delay descendant LEFT JOIN ( 169 SELECT 170 id, thread_dur 171 FROM chrome_thread_slice 172 ) AS cpu ON 173 cpu.id = descendant.descendant_id; 174 175-- Now that we've generated the descendant count how many siblings each row 176-- has. Recall that all the top level tasks are repeated but each row represents 177-- a descendant slice. This means since we LEFT JOIN we will say a slice has 0 178-- siblings if it has no descendants (which is true), and otherwise we will 179-- compute the siblings as the count of all slices with the same parent minus 180-- the current slice. 181DROP VIEW IF EXISTS counted_descendant_blocking_tasks_queuing_delay; 182CREATE PERFETTO VIEW counted_descendant_blocking_tasks_queuing_delay AS 183SELECT 184 base.*, 185 COALESCE(single_descendant.number_of_siblings, 0) AS number_of_siblings 186FROM 187 all_descendant_blocking_tasks_queuing_delay_with_cpu_time base LEFT JOIN ( 188 SELECT 189 descendant_parent_id, 190 COUNT(*) - 1 AS number_of_siblings 191 FROM all_descendant_blocking_tasks_queuing_delay_with_cpu_time 192 WHERE descendant_parent_id IS NOT NULL 193 GROUP BY 1 194 ) single_descendant ON 195 single_descendant.descendant_parent_id = base.descendant_parent_id; 196 197-- Now we group by the |id| which is the top level task id and find the first 198-- descendant_depth where we have a sibling. We need this because we only want 199-- to include single descendant slices in our metric name to keep it easy to 200-- reason about what that code is doing. 201DROP VIEW IF EXISTS blocking_tasks_queuing_delay_with_invalid_depth; 202CREATE PERFETTO VIEW blocking_tasks_queuing_delay_with_invalid_depth AS 203SELECT 204 base.*, 205 ( 206 descendant_cpu_time_above_relative_threshold 207 AND descendant_cpu_percentage > 0.05 208 ) OR ( 209 descendant_dur_above_relative_threshold 210 AND descendant_dur_percentage > 0.05 211 ) AS descendant_major_slice, 212 COALESCE(depth.invalid_depth, 10) AS invalid_depth 213FROM 214 counted_descendant_blocking_tasks_queuing_delay base LEFT JOIN ( 215 SELECT 216 id, 217 MIN(descendant_depth) AS invalid_depth 218 FROM counted_descendant_blocking_tasks_queuing_delay 219 WHERE number_of_siblings >= 1 220 GROUP BY 1 221 ) AS depth ON base.id = depth.id 222ORDER BY 223 descendant_depth ASC, 224 descendant_cpu_percentage DESC, 225 descendant_dur_percentage DESC; 226 227-- Now to get back to a single output per top level task we group by all the 228-- toplevel fields and aggregate the descendant fields. We only include the 229-- descendant if their depth is less than the first depth with siblings (the 230-- |invalid_depth|). 231DROP VIEW IF EXISTS descendant_blocking_tasks_queuing_delay; 232CREATE PERFETTO VIEW descendant_blocking_tasks_queuing_delay AS 233SELECT 234 id, 235 ts, 236 dur, 237 track_id, 238 trace_id, 239 name, 240 category, 241 scroll_slice_id AS scroll_id, 242 scroll_ts, 243 scroll_dur, 244 scroll_track_id, 245 jank, 246 queuing_time_ns, 247 dur_overlapping_ns, 248 description, 249 replace(file, rtrim(file, replace(file, '/', '')), '') AS file, 250 thread_name, 251 process_name, 252 function, 253 avg_vsync_interval, 254 GROUP_CONCAT( 255 CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN 256 descendant_id 257 ELSE 258 NULL 259 END, 260 "-") AS descendant_id, 261 GROUP_CONCAT( 262 CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN 263 descendant_ts 264 ELSE 265 NULL 266 END, 267 "-") AS descendant_ts, 268 GROUP_CONCAT( 269 CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN 270 descendant_dur 271 ELSE 272 NULL 273 END, 274 "-") AS descendant_dur, 275 GROUP_CONCAT( 276 CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN 277 descendant_name 278 ELSE 279 NULL 280 END, "-") AS descendant_name, 281 GROUP_CONCAT( 282 CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN 283 descendant_thread_dur 284 ELSE 285 NULL 286 END, 287 "-") AS descendant_thread_dur, 288 GROUP_CONCAT( 289 CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN 290 descendant_cpu_percentage 291 ELSE 292 NULL 293 END, 294 "-") AS descendant_cpu_time, 295 GROUP_CONCAT( 296 CASE WHEN descendant_category = "mojom" THEN 297 descendant_name 298 ELSE 299 NULL 300 END, 301 "-") AS mojom_name, 302 -- All ipc_hashes should be equal so just select the first non-null one. 303 MIN(descendant_ipc_hash) AS mojom_ipc_hash, 304 GROUP_CONCAT( 305 CASE WHEN 306 descendant_category = "toplevel" 307 AND descendant_name NOT GLOB "*ThreadController*" THEN 308 descendant_name 309 ELSE 310 NULL 311 END, 312 "-") AS toplevel_name, 313 GROUP_CONCAT( 314 CASE WHEN descendant_category = "Java" THEN 315 descendant_name 316 ELSE 317 NULL 318 END, 319 "-") AS java_name 320FROM 321 blocking_tasks_queuing_delay_with_invalid_depth 322GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 323ORDER BY descendant_cpu_percentage DESC; 324 325-- Function prototype: takes a - separated list of slice names (formed by 326-- the GROUP_CONCAT above) and returns the first slice if any or NULL 327-- otherwise. 328CREATE OR REPLACE PERFETTO FUNCTION get_first_slice_name_or_null(name STRING) 329-- Returns the first slice name or NULL 330RETURNS STRING AS 331-- Performs the actual string modification, takes the either the whole string 332-- if there is no - or up to the first '-'. SUBSTR returns NULL if $name is 333-- NULL. 334SELECT SUBSTR($name, 0, 335 CASE WHEN INSTR($name, "-") = 0 THEN 336 LENGTH($name)+1 ELSE 337 INSTR($name, "-") 338 END); 339 340-- Function prototype: takes a - separated list of slice names (formed by 341-- the GROUP_CONCAT above) and checks for certain important view names and 342-- falls back on get_first_slice_name_or_null if it can't find one. 343CREATE OR REPLACE PERFETTO FUNCTION get_java_slice_summary_or_null(name STRING) 344-- Returns the summary of the provided list of java slice names. 345RETURNS STRING AS 346-- Performs a bunch of GLOB matches in an order, now there could be multiple 347-- matches (both Toolbar & TabList could be true) so the order matters in 348-- tagging since we dont support multiple tagging of values. Ideally we would 349-- determine which one was the longest duration, but this should be sufficient 350-- for now. 351SELECT 352 CASE WHEN $name GLOB "*ToolbarControlContainer*" THEN 353 "ToolbarControlContainer" 354 WHEN $name GLOB "*ToolbarProgressBar*" THEN 355 "ToolbarProgressBar" 356 WHEN $name GLOB "*TabGroupUiToolbarView*" THEN 357 "TabGroupUiToolbarView" 358 WHEN $name GLOB "*TabGridThumbnailView*" THEN 359 "TabGridThumbnailView" 360 WHEN $name GLOB "*TabThumbnailView" THEN 361 "TabThumbnailView" 362 WHEN $name GLOB "*TabGridDialogView*" THEN 363 "TabGridDialogView" 364 WHEN $name GLOB "*BottomContainer*" THEN 365 "BottomContainer" 366 WHEN $name GLOB "*FeedSwipeRefreshLayout*" THEN 367 "FeedSwipeRefreshLayout" 368 WHEN $name GLOB "*AutocompleteEditText*" THEN 369 "AutocompleteEditText" 370 WHEN $name GLOB "*HomeButton*" THEN 371 "HomeButton" 372 WHEN $name GLOB "*ToggleTabStackButton*" THEN 373 "ToggleTabStackButton" 374 WHEN $name GLOB "*ListMenuButton*" THEN 375 "ListMenuButton" 376 WHEN $name GLOB "*ScrimView*" THEN 377 "ScrimView" 378 WHEN $name GLOB "*ChromeImageView*" THEN 379 "ChromeImageView" 380 WHEN $name GLOB "*AppCompatImageView*" THEN 381 "AppCompatImageView" 382 WHEN $name GLOB "*ChromeImageButton*" THEN 383 "ChromeImageButton" 384 WHEN $name GLOB "*AppCompatImageButton*" THEN 385 "AppCompatImageButton" 386 WHEN $name GLOB "*TabListRecyclerView*" THEN 387 "TabListRecyclerView" 388 ELSE 389 get_first_slice_name_or_null($name) 390 END; 391 392-- Function prototype: takes slice name, category and descendant_name and 393-- determines if this event should be classified as unknown or not. 394-- 395-- Returns either "-UnknownEvent" or "". 396CREATE OR REPLACE PERFETTO FUNCTION unknown_event_or_empty_string(name STRING, 397 cat STRING, 398 has_descendant STRING) 399RETURNS STRING AS 400-- If our current event has a posted from we consider it already categorized 401-- even if we dont have events underneath it. If its java often we wont have 402-- sub events, and finally if its a single event we just use its name there 403-- isn't anything under to use so just leave it at that. 404SELECT 405 CASE WHEN 406 $name = "ThreadControllerImpl::RunTask" OR 407 $cat = "Java" OR 408 $has_descendant IS NULL THEN 409 "" ELSE 410 "-UnknownEvent" 411 END; 412 413-- Function prototype: Takes a slice name, function, and file, and determines 414-- if we should use the slice name, or if its a RunTask event uses the 415-- function & file name, however if the RunTask posted from is one of the 416-- simple_watcher paths we collapse them for attributation. 417CREATE OR REPLACE PERFETTO FUNCTION top_level_name(name STRING, function STRING, file STRING) 418RETURNS STRING AS 419-- The difference for the mojom functions are: 420-- 1) PostDispatchNextMessageFromPipe: 421-- We knew that there is a message in the pipe, didnt try to set up a 422-- SimpleWatcher to monitor when a new one arrives. 423-- 2) ArmOrNotify: 424-- We tried to set up SimpleWatcher, but the setup failed as the 425-- message arrived as we were setting this up, so we posted a task 426-- instead. 427-- 3) Notify: 428-- SimpleWatcher was set up and after a period of monitoring detected 429-- a new message. 430-- For our jank use case this distinction isnt very useful so we group them 431-- together. 432SELECT 433 CASE WHEN $name = "ThreadControllerImpl::RunTask" THEN 434 CASE WHEN $function IN 435 ("PostDispatchNextMessageFromPipe", "ArmOrNotify", "Notify") THEN 436 "posted-from-mojo-pipe" 437 ELSE 438 "posted-from-" || $function || "()-in-" || $file 439 END 440 ELSE 441 $name 442 END; 443 444-- Create a common name for each "cause" based on the slice stack we found. 445DROP VIEW IF EXISTS scroll_jank_cause_queuing_delay_temp; 446CREATE PERFETTO VIEW scroll_jank_cause_queuing_delay_temp AS 447SELECT 448 top_level_name(name, function, file) || COALESCE( 449 "-" || descendant_name, "") AS location, 450 top_level_name(name, function, file) || COALESCE( 451 "-" || get_first_slice_name_or_null(mojom_name) 452 || COALESCE("(ipc=" || mojom_ipc_hash || ")", ""), 453 "-" || get_first_slice_name_or_null(toplevel_name) 454 || COALESCE("(ipc=" || mojom_ipc_hash || ")", ""), 455 "-" || get_java_slice_summary_or_null(java_name), 456 unknown_event_or_empty_string(name, category, descendant_name) 457 ) AS restricted_location, 458 base.* 459FROM descendant_blocking_tasks_queuing_delay base; 460 461-- Figure out the average time taken during non-janky scrolls updates for each 462-- TraceEvent (metric_name) stack. 463DROP VIEW IF EXISTS scroll_jank_cause_queuing_delay_average_no_jank_time; 464CREATE PERFETTO VIEW scroll_jank_cause_queuing_delay_average_no_jank_time AS 465SELECT 466 location, 467 AVG(dur_overlapping_ns) AS avg_dur_overlapping_ns 468FROM scroll_jank_cause_queuing_delay_temp 469WHERE NOT jank 470GROUP BY 1; 471 472-- Again figure out the average time, but based on a more restricted set of 473-- trace events. 474DROP VIEW IF EXISTS scroll_jank_cause_queuing_delay_average_no_jank_time_restricted; 475CREATE PERFETTO VIEW scroll_jank_cause_queuing_delay_average_no_jank_time_restricted AS 476SELECT 477 restricted_location, 478 AVG(dur_overlapping_ns) AS avg_dur_overlapping_ns_restricted 479FROM scroll_jank_cause_queuing_delay_temp 480WHERE NOT jank 481GROUP BY 1; 482 483 484-- Join every row (jank and non-jank with the average non-jank time for the 485-- given metric_name). 486DROP VIEW IF EXISTS scroll_jank_cause_queuing_delay_unannotated; 487CREATE PERFETTO VIEW scroll_jank_cause_queuing_delay_unannotated AS 488SELECT 489 base.*, 490 'InputLatency.LatencyInfo.Flow.QueuingDelay.' 491 || CASE WHEN jank THEN 'Jank' ELSE 'NoJank' END || '.BlockingTasksUs.' 492 || base.location AS metric_name, 493 COALESCE(avg_no_jank.avg_dur_overlapping_ns, 0) 494 AS avg_no_jank_dur_overlapping_ns 495FROM 496 scroll_jank_cause_queuing_delay_temp base LEFT JOIN 497 scroll_jank_cause_queuing_delay_average_no_jank_time avg_no_jank ON 498 base.location = avg_no_jank.location; 499 500-- Join in the restricted set of trace events average as well to form the final output. 501DROP VIEW IF EXISTS scroll_jank_cause_queuing_delay; 502CREATE PERFETTO VIEW scroll_jank_cause_queuing_delay AS 503SELECT 504 base.*, 505 'QueuingDelay.' 506 || CASE WHEN jank THEN 'Jank' ELSE 'NoJank' END || '.BlockingTasksUs.' 507 || base.restricted_location AS restricted_metric_name, 508 COALESCE(avg_no_jank.avg_dur_overlapping_ns_restricted, 0) 509 AS avg_no_jank_dur_overlapping_ns_restricted 510FROM 511 scroll_jank_cause_queuing_delay_unannotated base LEFT JOIN 512 scroll_jank_cause_queuing_delay_average_no_jank_time_restricted avg_no_jank ON 513 base.restricted_location = avg_no_jank.restricted_location; 514