metrics: Add touch_jank.sql

To share code between touch_jank.sql and scroll_jank.sql, this converts
scroll_jank.sql into a templated metric stored in gesture_jank.sql.
scroll_jank.sql and touch_jank.sql then just differ in their template
parameters.

Like scroll_jank.sql, touch_jank.sql only covers update/move events that
reach the GPU. In future we will also track TouchMove events that don't,
but will keep them separate so the events in each metric are actually
comparable.

Bug: 182160743
Change-Id: I916955280a729152eb05a35c8592e0080fceecf6
diff --git a/Android.bp b/Android.bp
index 570fb04..bb73b17 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3686,6 +3686,7 @@
     "protos/perfetto/metrics/chrome/reported_by_page.proto",
     "protos/perfetto/metrics/chrome/scroll_jank.proto",
     "protos/perfetto/metrics/chrome/test_chrome_metric.proto",
+    "protos/perfetto/metrics/chrome/touch_jank.proto",
     "protos/perfetto/metrics/custom_options.proto",
     "protos/perfetto/metrics/metrics.proto",
   ],
@@ -8043,6 +8044,7 @@
     "src/trace_processor/metrics/chrome/cpu_time_by_rail_mode.sql",
     "src/trace_processor/metrics/chrome/estimated_power_by_category.sql",
     "src/trace_processor/metrics/chrome/estimated_power_by_rail_mode.sql",
+    "src/trace_processor/metrics/chrome/gesture_jank.sql",
     "src/trace_processor/metrics/chrome/rail_modes.sql",
     "src/trace_processor/metrics/chrome/scroll_flow_event.sql",
     "src/trace_processor/metrics/chrome/scroll_flow_event_queuing_delay.sql",
@@ -8053,6 +8055,7 @@
     "src/trace_processor/metrics/chrome/scroll_jank_cause_get_bitmap.sql",
     "src/trace_processor/metrics/chrome/scroll_jank_cause_queuing_delay.sql",
     "src/trace_processor/metrics/chrome/test_chrome_metric.sql",
+    "src/trace_processor/metrics/chrome/touch_jank.sql",
     "src/trace_processor/metrics/experimental/blink_gc_metric.sql",
     "src/trace_processor/metrics/experimental/frame_times.sql",
     "src/trace_processor/metrics/experimental/media_metric.sql",
diff --git a/BUILD b/BUILD
index 7a9483c..97a23a4 100644
--- a/BUILD
+++ b/BUILD
@@ -1071,6 +1071,7 @@
         "src/trace_processor/metrics/chrome/cpu_time_by_rail_mode.sql",
         "src/trace_processor/metrics/chrome/estimated_power_by_category.sql",
         "src/trace_processor/metrics/chrome/estimated_power_by_rail_mode.sql",
+        "src/trace_processor/metrics/chrome/gesture_jank.sql",
         "src/trace_processor/metrics/chrome/rail_modes.sql",
         "src/trace_processor/metrics/chrome/scroll_flow_event.sql",
         "src/trace_processor/metrics/chrome/scroll_flow_event_queuing_delay.sql",
@@ -1081,6 +1082,7 @@
         "src/trace_processor/metrics/chrome/scroll_jank_cause_get_bitmap.sql",
         "src/trace_processor/metrics/chrome/scroll_jank_cause_queuing_delay.sql",
         "src/trace_processor/metrics/chrome/test_chrome_metric.sql",
+        "src/trace_processor/metrics/chrome/touch_jank.sql",
         "src/trace_processor/metrics/experimental/blink_gc_metric.sql",
         "src/trace_processor/metrics/experimental/frame_times.sql",
         "src/trace_processor/metrics/experimental/media_metric.sql",
@@ -2591,6 +2593,7 @@
         "protos/perfetto/metrics/chrome/reported_by_page.proto",
         "protos/perfetto/metrics/chrome/scroll_jank.proto",
         "protos/perfetto/metrics/chrome/test_chrome_metric.proto",
+        "protos/perfetto/metrics/chrome/touch_jank.proto",
     ],
     visibility = [
         PERFETTO_CONFIG.proto_library_visibility,
diff --git a/protos/perfetto/metrics/chrome/BUILD.gn b/protos/perfetto/metrics/chrome/BUILD.gn
index 0789138..ce0c908 100644
--- a/protos/perfetto/metrics/chrome/BUILD.gn
+++ b/protos/perfetto/metrics/chrome/BUILD.gn
@@ -28,6 +28,7 @@
     "reported_by_page.proto",
     "scroll_jank.proto",
     "test_chrome_metric.proto",
+    "touch_jank.proto",
   ]
 }
 
diff --git a/protos/perfetto/metrics/chrome/all_chrome_metrics.proto b/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
index eb52099..1f21764 100644
--- a/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
+++ b/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
@@ -22,9 +22,10 @@
 import "protos/perfetto/metrics/chrome/blink_gc_metric.proto";
 import "protos/perfetto/metrics/chrome/frame_times.proto";
 import "protos/perfetto/metrics/chrome/media_metric.proto";
+import "protos/perfetto/metrics/chrome/reported_by_page.proto";
 import "protos/perfetto/metrics/chrome/scroll_jank.proto";
 import "protos/perfetto/metrics/chrome/test_chrome_metric.proto";
-import "protos/perfetto/metrics/chrome/reported_by_page.proto";
+import "protos/perfetto/metrics/chrome/touch_jank.proto";
 
 extend TraceMetrics {
   optional TestChromeMetric test_chrome_metric = 1001;
@@ -33,4 +34,5 @@
   optional ScrollJank scroll_jank = 1004;
   optional BlinkGcMetric blink_gc_metric = 1005;
   optional MediaMetric media_metric = 1006;
+  optional TouchJank touch_jank = 1007;
 }
