• 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
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