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