diff --git a/protos/perfetto/metrics/chrome/touch_jank.proto b/protos/perfetto/metrics/chrome/touch_jank.proto
new file mode 100644
index 0000000..beebfb5
--- /dev/null
+++ b/protos/perfetto/metrics/chrome/touch_jank.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+import "protos/perfetto/metrics/custom_options.proto";
+
+message TouchJank {
+  // The percentage of time we consider janky of the total time spent performing
+  // touch gestures during the trace. I.E. approximately equal to
+  // |touch_jank_ms|/|touch_ms|.
+  optional double touch_jank_percentage = 1 [(unit) = "n%_smallerIsBetter"];
+  optional double touch_ms = 2 [(unit) = "ms_biggerIsBetter"];
+  optional double touch_processing_ms = 3 [(unit) = "ms_biggerIsBetter"];
+  optional double touch_jank_processing_ms = 4 [(unit) = "ms_smallerIsBetter"];
+  optional int64 num_touch_update_count = 5 [(unit) = "count_biggerIsBetter"];
+  optional int64 num_touch_update_jank_count = 6
+      [(unit) = "count_smallerIsBetter"];
+}
diff --git a/src/trace_processor/metrics/BUILD.gn b/src/trace_processor/metrics/BUILD.gn
index 01f68e3..84bb2cc 100644
--- a/src/trace_processor/metrics/BUILD.gn
+++ b/src/trace_processor/metrics/BUILD.gn
@@ -78,6 +78,7 @@
   "chrome/cpu_time_by_rail_mode.sql",
   "chrome/estimated_power_by_category.sql",
   "chrome/estimated_power_by_rail_mode.sql",
+  "chrome/gesture_jank.sql",
   "chrome/rail_modes.sql",
   "chrome/scroll_jank.sql",
   "chrome/scroll_jank_cause.sql",
@@ -88,6 +89,7 @@
   "chrome/scroll_flow_event.sql",
   "chrome/scroll_flow_event_queuing_delay.sql",
   "chrome/test_chrome_metric.sql",
+  "chrome/touch_jank.sql",
   "experimental/blink_gc_metric.sql",
   "experimental/frame_times.sql",
   "experimental/media_metric.sql",
