1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
|
-- Copyright 2024 The Chromium Authors
-- Use of this source code is governed by a BSD-style license that can be
-- found in the LICENSE file.
INCLUDE PERFETTO MODULE chrome.android_input;
INCLUDE PERFETTO MODULE intervals.intersect;
INCLUDE PERFETTO MODULE slices.with_context;
-- Processing steps of the Chrome input pipeline.
CREATE PERFETTO TABLE _chrome_input_pipeline_steps_no_input_type (
-- Id of this Chrome input pipeline (LatencyInfo).
latency_id LONG,
-- Slice id
slice_id LONG,
-- The step timestamp.
ts TIMESTAMP,
-- Step duration.
dur DURATION,
-- Utid of the thread.
utid LONG,
-- Step name (ChromeLatencyInfo.step).
step STRING,
-- Input type.
input_type STRING,
-- Start time of the parent Chrome scheduler task (if any) of this step.
task_start_time_ts TIMESTAMP
) AS
WITH
steps_with_potential_duplicates AS (
SELECT
extract_arg(thread_slice.arg_set_id, 'chrome_latency_info.trace_id') AS latency_id,
id AS slice_id,
ts,
dur,
utid,
extract_arg(thread_slice.arg_set_id, 'chrome_latency_info.step') AS step,
extract_arg(thread_slice.arg_set_id, 'chrome_latency_info.input_type') AS input_type,
ts - (
extract_arg(thread_slice.arg_set_id, 'current_task.event_offset_from_task_start_time_us') * 1000
) AS task_start_time_ts
FROM thread_slice
WHERE
NOT step IS NULL AND latency_id != -1
),
steps_with_ordering AS (
SELECT
*,
-- Partition the steps so that, if the same step (for the same input) was
-- emitted more than once (e.g. due to b:390406106), the step ends up in
-- the same partition as all its duplicates. This will enable us to
-- deduplicate the steps later.
-- If there are multiple STEP_RESAMPLE_SCROLL_EVENTS steps, we assume that
-- the input was only dispatched after the last resampling, so we only
-- care about the last STEP_RESAMPLE_SCROLL_EVENTS step. We don't have any
-- preference for other steps but, for the sake of determinsm and
-- consistency, let's always pick the last step.
row_number() OVER (PARTITION BY latency_id, utid, step, input_type ORDER BY ts DESC) AS ordering_within_partition
FROM steps_with_potential_duplicates
)
SELECT
latency_id,
slice_id,
ts,
dur,
utid,
step,
input_type,
task_start_time_ts
FROM steps_with_ordering
WHERE
-- This is where we actually remove duplicate steps.
ordering_within_partition = 1
ORDER BY
slice_id,
ts;
-- Each row represents one input pipeline.
CREATE PERFETTO TABLE chrome_inputs (
-- Id of this Chrome input pipeline (LatencyInfo).
latency_id LONG,
-- Input type.
input_type STRING
) AS
SELECT
-- Id of this Chrome input pipeline (LatencyInfo).
latency_id,
-- MIN selects the first non-null value.
min(input_type) AS input_type
FROM _chrome_input_pipeline_steps_no_input_type
WHERE
latency_id != -1
GROUP BY
latency_id;
-- Since not all steps have associated input type (but all steps
-- for a given latency id should have the same input type),
-- populate input type for steps where it would be NULL.
CREATE PERFETTO TABLE chrome_input_pipeline_steps (
-- Id of this Chrome input pipeline (LatencyInfo).
latency_id LONG,
-- Slice id
slice_id LONG,
-- The step timestamp.
ts TIMESTAMP,
-- Step duration.
dur DURATION,
-- Utid of the thread.
utid LONG,
-- Step name (ChromeLatencyInfo.step).
step STRING,
-- Input type.
input_type STRING,
-- Start time of the parent Chrome scheduler task (if any) of this step.
task_start_time_ts TIMESTAMP
) AS
SELECT
latency_id,
slice_id,
ts,
dur,
utid,
step,
chrome_inputs.input_type AS input_type,
task_start_time_ts
FROM chrome_inputs
LEFT JOIN _chrome_input_pipeline_steps_no_input_type
USING (latency_id)
WHERE
chrome_inputs.input_type IS NOT NULL;
-- For each input, if it was coalesced into another input, get the other input's
-- latency id.
CREATE PERFETTO TABLE chrome_coalesced_inputs (
-- The `latency_id` of the coalesced input.
coalesced_latency_id LONG,
-- The `latency_id` of the other input that the current input was coalesced
-- into. Guaranteed to be different from `coalesced_latency_id`.
presented_latency_id LONG
) AS
SELECT
args.int_value AS coalesced_latency_id,
latency_id AS presented_latency_id
FROM chrome_input_pipeline_steps AS step
JOIN slice
USING (slice_id)
JOIN args
USING (arg_set_id)
WHERE
step.step = 'STEP_RESAMPLE_SCROLL_EVENTS'
AND args.flat_key = 'chrome_latency_info.coalesced_trace_ids'
AND coalesced_latency_id != presented_latency_id;
-- Each scroll update event (except flings) in Chrome starts its life as a touch
-- move event, which is then eventually converted to a scroll update itself.
-- Each of these events is represented by its own LatencyInfo. This table
-- contains a mapping between touch move events and scroll update events they
-- were converted into.
CREATE PERFETTO TABLE chrome_touch_move_to_scroll_update (
-- Latency id of the touch move input (LatencyInfo).
touch_move_latency_id LONG,
-- Latency id of the corresponding scroll update input (LatencyInfo).
scroll_update_latency_id LONG
) AS
WITH
scroll_update_steps AS MATERIALIZED (
SELECT
*
FROM chrome_input_pipeline_steps
WHERE
step = 'STEP_SEND_INPUT_EVENT_UI' AND input_type = 'GESTURE_SCROLL_UPDATE_EVENT'
),
-- By default, we map a scroll update event to an ancestor touch move event with
-- STEP_TOUCH_EVENT_HANDLED.
default_mapping AS MATERIALIZED (
SELECT
touch_move_handled_step.latency_id AS touch_move_latency_id,
scroll_update_step.latency_id AS scroll_update_latency_id
-- Performance optimization: we are interested in finding all of the
-- scroll_update_steps which have a "touch_move_handled" parent: to do this,
-- we intersect them and find all of the intersections which match
-- a scroll_update_step.
-- We are using `slice_id` as `id` as it's unique and 32-bit (unlike latency_id,
-- which can be 64-bit).
FROM _interval_intersect!(
(
(
SELECT slice_id AS id, *
FROM scroll_update_steps
WHERE dur > 0
),
(
SELECT slice_id AS id, *
FROM chrome_input_pipeline_steps step
WHERE step = 'STEP_TOUCH_EVENT_HANDLED'
AND dur > 0
)
),
(utid)
) AS ii
JOIN scroll_update_steps AS scroll_update_step
ON ii.id_0 = scroll_update_step.slice_id
JOIN chrome_input_pipeline_steps AS touch_move_handled_step
ON ii.id_1 = touch_move_handled_step.slice_id
-- If intersection matches the `scroll_update_step`, then it means that
-- `touch_move_handled_step` is an ancestor of `scroll_update_step`.
WHERE
ii.ts = scroll_update_step.ts AND ii.dur = scroll_update_step.dur
),
-- In the rare case where there are no touch handlers in the renderer, there's
-- no ancestor touch move event with STEP_TOUCH_EVENT_HANDLED. In that case, we
-- try to fall back to an ancestor touch move event with
-- STEP_SEND_INPUT_EVENT_UI instead.
fallback_mapping AS MATERIALIZED (
SELECT
send_touch_move_step.latency_id AS touch_move_latency_id,
scroll_update_step.latency_id AS scroll_update_latency_id
-- See the comment in the default_mapping CTE for an explanation what's going on here.
FROM _interval_intersect!(
(
(
SELECT slice_id AS id, *
FROM scroll_update_steps
WHERE dur > 0
),
(
SELECT slice_id AS id, *
FROM chrome_input_pipeline_steps step
WHERE step = 'STEP_SEND_INPUT_EVENT_UI'
AND input_type = 'TOUCH_MOVE_EVENT'
AND dur > 0
)
),
(utid)
) AS ii
JOIN scroll_update_steps AS scroll_update_step
ON ii.id_0 = scroll_update_step.slice_id
JOIN chrome_input_pipeline_steps AS send_touch_move_step
ON ii.id_1 = send_touch_move_step.slice_id
WHERE
ii.ts = scroll_update_step.ts AND ii.dur = scroll_update_step.dur
),
-- We ideally would want to do a FULL JOIN here, but it is very slow in SQLite,
-- so instead we are doing UNION + two LEFT JOINs.
scroll_update_latency_ids AS (
SELECT
scroll_update_latency_id
FROM default_mapping
UNION
SELECT
scroll_update_latency_id
FROM fallback_mapping
)
SELECT
coalesce(default_mapping.touch_move_latency_id, fallback_mapping.touch_move_latency_id) AS touch_move_latency_id,
scroll_update_latency_id
FROM scroll_update_latency_ids
LEFT JOIN default_mapping
USING (scroll_update_latency_id)
LEFT JOIN fallback_mapping
USING (scroll_update_latency_id);
-- Matches Android input id to the corresponding touch move event.
CREATE PERFETTO TABLE chrome_dispatch_android_input_event_to_touch_move (
-- Input id (assigned by the system, used by InputReader and InputDispatcher)
android_input_id STRING,
-- Latency id.
touch_move_latency_id LONG
) AS
SELECT
chrome_deliver_android_input_event.android_input_id,
latency_id AS touch_move_latency_id
FROM chrome_deliver_android_input_event
LEFT JOIN chrome_input_pipeline_steps
USING (utid)
WHERE
chrome_input_pipeline_steps.input_type = 'TOUCH_MOVE_EVENT'
AND chrome_input_pipeline_steps.step = 'STEP_SEND_INPUT_EVENT_UI'
AND chrome_deliver_android_input_event.ts <= chrome_input_pipeline_steps.ts
AND chrome_deliver_android_input_event.ts + chrome_deliver_android_input_event.dur >= chrome_input_pipeline_steps.ts + chrome_input_pipeline_steps.dur;
|