diff --git a/src/trace_processor/metrics/chrome/gesture_jank.sql b/src/trace_processor/metrics/chrome/gesture_jank.sql
new file mode 100644
index 0000000..e88faaf
--- /dev/null
+++ b/src/trace_processor/metrics/chrome/gesture_jank.sql
@@ -0,0 +1,319 @@
+--
+-- Copyright 2021 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- A collection of templated metrics related to continuous motion gestures that
+-- have start, end and update events.
+--
+-- We define an update to be janky if comparing forwards or backwards (ignoring
+-- coalesced updates) a given updates exceeds the duration of its predecessor or
+-- successor by 50% of a vsync interval (defaulted to 60 FPS).
+--
+-- WARNING: This metric should not be used as a source of truth. It is under
+--          active development and the values & meaning might change without
+--          notice.
+
+-- Get all chrome processes and threads tables set up.
+SELECT RUN_METRIC('chrome/chrome_processes.sql');
+
+-- When working on InputLatency events we need to ensure we have all the events
+-- from the browser, renderer, and GPU processes. This query isn't quite
+-- perfect. In system tracing we could have 3 browser processes all in the
+-- background and this would match, but for now its the best we can do (renderer
+-- and GPU names on android are quite complicated, but this should filter 99%
+-- (citation needed) of what we want.
+--
+-- See b/151077536 for historical context.
+-- TODO(b/197841224): Refactor or remove this table.
+DROP VIEW IF EXISTS sufficient_chrome_processes;
+CREATE VIEW sufficient_chrome_processes AS
+  SELECT
+    CASE WHEN (
+      SELECT COUNT(*) FROM chrome_process) = 0
+    THEN
+      FALSE
+    ELSE (
+      SELECT COUNT(*) >= 3 FROM (
+        SELECT name FROM chrome_process
+        WHERE
+          name LIKE "Browser" OR
+          name LIKE "Renderer" OR
+          name LIKE "Gpu" OR
+          name LIKE 'com.android.chrome%' OR
+          name LIKE 'com.chrome.beta%' OR
+          name LIKE 'com.chrome.dev%' OR
+          name LIKE 'com.chrome.canary%' OR
+          name LIKE 'com.google.android.apps.chrome%' OR
+          name LIKE 'org.chromium.chrome%'
+        GROUP BY name
+    )) END AS have_enough_chrome_processes;
+
+-- A simple table that checks the time between VSync (this can be used to
+-- determine if we're refreshing at 90 FPS or 60 FPS.
+--
+-- Note: In traces without the "Java" category there will be no VSync
+--       TraceEvents and this table will be empty.
+--
+-- Note: Must be a TABLE because it uses a window function which can behave
+--       strangely in views.
+DROP TABLE IF EXISTS vsync_intervals;
+CREATE TABLE vsync_intervals AS
+  SELECT
+    slice_id,
+    ts,
+    dur,
+    track_id,
+    LEAD(ts) OVER(PARTITION BY track_id ORDER BY ts) - ts AS time_to_next_vsync
+  FROM slice
+  WHERE name = "VSync"
+  ORDER BY track_id, ts;
+
+-- Get all the "begin" and "end" events. We take their IDs to group them
+-- together into gestures later and the timestamp and duration to compute the
+-- duration of the gesture.
+DROP VIEW IF EXISTS {{prefix}}_begin_and_end;
+CREATE VIEW {{prefix}}_begin_and_end AS
+  SELECT
+    slice.name,
+    slice.id,
+    slice.ts,
+    slice.dur,
+    slice.track_id,
+    EXTRACT_ARG(arg_set_id, 'chrome_latency_info.{{id_field}}')
+        AS {{id_field}},
+    EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id
+  FROM
+    slice
+  WHERE
+    slice.name IN (
+      'InputLatency::{{gesture_start}}',
+      'InputLatency::{{gesture_end}}'
+    )
+  ORDER BY ts;
+
+-- Now we take the "begin" and the "end" events and join the information into a
+-- single row per gesture. We also compute the average Vysnc interval of the
+-- gesture (hopefully this would be either 60 FPS for the whole gesture or 90
+-- FPS but that isn't always the case). If the trace doesn't contain the VSync
+-- TraceEvent we just fall back on assuming its 60 FPS (this is the 1.6e+7 in
+-- the COALESCE which corresponds to 16 ms or 60 FPS).
+DROP VIEW IF EXISTS joined_{{prefix}}_begin_and_end;
+CREATE VIEW joined_{{prefix}}_begin_and_end AS
+  SELECT
+    begin.id AS begin_id,
+    begin.ts AS begin_ts,
+    begin.dur AS begin_dur,
+    begin.track_id AS begin_track_id,
+    begin.trace_id AS begin_trace_id,
+    COALESCE(begin.{{id_field}}, begin.trace_id)
+        AS begin_{{id_field}},
+    end.ts AS end_ts,
+    end.ts + end.dur AS end_ts_and_dur,
+    end.trace_id AS end_trace_id,
+    COALESCE((
+      SELECT
+        CAST(AVG(time_to_next_vsync) AS FLOAT)
+      FROM vsync_intervals in_query
+      WHERE
+        time_to_next_vsync IS NOT NULL AND
+        in_query.ts > begin.ts AND
+        in_query.ts < end.ts
+    ), 1e+9 / 60) AS avg_vsync_interval
+  FROM {{prefix}}_begin_and_end begin JOIN {{prefix}}_begin_and_end end ON
+    begin.trace_id < end.trace_id AND
+    begin.name = 'InputLatency::{{gesture_start}}' AND
+    end.name = 'InputLatency::{{gesture_end}}' AND (
+      (
+        begin.{{id_field}} IS NULL AND
+        end.trace_id = (
+          SELECT MIN(trace_id)
+          FROM {{prefix}}_begin_and_end in_query
+          WHERE
+            name = 'InputLatency::{{gesture_end}}' AND
+          in_query.trace_id > begin.trace_id
+        )
+      ) OR
+      end.{{id_field}} = begin.{{id_field}}
+    )
+  ORDER BY begin.ts;
+
+-- Get the "update" events by name ordered by the |{{id_field}}|, and
+-- timestamp. Then compute the number of frames (relative to vsync interval)
+-- that each event took. 1.6e+7 is 16 ms in nanoseconds and is used in case
+-- there are no VSync events to default to 60 fps. We join each
+-- {{gesture_update}} event to the information about its "begin" and "end"
+-- events for easy computation later.
+--
+-- We remove updates with |dur| == -1 because this means we have no "end" event
+-- and can't reasonably determine what it should be. We have separate tracking
+-- to ensure this only happens at the end of the trace where its expected.
+--
+-- Note: Must be a TABLE because it uses a window function which can behave
+--       strangely in views.
+DROP TABLE IF EXISTS {{id_field}}_update;
+CREATE TABLE {{id_field}}_update AS
+  SELECT
+    ROW_NUMBER() OVER (
+      ORDER BY {{id_field}} ASC, ts ASC) AS row_number,
+    begin_id,
+    begin_ts,
+    begin_dur,
+    begin_track_id,
+    begin_trace_id,
+    COALESCE({{id_field}}, begin_trace_id) AS {{id_field}},
+    CASE WHEN
+      end_ts_and_dur > ts + dur THEN
+        end_ts_and_dur
+      ELSE
+        ts + dur
+      END AS maybe_gesture_end,
+    id,
+    ts,
+    dur,
+    track_id,
+    trace_id,
+    dur/avg_vsync_interval AS gesture_frames_exact
+  FROM joined_{{prefix}}_begin_and_end begin_and_end JOIN (
+    SELECT
+      EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id,
+      EXTRACT_ARG(arg_set_id, 'chrome_latency_info.{{id_field}}')
+          AS {{id_field}},
+      *
+    FROM
+      slice JOIN track ON slice.track_id = track.id
+    WHERE
+      slice.name = 'InputLatency::{{gesture_update}}' AND
+      slice.dur != -1 AND
+      NOT COALESCE(
+              EXTRACT_ARG(arg_set_id, "chrome_latency_info.is_coalesced"),
+              TRUE)
+      AND slice.arg_set_id IN (
+        SELECT arg_set_id
+        FROM args
+        WHERE args.arg_set_id = slice.arg_set_id
+        AND flat_key = 'chrome_latency_info.component_info.component_type'
+        AND string_value = 'COMPONENT_INPUT_EVENT_GPU_SWAP_BUFFER'
+      )
+  ) gesture_update ON
+  gesture_update.ts <= begin_and_end.end_ts AND
+  gesture_update.ts >= begin_and_end.begin_ts AND
+  gesture_update.trace_id > begin_and_end.begin_trace_id AND
+  gesture_update.trace_id < begin_and_end.end_trace_id AND (
+    gesture_update.{{id_field}} IS NULL OR
+    gesture_update.{{id_field}} = begin_and_end.begin_{{id_field}}
+  );
+
+-- This takes the "update" events and joins it to the previous "update" event
+-- (previous row and NULL if there isn't one) and the next "update" event (next
+-- row and again NULL if there isn't one). Then we compute the duration of the
+-- event (relative to fps) and see if it increased by more than 0.5 (which is
+-- 1/2 of 16 ms at 60 fps, and so on).
+--
+-- A small number is added to 0.5 in order to make sure that the comparison does
+-- not filter out ratios that are precisely 0.5, which can fall a little above
+-- or below exact value due to inherent inaccuracy of operations with
+-- floating-point numbers. Value 1e-9 have been chosen as follows: the ratio has
+-- nanoseconds in numerator and VSync interval in denominator. Assuming refresh
+-- rate more than 1 FPS (and therefore VSync interval less than a second), this
+-- ratio should increase with increments more than minimal value in numerator
+-- (1ns) divided by maximum value in denominator, giving 1e-9.
+--
+--
+-- We only compare an "update" event to another event within the same gesture
+-- ({{id_field}} == prev/next {{id_field}}). This controls somewhat for
+-- variability of gestures.
+--
+-- Note: Must be a TABLE because it uses a window function which can behave
+--       strangely in views.
+DROP TABLE IF EXISTS {{prefix}}_jank_maybe_null_prev_and_next;
+CREATE TABLE {{prefix}}_jank_maybe_null_prev_and_next AS
+  SELECT
+    currprev.*,
+    CASE WHEN
+      currprev.{{id_field}} != prev_{{id_field}} OR
+      prev_ts IS NULL OR
+      prev_ts < currprev.begin_ts OR
+      prev_ts > currprev.maybe_gesture_end
+    THEN
+      FALSE
+    ELSE
+      currprev.gesture_frames_exact > prev_gesture_frames_exact + 0.5 + 1e-9
+    END AS prev_jank,
+    CASE WHEN
+      currprev.{{id_field}} != next.{{id_field}} OR
+      next.ts IS NULL OR
+      next.ts < currprev.begin_ts OR
+      next.ts > currprev.maybe_gesture_end
+    THEN
+      FALSE
+    ELSE
+      currprev.gesture_frames_exact > next.gesture_frames_exact + 0.5 + 1e-9
+    END AS next_jank,
+    next.gesture_frames_exact AS next_gesture_frames_exact
+  FROM (
+    SELECT
+      curr.*,
+      curr.maybe_gesture_end - curr.begin_ts AS gesture_dur,
+      prev.ts AS prev_ts,
+      prev.{{id_field}} AS prev_{{id_field}},
+      prev.gesture_frames_exact AS prev_gesture_frames_exact
+    FROM
+      {{id_field}}_update curr LEFT JOIN
+      {{id_field}}_update prev ON prev.row_number + 1 = curr.row_number
+  ) currprev LEFT JOIN
+  {{id_field}}_update next ON currprev.row_number + 1 = next.row_number
+  ORDER BY currprev.{{id_field}} ASC, currprev.ts ASC;
+
+-- This just uses prev_jank and next_jank to see if each "update" event is a
+-- jank.
+DROP VIEW IF EXISTS {{prefix}}_jank;
+CREATE VIEW {{prefix}}_jank AS
+  SELECT
+    id AS slice_id,
+    (next_jank IS NOT NULL AND next_jank) OR
+    (prev_jank IS NOT NULL AND prev_jank)
+    AS jank,
+    *
+  FROM {{prefix}}_jank_maybe_null_prev_and_next
+  ORDER BY {{id_field}} ASC, ts ASC;
+
+DROP VIEW IF EXISTS {{prefix}}_jank_output;
+CREATE VIEW {{prefix}}_jank_output AS
+  SELECT
+    {{proto_name}}(
+      '{{prefix}}_jank_percentage', (
+        SELECT
+          (
+            SUM(CASE WHEN jank THEN dur ELSE 0 END)/CAST(SUM(dur) AS REAL)
+          ) * 100.0
+        FROM {{prefix}}_jank
+      ),
+      '{{prefix}}_ms', (
+        SELECT
+          CAST(SUM(gesture_dur)/1e6 AS REAL)
+        FROM (
+          SELECT
+            MAX(gesture_dur) AS gesture_dur
+          FROM {{prefix}}_jank
+          GROUP BY {{id_field}}
+        )
+      ),
+      '{{prefix}}_processing_ms', CAST(SUM(dur)/1e6 AS REAL),
+      '{{prefix}}_jank_processing_ms', (
+        SELECT CAST(SUM(dur)/1e6 AS REAL) FROM {{prefix}}_jank WHERE jank
+      ),
+      'num_{{prefix}}_update_count', COUNT(*),
+      'num_{{prefix}}_update_jank_count', SUM(jank)
+    )
+  FROM {{prefix}}_jank;
diff --git a/src/trace_processor/metrics/chrome/scroll_jank.sql b/src/trace_processor/metrics/chrome/scroll_jank.sql
index 4561b1d..ddb8b20 100644
--- a/src/trace_processor/metrics/chrome/scroll_jank.sql
+++ b/src/trace_processor/metrics/chrome/scroll_jank.sql
@@ -24,295 +24,11 @@
 --          active development and the values & meaning might change without
 --          notice.
 
--- Get all chrome processes and threads tables set up.
-SELECT RUN_METRIC('chrome/chrome_processes.sql');
-
--- When working on GestureScrollUpdate events we need to ensure we have all the
--- events from the browser, renderer, and GPU processes. This query isn't quite
--- perfect. In system tracing we could have 3 browser processes all in the
--- background and this would match, but for now its the best we can do (renderer
--- and GPU names on android are quite complicated, but this should filter 99% (
--- citation needed) of what we want.
---
--- See b/151077536 for historical context.
-DROP VIEW IF EXISTS sufficient_chrome_processes;
-CREATE VIEW sufficient_chrome_processes AS
-  SELECT
-    CASE WHEN (
-      SELECT COUNT(*) FROM chrome_process) = 0
-    THEN
-      FALSE
-    ELSE (
-      SELECT COUNT(*) >= 3 FROM (
-        SELECT name FROM chrome_process
-        WHERE
-          name LIKE "Browser" OR
-          name LIKE "Renderer" OR
-          name LIKE "Gpu" OR
-          name LIKE 'com.android.chrome%' OR
-          name LIKE 'com.chrome.beta%' OR
-          name LIKE 'com.chrome.dev%' OR
-          name LIKE 'com.chrome.canary%' OR
-          name LIKE 'com.google.android.apps.chrome%' OR
-          name LIKE 'org.chromium.chrome%'
-        GROUP BY name
-    )) END AS have_enough_chrome_processes;
-
--- A simple table that checks the time between VSync (this can be used to
--- determine if we're scrolling at 90 FPS or 60 FPS.
---
--- Note: In traces without the "Java" category there will be no VSync
---       TraceEvents and this table will be empty.
---
--- Note: Must be a TABLE because it uses a window function which can behave
---       strangely in views.
-DROP TABLE IF EXISTS vsync_intervals;
-CREATE TABLE vsync_intervals AS
-  SELECT
-    slice_id,
-    ts,
-    dur,
-    track_id,
-    LEAD(ts) OVER(PARTITION BY track_id ORDER BY ts) - ts AS time_to_next_vsync
-  FROM slice
-  WHERE name = "VSync"
-  ORDER BY track_id, ts;
-
--- Get all the GestureScrollBegin and GestureScrollEnd events. We take their
--- IDs to group them together into scrolls later and the timestamp and duration
--- to compute the duration of the scroll.
-DROP VIEW IF EXISTS scroll_begin_and_end;
-CREATE VIEW scroll_begin_and_end AS
-  SELECT
-    slice.name,
-    slice.id,
-    slice.ts,
-    slice.dur,
-    slice.track_id,
-    EXTRACT_ARG(arg_set_id, 'chrome_latency_info.gesture_scroll_id')
-        AS gesture_scroll_id,
-    EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id
-  FROM
-    slice
-  WHERE
-    slice.name IN (
-      'InputLatency::GestureScrollBegin',
-      'InputLatency::GestureScrollEnd'
-    )
-  ORDER BY ts;
-
--- Now we take the GestureScrollBegin and the GestureScrollEnd events and join
--- the information into a single row per scroll. We also compute the average
--- Vysnc interval of the scroll (hopefully this would be either 60 FPS for the
--- whole scroll or 90 FPS but that isn't always the case). If the trace doesn't
--- contain the VSync TraceEvent we just fall back on assuming its 60 FPS (this
--- is the 1.6e+7 in the COALESCE which corresponds to 16 ms or 60 FPS).
-DROP VIEW IF EXISTS joined_scroll_begin_and_end;
-CREATE VIEW joined_scroll_begin_and_end AS
-  SELECT
-    begin.id AS begin_id,
-    begin.ts AS begin_ts,
-    begin.dur AS begin_dur,
-    begin.track_id AS begin_track_id,
-    begin.trace_id AS begin_trace_id,
-    COALESCE(begin.gesture_scroll_id, begin.trace_id)
-        AS begin_gesture_scroll_id,
-    end.ts AS end_ts,
-    end.ts + end.dur AS end_ts_and_dur,
-    end.trace_id AS end_trace_id,
-    COALESCE((
-      SELECT
-        CAST(AVG(time_to_next_vsync) AS FLOAT)
-      FROM vsync_intervals in_query
-      WHERE
-        time_to_next_vsync IS NOT NULL AND
-        in_query.ts > begin.ts AND
-        in_query.ts < end.ts
-    ), 1e+9 / 60) AS avg_vsync_interval
-  FROM scroll_begin_and_end begin JOIN scroll_begin_and_end end ON
-    begin.trace_id < end.trace_id AND
-    begin.name = 'InputLatency::GestureScrollBegin' AND
-    end.name = 'InputLatency::GestureScrollEnd' AND (
-      (
-        begin.gesture_scroll_id IS NULL AND
-        end.trace_id = (
-          SELECT MIN(trace_id)
-          FROM scroll_begin_and_end in_query
-          WHERE
-            name = 'InputLatency::GestureScrollEnd' AND
-          in_query.trace_id > begin.trace_id
-        )
-      ) OR
-      end.gesture_scroll_id = begin.gesture_scroll_id
-    )
-  ORDER BY begin.ts;
-
--- Get the GestureScrollUpdate events by name ordered by the
--- |gesture_scroll_id|, and timestamp. Then compute the number of frames (
--- relative to vsync interval) that each event took. 1.6e+7 is 16 ms in
--- nanoseconds and is used in case there are no VSync events to default to 60
--- fps. We join each GestureScrollUpdate event to the information about it'
--- begin and end events for easy computation later.
---
--- We remove updates with |dur| == -1 because this means we have no end event
--- and can't reasonably determine what it should be. We have separate tracking
--- to ensure this only happens at the end of the trace where its expected.
---
--- Note: Must be a TABLE because it uses a window function which can behave
---       strangely in views.
-DROP TABLE IF EXISTS gesture_scroll_update;
-CREATE TABLE gesture_scroll_update AS
-  SELECT
-    ROW_NUMBER() OVER (
-      ORDER BY gesture_scroll_id ASC, ts ASC) AS row_number,
-    begin_id,
-    begin_ts,
-    begin_dur,
-    begin_track_id,
-    begin_trace_id,
-    COALESCE(gesture_scroll_id, begin_trace_id) AS gesture_scroll_id,
-    CASE WHEN
-      end_ts_and_dur > ts + dur THEN
-        end_ts_and_dur
-      ELSE
-        ts + dur
-      END AS maybe_scroll_end,
-    id,
-    ts,
-    dur,
-    track_id,
-    trace_id,
-    dur/avg_vsync_interval AS scroll_frames_exact
-  FROM joined_scroll_begin_and_end begin_and_end JOIN (
-    SELECT
-      EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id,
-      EXTRACT_ARG(arg_set_id, 'chrome_latency_info.gesture_scroll_id')
-          AS gesture_scroll_id,
-      *
-    FROM
-      slice JOIN track ON slice.track_id = track.id
-    WHERE
-      slice.name = 'InputLatency::GestureScrollUpdate' AND
-      slice.dur != -1 AND
-      NOT COALESCE(
-              EXTRACT_ARG(arg_set_id, "chrome_latency_info.is_coalesced"),
-              TRUE)
-      AND slice.arg_set_id IN (
-        SELECT arg_set_id FROM args
-        WHERE args.arg_set_id = slice.arg_set_id
-        AND flat_key = 'chrome_latency_info.component_info.component_type'
-        AND string_value = 'COMPONENT_INPUT_EVENT_GPU_SWAP_BUFFER'
-      )
-  ) scroll_update ON
-  scroll_update.ts <= begin_and_end.end_ts AND
-  scroll_update.ts >= begin_and_end.begin_ts AND
-  scroll_update.trace_id > begin_and_end.begin_trace_id AND
-  scroll_update.trace_id < begin_and_end.end_trace_id AND (
-    scroll_update.gesture_scroll_id IS NULL OR
-    scroll_update.gesture_scroll_id = begin_and_end.begin_gesture_scroll_id
-  );
-
--- This takes the GestureScrollUpdate events and joins it to the previous
--- GestureScrollUpdate event (previous row and NULL if there isn't one) and the
--- next GestureScrollUpdate event (next row and again NULL if there isn't one).
--- Then we compute the duration of the event (relative to fps) and see if it
--- increased by more than 0.5 (which is 1/2 of 16 ms at 60 fps, and so on).
---
--- A small number is added to 0.5 in order to make sure that the comparison
--- does not filter out ratios that are precisely 0.5, which can fall a little
--- above or below exact value due to inherent inaccuracy of operations with
--- floating-point numbers. Value 1e-9 have been chosen as follows: the ratio
--- has nanoseconds in numerator and VSync interval in denominator. Assuming
--- refresh rate more than 1 FPS (and therefore VSync interval less than a
--- second), this ratio should increase with increments more than minimal value
--- in numerator (1ns) divided by maximum value in denominator, giving 1e-9.
---
--- We only compare a GestureScrollUpdate event to another event within the same
--- scroll (gesture_scroll_id == prev/next gesture_scroll_id). This controls
--- somewhat for variability of scrolls.
---
--- Note: Must be a TABLE because it uses a window function which can behave
---       strangely in views.
-DROP TABLE IF EXISTS scroll_jank_maybe_null_prev_and_next;
-CREATE TABLE scroll_jank_maybe_null_prev_and_next AS
-  SELECT
-    currprev.*,
-    CASE WHEN
-      currprev.gesture_scroll_id != prev_gesture_scroll_id OR
-      prev_ts IS NULL OR
-      prev_ts < currprev.begin_ts OR
-      prev_ts > currprev.maybe_scroll_end
-    THEN
-      FALSE
-    ELSE
-      currprev.scroll_frames_exact > prev_scroll_frames_exact + 0.5 + 1e-9
-    END AS prev_jank,
-    CASE WHEN
-      currprev.gesture_scroll_id != next.gesture_scroll_id OR
-      next.ts IS NULL OR
-      next.ts < currprev.begin_ts OR
-      next.ts > currprev.maybe_scroll_end
-    THEN
-      FALSE
-    ELSE
-      currprev.scroll_frames_exact > next.scroll_frames_exact + 0.5 + 1e-9
-    END AS next_jank,
-    next.scroll_frames_exact AS next_scroll_frames_exact
-  FROM (
-    SELECT
-      curr.*,
-      curr.maybe_scroll_end - curr.begin_ts AS scroll_dur,
-      prev.ts AS prev_ts,
-      prev.gesture_scroll_id AS prev_gesture_scroll_id,
-      prev.scroll_frames_exact AS prev_scroll_frames_exact
-    FROM
-      gesture_scroll_update curr LEFT JOIN
-      gesture_scroll_update prev ON prev.row_number + 1 = curr.row_number
-  ) currprev LEFT JOIN
-  gesture_scroll_update next ON currprev.row_number + 1 = next.row_number
-  ORDER BY currprev.gesture_scroll_id ASC, currprev.ts ASC;
-
--- This just uses prev_jank and next_jank to see if each GestureScrollUpdate
--- event is a jank.
-DROP VIEW IF EXISTS scroll_jank;
-CREATE VIEW scroll_jank AS
-  SELECT
-    id AS slice_id,
-    (next_jank IS NOT NULL AND next_jank) OR
-    (prev_jank IS NOT NULL AND prev_jank)
-    AS jank,
-    *
-  FROM scroll_jank_maybe_null_prev_and_next
-  ORDER BY gesture_scroll_id ASC, ts ASC;
-
-DROP VIEW IF EXISTS scroll_jank_ms;
-
-DROP VIEW IF EXISTS scroll_jank_output;
-CREATE VIEW scroll_jank_output AS
-  SELECT
-    ScrollJank(
-      'scroll_jank_percentage', (
-        SELECT
-          (
-            SUM(CASE WHEN jank THEN dur ELSE 0 END)/CAST(SUM(dur) AS REAL)
-          ) * 100.0
-        FROM scroll_jank
-      ),
-      'scroll_ms', (
-        SELECT
-          CAST(SUM(scroll_dur)/1e6 AS REAL)
-        FROM (
-          SELECT
-            MAX(scroll_dur) AS scroll_dur
-          FROM scroll_jank
-          GROUP BY gesture_scroll_id
-        )
-      ),
-      'scroll_processing_ms', CAST(SUM(dur)/1e6 AS REAL),
-      'scroll_jank_processing_ms', (
-        SELECT CAST(SUM(dur)/1e6 AS REAL) FROM scroll_jank WHERE jank
-      ),
-      'num_scroll_update_count', COUNT(*),
-      'num_scroll_update_jank_count', SUM(jank)
-    )
-  FROM scroll_jank;
+SELECT RUN_METRIC(
+    'chrome/gesture_jank.sql',
+    'prefix', 'scroll',
+    'gesture_start', 'GestureScrollBegin',
+    'gesture_update', 'GestureScrollUpdate',
+    'gesture_end', 'GestureScrollEnd',
+    'id_field', 'gesture_scroll_id',
+    'proto_name', 'ScrollJank');
diff --git a/src/trace_processor/metrics/chrome/touch_jank.sql b/src/trace_processor/metrics/chrome/touch_jank.sql
new file mode 100644
index 0000000..2920099
--- /dev/null
+++ b/src/trace_processor/metrics/chrome/touch_jank.sql
@@ -0,0 +1,33 @@
+--
+-- Copyright 2021 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- A collection of metrics related to TouchMove events.
+--
+-- We define a TouchMove to be janky if comparing forwards or backwards
+-- (ignoring coalesced updates) a given TouchMove exceeds the duration of its
+-- predecessor or successor by 50% of a vsync interval (defaulted to 60 FPS).
+--
+-- WARNING: This metric should not be used as a source of truth. It is under
+--          active development and the values & meaning might change without
+--          notice.
+
+SELECT RUN_METRIC(
+    'chrome/gesture_jank.sql',
+    'prefix', 'touch',
+    'gesture_start', 'TouchStart',
+    'gesture_update', 'TouchMove',
+    'gesture_end', 'TouchEnd',
+    'id_field', 'touch_id',
+    'proto_name', 'TouchJank');
diff --git a/test/synth_common.py b/test/synth_common.py
index b1ad592..ab2abc1 100644
--- a/test/synth_common.py
+++ b/test/synth_common.py
@@ -668,18 +668,23 @@
                                     track=None,
                                     trace_id=None,
                                     gesture_scroll_id=None,
+                                    touch_id=None,
                                     is_coalesced=None,
                                     gets_to_gpu=True):
     packet = self.add_track_event_slice(
         "InputLatency::" + name, ts=ts, dur=dur, track=track)
-    packet.track_event.chrome_latency_info.trace_id = trace_id
-    packet.track_event.chrome_latency_info.gesture_scroll_id = gesture_scroll_id
+    latency_info = packet.track_event.chrome_latency_info
+    latency_info.trace_id = trace_id
+    if gesture_scroll_id is not None:
+      latency_info.gesture_scroll_id = gesture_scroll_id
+    if touch_id is not None:
+      latency_info.touch_id = touch_id
     if gets_to_gpu:
-      component = packet.track_event.chrome_latency_info.component_info.add()
+      component = latency_info.component_info.add()
       # 13 is id of COMPONENT_INPUT_EVENT_GPU_SWAP_BUFFER
       component.component_type = 13
     if is_coalesced is not None:
-      packet.track_event.chrome_latency_info.is_coalesced = is_coalesced
+      latency_info.is_coalesced = is_coalesced
     return packet
 
   def add_chrome_metadata(self, os_name=None):
diff --git a/test/trace_processor/chrome/index b/test/trace_processor/chrome/index
index 4bbaecb..029a47d 100644
--- a/test/trace_processor/chrome/index
+++ b/test/trace_processor/chrome/index
@@ -1,6 +1,8 @@
 # Tests related to Chrome's use of Perfetto.
 
 # Chrome metrics (found in the trace_processor/chrome directory).
+
+# Scroll jank metrics
 ../../data/chrome_scroll_without_vsync.pftrace scroll_jank_general_validation.sql scroll_jank_general_validation.out
 ../../data/chrome_scroll_without_vsync.pftrace scroll_jank.sql scroll_jank.out
 ../../data/chrome_scroll_without_vsync.pftrace scroll_flow_event.sql scroll_flow_event.out
@@ -16,6 +18,10 @@
 scroll_jank_mojo_simple_watcher.py scroll_jank_mojo_simple_watcher.sql scroll_jank_mojo_simple_watcher.out
 scroll_jank_gpu_check.py scroll_jank_gpu_check.sql scroll_jank_gpu_check.out
 
+# Touch gesture metrics
+../../data/chrome_touch_gesture_scroll.pftrace touch_jank.sql touch_jank.out
+touch_jank.py touch_jank.sql touch_jank_synth.out
+
 # Chrome memory snapshots.
 ../../data/chrome_memory_snapshot.pftrace memory_snapshot_general_validation.sql memory_snapshot_general_validation.out
 ../../data/chrome_memory_snapshot.pftrace memory_snapshot_os_dump_events.sql memory_snapshot_os_dump_events.out
diff --git a/test/trace_processor/chrome/scroll_jank_general_validation.sql b/test/trace_processor/chrome/scroll_jank_general_validation.sql
index 4143875..1aa3059b 100644
--- a/test/trace_processor/chrome/scroll_jank_general_validation.sql
+++ b/test/trace_processor/chrome/scroll_jank_general_validation.sql
@@ -34,7 +34,7 @@
   -- This means we should have scroll_dur == 1628470852
   SELECT SUM(scroll_dur) FROM (
     SELECT
-      gesture_scroll_id, max(maybe_scroll_end) - begin_ts AS scroll_dur
+      gesture_scroll_id, max(maybe_gesture_end) - begin_ts AS scroll_dur
     FROM scroll_jank
     GROUP BY gesture_scroll_id
   )
diff --git a/test/trace_processor/chrome/touch_jank.out b/test/trace_processor/chrome/touch_jank.out
new file mode 100644
index 0000000..9f55ea8
--- /dev/null
+++ b/test/trace_processor/chrome/touch_jank.out
@@ -0,0 +1,16 @@
+
+"touch_id","trace_id","jank","ts","dur"
+6280,6288,1,1383122421558418,226350000
+6280,6312,0,1383122543558418,185244000
+6318,6328,1,1383123604558418,207945000
+6318,6414,0,1383124088558418,173233000
+6420,6552,0,1383125521558418,161901000
+6586,6602,0,1383126581558418,87964000
+6586,6693,1,1383127076558418,140115000
+6697,6698,1,1383127670558418,301236000
+6697,6723,0,1383127837558418,134236000
+6788,6802,0,1383129053558418,163272000
+6788,6877,1,1383129492558418,370407000
+6906,6911,1,1383130519558418,198057000
+6906,6915,0,1383130553558418,164057000
+6906,6940,0,1383130681558418,167758000
diff --git a/test/trace_processor/chrome/touch_jank.py b/test/trace_processor/chrome/touch_jank.py
new file mode 100644
index 0000000..86678f6
--- /dev/null
+++ b/test/trace_processor/chrome/touch_jank.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python3
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is intended to test the handling of simple_watcher style mojo events,
+# which are often missing trace events below them and so are all aggregated
+# together despite them coming into different mojo interfaces.
+
+from os import sys
+
+import synth_common
+
+from synth_common import ms_to_ns
+trace = synth_common.create_trace()
+
+process_track1 = 1234
+
+trace.add_process_track_descriptor(process_track1, pid=0)
+
+process_pid1 = 2345
+
+thread_track1 = 1235
+
+# Main threads have the same ID as the process
+thread_tid1 = process_pid1
+
+seq1 = 9876
+
+thread1_counter = 60
+
+touch_move_trace_id = 34576
+trace_id1 = touch_move_trace_id + 1
+trace_id2 = trace_id1 + 1
+trace_id3 = trace_id2 + 1
+touch_end_trace_id = trace_id3 + 1
+
+touch_gesture_id = 87654
+
+flow_id1 = 45678
+flow_id2 = 45679
+flow_id3 = 45680
+
+trace.add_input_latency_event_slice(
+    "TouchStart",
+    ts=ms_to_ns(0),
+    dur=ms_to_ns(1),
+    track=touch_move_trace_id,
+    trace_id=touch_move_trace_id,
+    touch_id=touch_gesture_id)
+
+trace.add_chrome_process_track_descriptor(process_track1, process_pid1)
+
+trace.add_chrome_thread_with_cpu_counter(
+    process_track1,
+    thread_track1,
+    trusted_packet_sequence_id=seq1,
+    counter_track=thread1_counter,
+    pid=process_pid1,
+    tid=thread_tid1,
+    thread_type=trace.prototypes.ThreadDescriptor.ChromeThreadType
+    .CHROME_THREAD_MAIN)
+
+# Touch move 1 - not janky
+trace.add_input_latency_event_slice(
+    "TouchMove",
+    ts=ms_to_ns(0),
+    dur=ms_to_ns(10),
+    track=trace_id1,
+    trace_id=trace_id1,
+    touch_id=touch_gesture_id,
+    is_coalesced=0)
+
+trace.add_latency_info_flow(
+    ts=ms_to_ns(0),
+    dur=ms_to_ns(1),
+    trusted_sequence_id=seq1,
+    trace_id=trace_id1,
+    flow_ids=[flow_id1])
+
+# The slices below will block this "not janky" touch move 1.
+trace.add_track_event_slice(
+    "task", ts=ms_to_ns(2), dur=ms_to_ns(6), trusted_sequence_id=seq1)
+
+trace.add_track_event_slice(
+    "subtask", ts=ms_to_ns(3), dur=ms_to_ns(4), trusted_sequence_id=seq1)
+# This ends the blocking slices of "not janky" touch move 1.
+
+trace.add_latency_info_flow(
+    ts=ms_to_ns(11),
+    dur=ms_to_ns(1),
+    trusted_sequence_id=seq1,
+    trace_id=trace_id1,
+    terminating_flow_ids=[flow_id1])
+
+# Touch move 2 - janky
+trace.add_input_latency_event_slice(
+    "TouchMove",
+    ts=ms_to_ns(16),
+    dur=ms_to_ns(33),
+    track=trace_id2,
+    trace_id=trace_id2,
+    touch_id=touch_gesture_id,
+    is_coalesced=0)
+
+trace.add_latency_info_flow(
+    ts=ms_to_ns(16),
+    dur=ms_to_ns(1),
+    trusted_sequence_id=seq1,
+    trace_id=trace_id2,
+    flow_ids=[flow_id2])
+
+# The slices below will block this "janky" touch move 2.
+trace.add_track_event_slice(
+    "task", ts=ms_to_ns(18), dur=ms_to_ns(29), trusted_sequence_id=seq1)
+
+trace.add_track_event_slice(
+    "subtask", ts=ms_to_ns(19), dur=ms_to_ns(27), trusted_sequence_id=seq1)
+# This ends the blocking slices of "janky" touch move 2.
+
+trace.add_latency_info_flow(
+    ts=ms_to_ns(50),
+    dur=ms_to_ns(1),
+    trusted_sequence_id=seq1,
+    trace_id=trace_id2,
+    terminating_flow_ids=[flow_id2])
+
+# Touch move 3 - janky
+trace.add_input_latency_event_slice(
+    "TouchMove",
+    ts=ms_to_ns(55),
+    dur=ms_to_ns(33),
+    track=trace_id3,
+    trace_id=trace_id3,
+    touch_id=touch_gesture_id,
+    is_coalesced=0)
+
+trace.add_latency_info_flow(
+    ts=ms_to_ns(55),
+    dur=ms_to_ns(1),
+    trusted_sequence_id=seq1,
+    trace_id=trace_id3,
+    flow_ids=[flow_id3])
+
+# The slices below will block this "janky" touch move 3.
+trace.add_track_event_slice(
+    "task", ts=ms_to_ns(57), dur=ms_to_ns(29), trusted_sequence_id=seq1)
+
+packet = trace.add_track_event_slice(
+    "subtask", ts=ms_to_ns(58), dur=ms_to_ns(27), trusted_sequence_id=seq1)
+# This ends the blocking slices of "janky" touch move 3.
+
+trace.add_latency_info_flow(
+    ts=ms_to_ns(89),
+    dur=ms_to_ns(1),
+    trusted_sequence_id=seq1,
+    trace_id=trace_id3,
+    terminating_flow_ids=[flow_id3])
+
+trace.add_input_latency_event_slice(
+    "TouchEnd",
+    ts=ms_to_ns(90),
+    dur=ms_to_ns(2),
+    track=touch_end_trace_id,
+    trace_id=touch_end_trace_id,
+    touch_id=touch_gesture_id)
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/chrome/touch_jank.sql b/test/trace_processor/chrome/touch_jank.sql
new file mode 100644
index 0000000..723b146
--- /dev/null
+++ b/test/trace_processor/chrome/touch_jank.sql
@@ -0,0 +1,24 @@
+--
+-- Copyright 2020 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+SELECT RUN_METRIC('chrome/touch_jank.sql') AS suppress_query_output;
+
+SELECT
+  touch_id,
+  trace_id,
+  jank,
+  ts,
+  dur
+FROM touch_jank;
diff --git a/test/trace_processor/chrome/touch_jank_synth.out b/test/trace_processor/chrome/touch_jank_synth.out
new file mode 100644
index 0000000..0c6f96f
--- /dev/null
+++ b/test/trace_processor/chrome/touch_jank_synth.out
@@ -0,0 +1,5 @@
+
+"touch_id","trace_id","jank","ts","dur"
+87654,34577,0,0,10000000
+87654,34578,1,16000000,33000000
+87654,34579,0,55000000,33000000
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 3c1be18..42a2abc 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -234,8 +234,8 @@
     # Example traces for regression tests.
     Dependency(
         'test/data.zip',
-         'https://storage.googleapis.com/perfetto/test-data-20210831-142240.zip',
-         'a40ceaed49bce81f3d4f3c0205356103b8c0ea153bf555870916c4ebede9abf8',
+         'https://storage.googleapis.com/perfetto/test-data-20210902-155802.zip',
+         '62f31bd3b49f5c09c1fec047a551d91141c9f394e555564050a8fa14132fa502',
         'all', 'all',
     ),