-- Copyright 2023 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 chrome.event_latency;

INCLUDE PERFETTO MODULE chrome.graphics_pipeline;

INCLUDE PERFETTO MODULE chrome.input;

INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_offsets;

-- Ties together input (`LatencyInfo.Flow`) and frame (`Graphics.Pipeline`)
-- trace events. Only covers input events of the `GESTURE_SCROLL_UPDATE_EVENT`
-- type.
CREATE PERFETTO TABLE chrome_scroll_update_refs (
  -- Id of the Chrome input pipeline (`LatencyInfo.Flow`).
  scroll_update_latency_id LONG,
  -- Id of the touch move input corresponding to this scroll update.
  touch_move_latency_id LONG,
  -- Id of the `EventLatency` of the frame that the input was presented in.
  presentation_latency_id LONG,
  -- Id of the frame pipeline (`Graphics.Pipeline`), pre-surface aggregation.
  surface_frame_id LONG,
  -- Id of the frame pipeline (`Graphics.Pipeline`), post-surface aggregation.
  display_trace_id LONG
) AS
SELECT
  scroll_update.latency_id AS scroll_update_latency_id,
  chrome_touch_move_to_scroll_update.touch_move_latency_id,
  coalesce(chrome_coalesced_inputs.presented_latency_id, scroll_update.latency_id) AS presentation_latency_id,
  chrome_graphics_pipeline_inputs_to_surface_frames.surface_frame_trace_id AS surface_frame_id,
  chrome_surface_frame_id_to_first_display_id.display_trace_id
FROM chrome_inputs AS scroll_update
LEFT JOIN chrome_graphics_pipeline_inputs_to_surface_frames
  USING (latency_id)
LEFT JOIN chrome_surface_frame_id_to_first_display_id
  ON chrome_surface_frame_id_to_first_display_id.surface_frame_trace_id = chrome_graphics_pipeline_inputs_to_surface_frames.surface_frame_trace_id
LEFT JOIN chrome_touch_move_to_scroll_update
  ON chrome_touch_move_to_scroll_update.scroll_update_latency_id = scroll_update.latency_id
LEFT JOIN chrome_coalesced_inputs
  ON chrome_coalesced_inputs.coalesced_latency_id = scroll_update.latency_id
WHERE
  scroll_update.input_type = 'GESTURE_SCROLL_UPDATE_EVENT';

-- Timestamps and other related information for events during the
-- input-associated (before inputs are coalesced into a frame) stages of a
-- scroll.
CREATE PERFETTO TABLE _scroll_update_input_timestamps_and_metadata AS
SELECT
  refs.scroll_update_latency_id AS id,
  refs.presentation_latency_id AS presented_in_frame_id,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  chrome_event_latency.scroll_id,
  chrome_event_latency.is_presented,
  chrome_event_latency.is_janky,
  chrome_event_latency.event_type = 'INERTIAL_GESTURE_SCROLL_UPDATE' AS is_inertial,
  chrome_event_latency.event_type = 'FIRST_GESTURE_SCROLL_UPDATE' AS is_first_scroll_update_in_scroll,
  chrome_event_latency.ts AS generation_ts,
  chrome_android_input.input_reader_processing_end_ts,
  chrome_android_input.input_dispatcher_processing_end_ts,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  touch_move_received_step.slice_id AS touch_move_received_slice_id,
  touch_move_received_step.ts AS touch_move_received_ts,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  touch_move_processed_step.slice_id AS touch_move_processed_slice_id,
  touch_move_processed_step.ts AS touch_move_processed_ts,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  scroll_update_created_step.slice_id AS scroll_update_created_slice_id,
  scroll_update_created_step.utid AS browser_utid,
  scroll_update_created_step.ts AS scroll_update_created_ts,
  scroll_update_created_step.ts + scroll_update_created_step.dur AS scroll_update_created_end_ts,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  compositor_dispatch_step.slice_id AS compositor_dispatch_slice_id,
  compositor_dispatch_step.task_start_time_ts AS compositor_dispatch_task_ts,
  compositor_dispatch_step.ts AS compositor_dispatch_ts,
  compositor_dispatch_step.ts + compositor_dispatch_step.dur AS compositor_dispatch_end_ts,
  compositor_dispatch_step.utid AS compositor_utid,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  compositor_coalesced_input_handled_step.slice_id AS compositor_coalesced_input_handled_slice_id,
  compositor_coalesced_input_handled_step.ts AS compositor_coalesced_input_handled_ts,
  compositor_coalesced_input_handled_step.ts + compositor_coalesced_input_handled_step.dur AS compositor_coalesced_input_handled_end_ts
FROM chrome_scroll_update_refs AS refs
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_gesture_scroll_updates AS chrome_event_latency
  ON chrome_event_latency.scroll_update_id = refs.scroll_update_latency_id
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_dispatch_android_input_event_to_touch_move
  ON refs.touch_move_latency_id = chrome_dispatch_android_input_event_to_touch_move.touch_move_latency_id
LEFT JOIN chrome_android_input
  ON chrome_android_input.android_input_id = chrome_dispatch_android_input_event_to_touch_move.android_input_id
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_input_pipeline_steps AS touch_move_received_step
  ON refs.touch_move_latency_id = touch_move_received_step.latency_id
  AND touch_move_received_step.step = 'STEP_SEND_INPUT_EVENT_UI'
  AND touch_move_received_step.input_type = 'TOUCH_MOVE_EVENT'
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_input_pipeline_steps AS touch_move_processed_step
  ON touch_move_processed_step.latency_id = refs.touch_move_latency_id
  AND touch_move_processed_step.step = 'STEP_TOUCH_EVENT_HANDLED'
  AND touch_move_processed_step.input_type = 'TOUCH_MOVE_EVENT'
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_input_pipeline_steps AS scroll_update_created_step
  ON scroll_update_created_step.latency_id = refs.scroll_update_latency_id
  AND scroll_update_created_step.step = 'STEP_SEND_INPUT_EVENT_UI'
  AND scroll_update_created_step.input_type = 'GESTURE_SCROLL_UPDATE_EVENT'
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_input_pipeline_steps AS compositor_dispatch_step
  ON compositor_dispatch_step.latency_id = refs.scroll_update_latency_id
  AND compositor_dispatch_step.step = 'STEP_HANDLE_INPUT_EVENT_IMPL'
  AND compositor_dispatch_step.input_type = 'GESTURE_SCROLL_UPDATE_EVENT'
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_input_pipeline_steps AS compositor_coalesced_input_handled_step
  ON compositor_coalesced_input_handled_step.latency_id = refs.scroll_update_latency_id
  AND compositor_coalesced_input_handled_step.step = 'STEP_DID_HANDLE_INPUT_AND_OVERSCROLL'
  AND compositor_coalesced_input_handled_step.input_type = 'GESTURE_SCROLL_UPDATE_EVENT';

-- Timestamps and durations for the input-associated (before coalescing inputs
-- into a frame) stages of a scroll.
CREATE PERFETTO TABLE chrome_scroll_update_input_pipeline (
  -- Id of the `LatencyInfo.Flow` slices corresponding to this scroll event.
  id LONG,
  -- Id of the scroll this scroll update belongs to.
  scroll_id LONG,
  -- Id of the frame that this input was presented in. Can be joined with
  -- `chrome_scroll_update_frame_pipeline.id`.
  presented_in_frame_id LONG,
  -- Whether this input event was presented.
  is_presented BOOL,
  -- Whether the corresponding frame is janky based on the
  -- Event.ScrollJank.DelayedFramesPercentage.FixedWindow metric. This comes
  -- directly from `perfetto.protos.EventLatency.is_janky_scrolled_frame`.
  is_janky BOOL,
  -- Whether the corresponding scroll is inertial (fling).
  -- If this is `true`, "generation" and "touch_move" related timestamps and
  -- durations will be null.
  is_inertial BOOL,
  -- Whether this is the first update in a scroll.
  -- First scroll update can never be janky.
  is_first_scroll_update_in_scroll BOOL,
  -- Whether this is the first input that was presented in frame
  -- `presented_in_frame_id`.
  is_first_scroll_update_in_frame BOOL,
  -- Input generation timestamp (from the Android system).
  generation_ts TIMESTAMP,
  -- End timestamp for the InputReader step (see android_input.sql).
  -- Only populated when atrace 'input' category is enabled.
  input_reader_processing_end_ts TIMESTAMP,
  -- End timestamp for the InputDispatcher step (see android_input.sql).
  -- Only populated when atrace 'input' category is enabled.
  input_dispatcher_processing_end_ts TIMESTAMP,
  -- Duration from input generation to when the browser received the input.
  generation_to_browser_main_dur DURATION,
  -- Utid for the browser main thread.
  browser_utid LONG,
  -- Slice id for the `STEP_SEND_INPUT_EVENT_UI` slice for the touch move.
  touch_move_received_slice_id LONG,
  -- Timestamp for the `STEP_SEND_INPUT_EVENT_UI` slice for the touch move.
  touch_move_received_ts TIMESTAMP,
  -- Duration for processing  a `TouchMove` event.
  touch_move_processing_dur DURATION,
  -- Slice id for the `STEP_SEND_INPUT_EVENT_UI` slice for the gesture scroll.
  scroll_update_created_slice_id LONG,
  -- Timestamp for the `STEP_SEND_INPUT_EVENT_UI` slice for the gesture scroll.
  scroll_update_created_ts TIMESTAMP,
  -- Duration for creating a `GestureScrollUpdate` from a `TouchMove` event.
  scroll_update_processing_dur DURATION,
  -- End timestamp for the `STEP_SEND_INPUT_EVENT_UI` slice for the above.
  scroll_update_created_end_ts TIMESTAMP,
  -- Duration between the browser and compositor dispatch.
  browser_to_compositor_delay_dur DURATION,
  -- Utid for the renderer compositor thread.
  compositor_utid LONG,
  -- Slice id for the `STEP_HANDLE_INPUT_EVENT_IMPL` slice.
  compositor_dispatch_slice_id LONG,
  -- Timestamp for the `STEP_HANDLE_INPUT_EVENT_IMPL` slice or the
  -- containing task (if available).
  compositor_dispatch_ts TIMESTAMP,
  -- Duration for the compositor dispatch itself.
  compositor_dispatch_dur DURATION,
  -- End timestamp for the `STEP_HANDLE_INPUT_EVENT_IMPL` slice.
  compositor_dispatch_end_ts TIMESTAMP,
  -- Duration between compositor dispatch and coalescing input.
  compositor_dispatch_to_coalesced_input_handled_dur DURATION,
  -- Slice id for the `STEP_DID_HANDLE_INPUT_AND_OVERSCROLL` slice.
  compositor_coalesced_input_handled_slice_id LONG,
  -- Timestamp for the `STEP_DID_HANDLE_INPUT_AND_OVERSCROLL` slice.
  compositor_coalesced_input_handled_ts TIMESTAMP,
  -- Duration for the `STEP_DID_HANDLE_INPUT_AND_OVERSCROLL` slice.
  compositor_coalesced_input_handled_dur DURATION,
  -- End timestamp for the `STEP_DID_HANDLE_INPUT_AND_OVERSCROLL` slice.
  compositor_coalesced_input_handled_end_ts TIMESTAMP
) AS
WITH
  processed_timestamps_and_metadata AS (
    SELECT
      id,
      scroll_id,
      presented_in_frame_id,
      is_presented,
      is_janky,
      is_inertial,
      is_first_scroll_update_in_scroll,
      row_number() OVER (PARTITION BY presented_in_frame_id ORDER BY generation_ts ASC) = 1 AS is_first_scroll_update_in_frame,
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
      -- Ids
      browser_utid,
      touch_move_received_slice_id,
      -- Timestamps
      generation_ts,
      input_reader_processing_end_ts,
      input_dispatcher_processing_end_ts,
      touch_move_received_ts,
      -- TODO(b:385160424): this is a workaround for cases when
      -- generation time is later than the input time.
      max(
        iif(
          is_inertial AND touch_move_received_ts IS NULL,
          scroll_update_created_ts,
          touch_move_received_ts
        ),
        generation_ts
      ) AS browser_main_received_ts,
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
      -- Ids
      scroll_update_created_slice_id,
      -- Timestamps
      scroll_update_created_ts,
      scroll_update_created_end_ts,
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
      -- Ids
      compositor_utid,
      compositor_dispatch_slice_id,
      -- Timestamps
      coalesce(compositor_dispatch_task_ts, compositor_dispatch_ts) AS compositor_dispatch_ts,
      compositor_dispatch_end_ts,
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
      -- Ids
      compositor_coalesced_input_handled_slice_id,
      -- Timestamps
      compositor_coalesced_input_handled_ts,
      compositor_coalesced_input_handled_end_ts
    FROM _scroll_update_input_timestamps_and_metadata
  )
SELECT
  id,
  scroll_id,
  presented_in_frame_id,
  is_presented,
  is_janky,
  is_inertial,
  is_first_scroll_update_in_scroll,
  is_first_scroll_update_in_frame,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- No applicable utid (duration between two threads).
  -- No applicable slice id (duration between two threads).
  generation_ts,
  input_reader_processing_end_ts,
  input_dispatcher_processing_end_ts,
  -- Flings don't have a touch move event so make GenerationToBrowserMain span
  -- all the way to the creation of the gesture scroll update.
  browser_main_received_ts - generation_ts AS generation_to_browser_main_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  browser_utid,
  touch_move_received_slice_id,
  touch_move_received_ts,
  scroll_update_created_ts - max(touch_move_received_ts, generation_ts) AS touch_move_processing_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `browser_utid`.
  scroll_update_created_slice_id,
  scroll_update_created_ts,
  scroll_update_created_end_ts - scroll_update_created_ts AS scroll_update_processing_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- No applicable utid (duration between two threads).
  -- No applicable slice id (duration between two threads).
  scroll_update_created_end_ts,
  -- TODO(b:385161677): use the start
  -- of the STEP_SEND_DISPATCH_EVENT_MOJO_MESSAGE step
  -- instead of scroll_update_created_end_ts.
  max(compositor_dispatch_ts, scroll_update_created_end_ts) - scroll_update_created_end_ts AS browser_to_compositor_delay_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  compositor_utid,
  compositor_dispatch_slice_id,
  compositor_dispatch_ts,
  compositor_dispatch_end_ts - compositor_dispatch_ts AS compositor_dispatch_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `compositor_utid`.
  -- No applicable slice id (duration between two slices).
  compositor_dispatch_end_ts,
  -- TODO(b:380868337): This is sometimes negative; check/fix this.
  compositor_coalesced_input_handled_ts - compositor_dispatch_end_ts AS compositor_dispatch_to_coalesced_input_handled_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `compositor_utid`.
  compositor_coalesced_input_handled_slice_id,
  compositor_coalesced_input_handled_ts,
  compositor_coalesced_input_handled_end_ts - compositor_coalesced_input_handled_ts AS compositor_coalesced_input_handled_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  compositor_coalesced_input_handled_end_ts
FROM processed_timestamps_and_metadata;

-- Timestamps and other related information for events during the
-- frame-associated (after inputs are coalesced into a frame) stages of a
-- scroll.
CREATE PERFETTO TABLE _scroll_update_frame_timestamps_and_metadata AS
SELECT
  refs.scroll_update_latency_id AS id,
  refs.display_trace_id,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  chrome_event_latency.vsync_interval_ms AS vsync_interval_ms,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  compositor_resample_step.slice_id AS compositor_resample_slice_id,
  compositor_resample_step.task_start_time_ts AS compositor_resample_task_ts,
  compositor_resample_step.ts AS compositor_resample_ts,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  compositor_receive_begin_frame_step.id AS compositor_receive_begin_frame_slice_id,
  compositor_receive_begin_frame_step.task_start_time_ts AS compositor_receive_begin_frame_task_ts,
  compositor_receive_begin_frame_step.ts AS compositor_receive_begin_frame_ts,
  --
  compositor_generate_compositor_frame_step.id AS compositor_generate_compositor_frame_slice_id,
  compositor_generate_compositor_frame_step.task_start_time_ts AS compositor_generate_compositor_frame_task_ts,
  compositor_generate_compositor_frame_step.ts AS compositor_generate_compositor_frame_ts,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  compositor_submit_compositor_frame_step.id AS compositor_submit_compositor_frame_slice_id,
  compositor_submit_compositor_frame_step.ts AS compositor_submit_compositor_frame_ts,
  compositor_submit_compositor_frame_step.ts + compositor_submit_compositor_frame_step.dur AS compositor_submit_compositor_frame_end_ts,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  viz_receive_compositor_frame_step.id AS viz_receive_compositor_frame_slice_id,
  viz_receive_compositor_frame_step.task_start_time_ts AS viz_receive_compositor_frame_task_ts,
  viz_receive_compositor_frame_step.ts AS viz_receive_compositor_frame_ts,
  viz_receive_compositor_frame_step.ts + viz_receive_compositor_frame_step.dur AS viz_receive_compositor_frame_end_ts,
  viz_receive_compositor_frame_step.utid AS viz_compositor_utid,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  viz_draw_and_swap_step.id AS viz_draw_and_swap_slice_id,
  viz_draw_and_swap_step.task_start_time_ts AS viz_draw_and_swap_task_ts,
  viz_draw_and_swap_step.ts AS viz_draw_and_swap_ts,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  viz_send_buffer_swap_step.id AS viz_send_buffer_swap_slice_id,
  viz_send_buffer_swap_step.ts + viz_send_buffer_swap_step.dur AS viz_send_buffer_swap_end_ts,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  viz_swap_buffers_step.id AS viz_swap_buffers_slice_id,
  viz_swap_buffers_step.task_start_time_ts AS viz_swap_buffers_task_ts,
  viz_swap_buffers_step.ts AS viz_swap_buffers_ts,
  viz_swap_buffers_step.ts + viz_swap_buffers_step.dur AS viz_swap_buffers_end_ts,
  viz_swap_buffers_step.utid AS viz_gpu_thread_utid,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  chrome_event_latency.buffer_available_timestamp,
  chrome_event_latency.buffer_ready_timestamp,
  chrome_event_latency.latch_timestamp,
  chrome_event_latency.presentation_timestamp
FROM chrome_scroll_update_refs AS refs
LEFT JOIN chrome_event_latencies AS chrome_event_latency
  ON chrome_event_latency.scroll_update_id = refs.presentation_latency_id
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_input_pipeline_steps AS compositor_resample_step
  ON compositor_resample_step.latency_id = refs.presentation_latency_id
  AND compositor_resample_step.step = 'STEP_RESAMPLE_SCROLL_EVENTS'
  AND compositor_resample_step.input_type = 'GESTURE_SCROLL_UPDATE_EVENT'
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_graphics_pipeline_surface_frame_steps AS compositor_receive_begin_frame_step
  ON compositor_receive_begin_frame_step.surface_frame_trace_id = refs.surface_frame_id
  AND compositor_receive_begin_frame_step.step = 'STEP_RECEIVE_BEGIN_FRAME'
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_graphics_pipeline_surface_frame_steps AS compositor_generate_compositor_frame_step
  ON compositor_generate_compositor_frame_step.surface_frame_trace_id = refs.surface_frame_id
  AND compositor_generate_compositor_frame_step.step = 'STEP_GENERATE_COMPOSITOR_FRAME'
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_graphics_pipeline_surface_frame_steps AS compositor_submit_compositor_frame_step
  ON compositor_submit_compositor_frame_step.surface_frame_trace_id = refs.surface_frame_id
  AND compositor_submit_compositor_frame_step.step = 'STEP_SUBMIT_COMPOSITOR_FRAME'
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_graphics_pipeline_surface_frame_steps AS viz_receive_compositor_frame_step
  ON viz_receive_compositor_frame_step.surface_frame_trace_id = refs.surface_frame_id
  AND viz_receive_compositor_frame_step.step = 'STEP_RECEIVE_COMPOSITOR_FRAME'
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_graphics_pipeline_display_frame_steps AS viz_draw_and_swap_step
  ON viz_draw_and_swap_step.display_trace_id = refs.display_trace_id
  AND viz_draw_and_swap_step.step = 'STEP_DRAW_AND_SWAP'
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_graphics_pipeline_display_frame_steps AS viz_send_buffer_swap_step
  ON viz_send_buffer_swap_step.display_trace_id = refs.display_trace_id
  AND viz_send_buffer_swap_step.step = 'STEP_SEND_BUFFER_SWAP'
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
LEFT JOIN chrome_graphics_pipeline_display_frame_steps AS viz_swap_buffers_step
  ON viz_swap_buffers_step.display_trace_id = refs.display_trace_id
  AND viz_swap_buffers_step.step = 'STEP_BUFFER_SWAP_POST_SUBMIT'
-- Filter out inputs which were coalesced into a different frame (so that rows
-- of this table correspond to frames).
WHERE
  refs.scroll_update_latency_id = refs.presentation_latency_id;

-- Timestamps and durations for the frame-associated (after coalescing inputs
-- into a frame) stages of a scroll.
CREATE PERFETTO TABLE chrome_scroll_update_frame_pipeline (
  -- Id of the `LatencyInfo.Flow` slices corresponding to this scroll event.
  id LONG,
  -- Id of the aggregated frame this scroll update was presented in.
  display_trace_id LONG,
  -- Vsync interval (in milliseconds).
  vsync_interval_ms DOUBLE,
  -- Slice id for the `STEP_RESAMPLE_SCROLL_EVENTS` slice.
  compositor_resample_slice_id LONG,
  -- Timestamp for the `STEP_RESAMPLE_SCROLL_EVENTS` slice.
  compositor_resample_ts TIMESTAMP,
  -- Timestamp for the `STEP_RECEIVE_BEGIN_FRAME` slice or the
  -- containing task (if available).
  compositor_receive_begin_frame_ts TIMESTAMP,
  -- Slice id for the `STEP_GENERATE_COMPOSITOR_FRAME` slice.
  compositor_generate_compositor_frame_slice_id LONG,
  -- Timestamp for the `STEP_GENERATE_COMPOSITOR_FRAME` slice or the
  -- containing task (if available).
  compositor_generate_compositor_frame_ts TIMESTAMP,
  -- Duration between generating and submitting the compositor frame.
  compositor_generate_frame_to_submit_frame_dur DURATION,
  -- Slice id for the `STEP_SUBMIT_COMPOSITOR_FRAME` slice.
  compositor_submit_compositor_frame_slice_id LONG,
  -- Timestamp for the `STEP_SUBMIT_COMPOSITOR_FRAME` slice.
  compositor_submit_compositor_frame_ts TIMESTAMP,
  -- Duration for submitting the compositor frame (to viz).
  compositor_submit_frame_dur DURATION,
  -- End timestamp for the `STEP_SUBMIT_COMPOSITOR_FRAME` slice.
  compositor_submit_compositor_frame_end_ts TIMESTAMP,
  -- Delay when a compositor frame is sent from the renderer to viz.
  compositor_to_viz_delay_dur DURATION,
  -- Utid for the viz compositor thread.
  viz_compositor_utid LONG,
  -- Slice id for the `STEP_RECEIVE_COMPOSITOR_FRAME` slice.
  viz_receive_compositor_frame_slice_id LONG,
  -- Timestamp for the `STEP_RECEIVE_COMPOSITOR_FRAME` slice or the
  -- containing task (if available).
  viz_receive_compositor_frame_ts TIMESTAMP,
  -- Duration of the viz work done on receiving the compositor frame.
  viz_receive_compositor_frame_dur DURATION,
  -- End timestamp for the `STEP_RECEIVE_COMPOSITOR_FRAME` slice.
  viz_receive_compositor_frame_end_ts TIMESTAMP,
  -- Duration between viz receiving the compositor frame to frame draw.
  viz_wait_for_draw_dur DURATION,
  -- Slice id for the `STEP_DRAW_AND_SWAP` slice.
  viz_draw_and_swap_slice_id LONG,
  -- Timestamp for the `STEP_DRAW_AND_SWAP` slice or the
  -- containing task (if available).
  viz_draw_and_swap_ts TIMESTAMP,
  -- Duration for the viz drawing/swapping work for this frame.
  viz_draw_and_swap_dur DURATION,
  -- Slice id for the `STEP_SEND_BUFFER_SWAP` slice.
  viz_send_buffer_swap_slice_id LONG,
  -- End timestamp for the `STEP_SEND_BUFFER_SWAP` slice.
  viz_send_buffer_swap_end_ts TIMESTAMP,
  -- Delay between viz work on compositor thread and `CompositorGpuThread`.
  viz_to_gpu_delay_dur DURATION,
  -- Utid for the viz `CompositorGpuThread`.
  viz_gpu_thread_utid LONG,
  -- Slice id for the `STEP_BUFFER_SWAP_POST_SUBMIT` slice.
  viz_swap_buffers_slice_id LONG,
  -- Timestamp for the `STEP_BUFFER_SWAP_POST_SUBMIT` slice or the
  -- containing task (if available).
  viz_swap_buffers_ts TIMESTAMP,
  -- Duration of frame buffer swapping work on viz.
  viz_swap_buffers_dur DURATION,
  -- End timestamp for the `STEP_BUFFER_SWAP_POST_SUBMIT` slice.
  viz_swap_buffers_end_ts TIMESTAMP,
  -- Duration of `EventLatency`'s `BufferReadyToLatch` step.
  viz_swap_buffers_to_latch_dur DURATION,
  -- Timestamp for `EventLatency`'s `LatchToSwapEnd` step.
  latch_timestamp TIMESTAMP,
  -- Duration of either `EventLatency`'s `LatchToSwapEnd` +
  -- `SwapEndToPresentationCompositorFrame` steps or its `LatchToPresentation`
  -- step.
  viz_latch_to_presentation_dur DURATION,
  -- Presentation timestamp for the frame.
  presentation_timestamp TIMESTAMP
) AS
WITH
  processed_timestamps_and_metadata AS (
    SELECT
      id,
      display_trace_id,
      vsync_interval_ms,
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
      -- Ids
      compositor_resample_slice_id,
      -- Timestamps
      coalesce(compositor_resample_task_ts, compositor_resample_ts) AS compositor_resample_ts,
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
      -- Ids
      compositor_receive_begin_frame_slice_id,
      -- Timestamps
      coalesce(compositor_receive_begin_frame_task_ts, compositor_receive_begin_frame_ts) AS compositor_receive_begin_frame_ts,
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
      -- Ids
      compositor_generate_compositor_frame_slice_id,
      -- Timestamps
      coalesce(
        compositor_generate_compositor_frame_task_ts,
        compositor_generate_compositor_frame_ts
      ) AS compositor_generate_compositor_frame_ts,
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
      -- Ids
      compositor_submit_compositor_frame_slice_id,
      -- Timestamps
      compositor_submit_compositor_frame_ts,
      compositor_submit_compositor_frame_end_ts,
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
      -- Ids
      viz_compositor_utid,
      viz_receive_compositor_frame_slice_id,
      -- Timestamps
      coalesce(viz_receive_compositor_frame_task_ts, viz_receive_compositor_frame_ts) AS viz_receive_compositor_frame_ts,
      viz_receive_compositor_frame_end_ts,
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
      -- Ids
      viz_draw_and_swap_slice_id,
      -- Timestamps
      coalesce(viz_draw_and_swap_task_ts, viz_draw_and_swap_ts) AS viz_draw_and_swap_ts,
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
      -- Ids
      viz_send_buffer_swap_slice_id,
      -- Timestamps
      viz_send_buffer_swap_end_ts,
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
      -- Ids
      viz_gpu_thread_utid,
      viz_swap_buffers_slice_id,
      -- Timestamps
      coalesce(viz_swap_buffers_task_ts, viz_swap_buffers_ts) AS viz_swap_buffers_ts,
      viz_swap_buffers_end_ts,
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
      -- Timestamps
      latch_timestamp,
      presentation_timestamp
    FROM _scroll_update_frame_timestamps_and_metadata
  )
SELECT
  id,
  display_trace_id,
  -- TODO(b:381062412): This is sometimes unexpectedly 0; check/fix this.
  vsync_interval_ms,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `compositor_utid`.
  compositor_resample_slice_id,
  compositor_resample_ts,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `compositor_utid`.
  compositor_receive_begin_frame_ts,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `compositor_utid`.
  compositor_generate_compositor_frame_slice_id,
  -- TODO(b:380868337): This is sometimes unexpectedly null; check/fix this.
  compositor_generate_compositor_frame_ts,
  compositor_submit_compositor_frame_ts - compositor_generate_compositor_frame_ts AS compositor_generate_frame_to_submit_frame_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `compositor_utid`.
  compositor_submit_compositor_frame_slice_id,
  compositor_submit_compositor_frame_ts,
  compositor_submit_compositor_frame_end_ts - compositor_submit_compositor_frame_ts AS compositor_submit_frame_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- No applicable utid (duration between two threads).
  -- No applicable slice id (duration between two threads).
  compositor_submit_compositor_frame_end_ts,
  -- TODO(b:380868337): This is sometimes negative; check/fix this.
  viz_receive_compositor_frame_ts - compositor_submit_compositor_frame_end_ts AS compositor_to_viz_delay_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  viz_compositor_utid,
  viz_receive_compositor_frame_slice_id,
  viz_receive_compositor_frame_ts,
  viz_receive_compositor_frame_end_ts - viz_receive_compositor_frame_ts AS viz_receive_compositor_frame_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `viz_compositor_utid`.
  -- No applicable slice id (duration between two slices).
  viz_receive_compositor_frame_end_ts,
  viz_draw_and_swap_ts - viz_receive_compositor_frame_end_ts AS viz_wait_for_draw_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `viz_compositor_utid`.
  viz_draw_and_swap_slice_id,
  viz_draw_and_swap_ts,
  viz_send_buffer_swap_end_ts - viz_draw_and_swap_ts AS viz_draw_and_swap_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- No applicable utid (duration between two threads).
  viz_send_buffer_swap_slice_id,
  viz_send_buffer_swap_end_ts,
  viz_swap_buffers_ts - viz_send_buffer_swap_end_ts AS viz_to_gpu_delay_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  viz_gpu_thread_utid,
  viz_swap_buffers_slice_id,
  viz_swap_buffers_ts,
  viz_swap_buffers_end_ts - viz_swap_buffers_ts AS viz_swap_buffers_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  viz_swap_buffers_end_ts,
  latch_timestamp - viz_swap_buffers_end_ts AS viz_swap_buffers_to_latch_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  latch_timestamp,
  presentation_timestamp - latch_timestamp AS viz_latch_to_presentation_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  presentation_timestamp
FROM processed_timestamps_and_metadata;

-- Defines slices for all of the individual scrolls in a trace based on the
-- LatencyInfo-based scroll definition.
--
-- NOTE: this view of top level scrolls is based on the LatencyInfo definition
-- of a scroll, which differs subtly from the definition based on
-- EventLatencies.
-- TODO(b/278684408): add support for tracking scrolls across multiple Chrome/
-- WebView instances. Currently gesture_scroll_id unique within an instance, but
-- is not unique across multiple instances. Switching to an EventLatency based
-- definition of scrolls should resolve this.
CREATE PERFETTO TABLE chrome_scrolls (
  -- The unique identifier of the scroll.
  id LONG,
  -- The start timestamp of the scroll.
  ts TIMESTAMP,
  -- The duration of the scroll.
  dur DURATION,
  -- The earliest timestamp of the EventLatency slice of the GESTURE_SCROLL_BEGIN type for the
  -- corresponding scroll id.
  gesture_scroll_begin_ts TIMESTAMP,
  -- The earliest timestamp of the EventLatency slice of the GESTURE_SCROLL_END type /
  -- the latest timestamp of the EventLatency slice of the GESTURE_SCROLL_UPDATE type for the
  -- corresponding scroll id.
  gesture_scroll_end_ts TIMESTAMP
) AS
SELECT
  scroll_id AS id,
  min(ts) AS ts,
  cast_int!(MAX(ts + dur) - MIN(ts)) AS dur,
  -- TODO(b:389055670): Remove this once the UI doesn't rely on it.
  NULL AS gesture_scroll_begin_ts,
  NULL AS gesture_scroll_end_ts
FROM chrome_gesture_scroll_updates
GROUP BY
  scroll_id;

-- Timestamps and durations for the critical path stages during scrolling.
-- This table covers both the input-associated (before coalescing inputs into a
-- frame) and frame-associated (after coalescing inputs into a frame) stages of
-- a scroll:
--
--                              ...
--                               |
--                +--------------+--------------+
--                |                             |
--                V                             V
--   +-------------------------+   +-------------------------+
--   | _scroll_update_INPUT_   |   | _scroll_update_FRAME_   |
--   | timestamps_and_metadata |   | timestamps_and_metadata |
--   +------------+------------+   +------------+------------+
--                |                             |
--                V                             V
--    +-----------------------+     +-----------------------+
--    | chrome_scroll_update_ |     | chrome_scroll_update_ |
--    |     INPUT_pipeline    |     |     FRAME_pipeline    |
--    +-----------+-----------+     +-----------+-----------+
--                |                             |
--                +--------------+--------------+
--                               |
--                               V
--                 +---------------------------+
--                 | chrome_scroll_update_info |
--                 +---------------------------+
CREATE PERFETTO TABLE chrome_scroll_update_info (
  -- Id of the `LatencyInfo.Flow` slices corresponding to this scroll event.
  id LONG,
  -- Id of the scroll this scroll update belongs to.
  scroll_id LONG,
  -- Id (`LatencyInfo.ID`) of the previous input in this scroll.
  previous_input_id LONG,
  -- Id (`display_trace_id`) of the aggregated frame which this scroll update
  -- was presented in.
  frame_display_id LONG,
  -- Vsync interval (in milliseconds).
  vsync_interval_ms DOUBLE,
  -- Whether this input event was presented.
  is_presented BOOL,
  -- Whether the corresponding frame is janky based on the
  -- Event.ScrollJank.DelayedFramesPercentage.FixedWindow metric. This comes
  -- directly from `perfetto.protos.EventLatency.is_janky_scrolled_frame`.
  is_janky BOOL,
  -- Whether the corresponding scroll is inertial (fling).
  -- If this is `true`, "generation" and "touch_move" related timestamps and
  -- durations will be null.
  is_inertial BOOL,
  -- Whether this is the first update in a scroll.
  -- First scroll update can never be janky.
  is_first_scroll_update_in_scroll BOOL,
  -- Whether this is the first input that was presented in the frame.
  is_first_scroll_update_in_frame BOOL,
  -- Duration from the start of the browser process to the first
  -- input generation timestamp.
  browser_uptime_dur DURATION,
  -- Input generation timestamp (from the Android system).
  generation_ts TIMESTAMP,
  -- Duration from the generation timestamp to the end of InputReader's work.
  -- Only populated when atrace 'input' category is enabled.
  input_reader_dur DURATION,
  -- Duration of InputDispatcher's work.
  -- Only populated when atrace 'input' category is enabled.
  input_dispatcher_dur DURATION,
  -- Duration from the generation timestamp for the previous input to
  -- this input's generation timestamp.
  since_previous_generation_dur DURATION,
  -- Duration from input generation to when the browser received the input.
  generation_to_browser_main_dur DURATION,
  -- Utid for the browser main thread.
  browser_utid LONG,
  -- Slice id for the `STEP_SEND_INPUT_EVENT_UI` slice for the touch move.
  touch_move_received_slice_id LONG,
  -- Timestamp for the `STEP_SEND_INPUT_EVENT_UI` slice for the touch move.
  touch_move_received_ts TIMESTAMP,
  -- Duration for processing  a `TouchMove` event.
  touch_move_processing_dur DURATION,
  -- Slice id for the `STEP_SEND_INPUT_EVENT_UI` slice for the gesture scroll.
  scroll_update_created_slice_id LONG,
  -- Timestamp for the `STEP_SEND_INPUT_EVENT_UI` slice for the gesture scroll.
  scroll_update_created_ts TIMESTAMP,
  -- Duration for creating a `GestureScrollUpdate` from a `TouchMove` event.
  scroll_update_processing_dur DURATION,
  -- End timestamp for the `STEP_SEND_INPUT_EVENT_UI` slice for the above.
  scroll_update_created_end_ts TIMESTAMP,
  -- Duration between the browser and compositor dispatch.
  browser_to_compositor_delay_dur DURATION,
  -- Utid for the renderer compositor thread.
  compositor_utid LONG,
  -- Slice id for the `STEP_HANDLE_INPUT_EVENT_IMPL` slice.
  compositor_dispatch_slice_id LONG,
  -- Timestamp for the `STEP_HANDLE_INPUT_EVENT_IMPL` slice or the
  -- containing task (if available).
  compositor_dispatch_ts TIMESTAMP,
  -- Duration for the compositor dispatch itself.
  compositor_dispatch_dur DURATION,
  -- End timestamp for the `STEP_HANDLE_INPUT_EVENT_IMPL` slice.
  compositor_dispatch_end_ts TIMESTAMP,
  -- Duration between compositor dispatch and input resampling work.
  compositor_dispatch_to_on_begin_frame_delay_dur DURATION,
  -- Slice id for the `STEP_RESAMPLE_SCROLL_EVENTS` slice.
  compositor_resample_slice_id LONG,
  -- Slice id for the `STEP_DID_HANDLE_INPUT_AND_OVERSCROLL` slice.
  compositor_coalesced_input_handled_slice_id LONG,
  -- Start timestamp for work done on the input during "OnBeginFrame".
  compositor_on_begin_frame_ts TIMESTAMP,
  -- Duration of the "OnBeginFrame" work for this input.
  compositor_on_begin_frame_dur DURATION,
  -- End timestamp for work done on the input during "OnBeginFrame".
  compositor_on_begin_frame_end_ts TIMESTAMP,
  -- Delay until the compositor work for generating the frame begins.
  compositor_on_begin_frame_to_generation_delay_dur DURATION,
  -- Slice id for the `STEP_GENERATE_COMPOSITOR_FRAME` slice.
  compositor_generate_compositor_frame_slice_id LONG,
  -- Timestamp for the `STEP_GENERATE_COMPOSITOR_FRAME` slice or the
  -- containing task (if available).
  compositor_generate_compositor_frame_ts TIMESTAMP,
  -- Duration between generating and submitting the compositor frame.
  compositor_generate_frame_to_submit_frame_dur DURATION,
  -- Slice id for the `STEP_SUBMIT_COMPOSITOR_FRAME` slice.
  compositor_submit_compositor_frame_slice_id LONG,
  -- Timestamp for the `STEP_SUBMIT_COMPOSITOR_FRAME` slice.
  compositor_submit_compositor_frame_ts TIMESTAMP,
  -- Duration for submitting the compositor frame (to viz).
  compositor_submit_frame_dur DURATION,
  -- End timestamp for the `STEP_SUBMIT_COMPOSITOR_FRAME` slice.
  compositor_submit_compositor_frame_end_ts TIMESTAMP,
  -- Delay when a compositor frame is sent from the renderer to viz.
  compositor_to_viz_delay_dur DURATION,
  -- Utid for the viz compositor thread.
  viz_compositor_utid LONG,
  -- Slice id for the `STEP_RECEIVE_COMPOSITOR_FRAME` slice.
  viz_receive_compositor_frame_slice_id LONG,
  -- Timestamp for the `STEP_RECEIVE_COMPOSITOR_FRAME` slice or the
  -- containing task (if available).
  viz_receive_compositor_frame_ts TIMESTAMP,
  -- Duration of the viz work done on receiving the compositor frame.
  viz_receive_compositor_frame_dur DURATION,
  -- End timestamp for the `STEP_RECEIVE_COMPOSITOR_FRAME` slice.
  viz_receive_compositor_frame_end_ts TIMESTAMP,
  -- Duration between viz receiving the compositor frame to frame draw.
  viz_wait_for_draw_dur DURATION,
  -- Slice id for the `STEP_DRAW_AND_SWAP` slice.
  viz_draw_and_swap_slice_id LONG,
  -- Timestamp for the `STEP_DRAW_AND_SWAP` slice or the
  -- containing task (if available).
  viz_draw_and_swap_ts TIMESTAMP,
  -- Duration for the viz drawing/swapping work for this frame.
  viz_draw_and_swap_dur DURATION,
  -- Slice id for the `STEP_SEND_BUFFER_SWAP` slice.
  viz_send_buffer_swap_slice_id LONG,
  -- End timestamp for the `STEP_SEND_BUFFER_SWAP` slice.
  viz_send_buffer_swap_end_ts TIMESTAMP,
  -- Delay between viz work on compositor thread and `CompositorGpuThread`.
  viz_to_gpu_delay_dur DURATION,
  -- Utid for the viz `CompositorGpuThread`.
  viz_gpu_thread_utid LONG,
  -- Slice id for the `STEP_BUFFER_SWAP_POST_SUBMIT` slice.
  viz_swap_buffers_slice_id LONG,
  -- Timestamp for the `STEP_BUFFER_SWAP_POST_SUBMIT` slice or the
  -- containing task (if available).
  viz_swap_buffers_ts TIMESTAMP,
  -- Duration of frame buffer swapping work on viz.
  viz_swap_buffers_dur DURATION,
  -- End timestamp for the `STEP_BUFFER_SWAP_POST_SUBMIT` slice.
  viz_swap_buffers_end_ts TIMESTAMP,
  -- Duration of `EventLatency`'s `BufferReadyToLatch` step.
  viz_swap_buffers_to_latch_dur DURATION,
  -- Timestamp for `EventLatency`'s `LatchToSwapEnd` step.
  latch_timestamp TIMESTAMP,
  -- Duration of either `EventLatency`'s `LatchToSwapEnd` +
  -- `SwapEndToPresentationCompositorFrame` steps or its `LatchToPresentation`
  -- step.
  viz_latch_to_presentation_dur DURATION,
  -- Presentation timestamp for the frame.
  presentation_timestamp TIMESTAMP
) AS
SELECT
  input.id,
  input.scroll_id,
  lag(input.id) OVER (PARTITION BY input.scroll_id ORDER BY input.generation_ts) AS previous_input_id,
  frame.display_trace_id AS frame_display_id,
  -- TODO(b:381062412): This is sometimes unexpectedly 0; check/fix this.
  frame.vsync_interval_ms,
  input.is_presented,
  input.is_janky,
  input.is_inertial,
  input.is_first_scroll_update_in_scroll,
  input.is_first_scroll_update_in_frame,
  generation_ts - browser_process.start_ts AS browser_uptime_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- No applicable utid (duration between two threads).
  -- No applicable slice id (duration between two threads).
  input.generation_ts,
  input.input_reader_processing_end_ts - generation_ts AS input_reader_dur,
  input.input_dispatcher_processing_end_ts - input.input_reader_processing_end_ts AS input_dispatcher_dur,
  input.generation_ts - lag(input.generation_ts) OVER (PARTITION BY input.scroll_id ORDER BY input.generation_ts) AS since_previous_generation_dur,
  input.generation_to_browser_main_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  input.browser_utid,
  input.touch_move_received_slice_id,
  input.touch_move_received_ts,
  input.touch_move_processing_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `browser_utid`.
  input.scroll_update_created_slice_id,
  input.scroll_update_created_ts,
  input.scroll_update_processing_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- No applicable utid (duration between two threads).
  -- No applicable slice id (duration between two threads).
  input.scroll_update_created_end_ts,
  -- TODO(b:380868337): This is sometimes negative; check/fix this.
  input.browser_to_compositor_delay_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  input.compositor_utid,
  input.compositor_dispatch_slice_id,
  input.compositor_dispatch_ts,
  input.compositor_dispatch_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `compositor_utid`.
  -- No applicable slice id (duration between two slices).
  input.compositor_dispatch_end_ts,
  -- TODO(b:380868337): This is sometimes negative; check/fix this.
  -- TODO(b:381273884): use frame.compositor_receive_begin_frame_ts instead of
  -- input.compositor_dispatch_end_ts.
  coalesce(frame.compositor_resample_ts, input.compositor_coalesced_input_handled_ts) - input.compositor_dispatch_end_ts AS compositor_dispatch_to_on_begin_frame_delay_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `compositor_utid`.
  -- `compositor_on_begin_frame_dur` can depend on two slices.
  frame.compositor_resample_slice_id,
  input.compositor_coalesced_input_handled_slice_id,
  coalesce(frame.compositor_resample_ts, input.compositor_coalesced_input_handled_ts) AS compositor_on_begin_frame_ts,
  input.compositor_coalesced_input_handled_end_ts - coalesce(frame.compositor_resample_ts, input.compositor_coalesced_input_handled_ts) AS compositor_on_begin_frame_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `compositor_utid`.
  -- No applicable slice id (duration between two slices).
  input.compositor_coalesced_input_handled_end_ts AS compositor_on_begin_frame_end_ts,
  frame.compositor_generate_compositor_frame_ts - input.compositor_coalesced_input_handled_end_ts AS compositor_on_begin_frame_to_generation_delay_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `compositor_utid`.
  frame.compositor_generate_compositor_frame_slice_id,
  -- TODO(b:380868337): This is sometimes unexpectedly null; check/fix this.
  frame.compositor_generate_compositor_frame_ts,
  frame.compositor_generate_frame_to_submit_frame_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `compositor_utid`.
  frame.compositor_submit_compositor_frame_slice_id,
  frame.compositor_submit_compositor_frame_ts,
  frame.compositor_submit_frame_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- No applicable utid (duration between two threads).
  -- No applicable slice id (duration between two threads).
  frame.compositor_submit_compositor_frame_end_ts,
  -- TODO(b:380868337): This is sometimes negative; check/fix this.
  frame.compositor_to_viz_delay_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  frame.viz_compositor_utid,
  frame.viz_receive_compositor_frame_slice_id,
  frame.viz_receive_compositor_frame_ts,
  frame.viz_receive_compositor_frame_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `viz_compositor_utid`.
  -- No applicable slice id (duration between two slices).
  frame.viz_receive_compositor_frame_end_ts,
  frame.viz_wait_for_draw_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- On `viz_compositor_utid`.
  frame.viz_draw_and_swap_slice_id,
  frame.viz_draw_and_swap_ts,
  frame.viz_draw_and_swap_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  -- No applicable utid (duration between two threads).
  frame.viz_send_buffer_swap_slice_id,
  frame.viz_send_buffer_swap_end_ts,
  frame.viz_to_gpu_delay_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  frame.viz_gpu_thread_utid,
  frame.viz_swap_buffers_slice_id,
  frame.viz_swap_buffers_ts,
  frame.viz_swap_buffers_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  frame.viz_swap_buffers_end_ts,
  frame.viz_swap_buffers_to_latch_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  frame.latch_timestamp,
  frame.viz_latch_to_presentation_dur,
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  frame.presentation_timestamp
FROM chrome_scroll_update_input_pipeline AS input
LEFT JOIN chrome_scroll_update_frame_pipeline AS frame
  ON input.presented_in_frame_id = frame.id
LEFT JOIN thread AS browser_main_thread
  ON browser_utid = browser_main_thread.utid
LEFT JOIN process AS browser_process
  ON browser_process.upid = browser_main_thread.upid;

-- Helper macro to compute the stage delta.
-- Should be used only as a part of `chrome_scroll_frame_info`.
CREATE PERFETTO MACRO _chrome_scroll_frame_stage_delta(
    name ColumnName
)
RETURNS Expr AS
iif(
  info.is_first_scroll_update_in_scroll,
  NULL,
  $name - lag($name) OVER (ORDER BY generation_ts)
);

-- A list of all presented Chrome frames which contain scroll updates and associated
-- metadata.
CREATE PERFETTO TABLE chrome_scroll_frame_info (
  -- Id (frame's display_trace_id) for the given frame.
  id LONG,
  -- Id of the scroll this scroll update belongs to.
  scroll_id LONG,
  -- Id (LatencyInfo.ID) of the last input before this frame.
  last_input_before_this_frame_id LONG,
  -- Vsync interval (in milliseconds).
  -- TODO(b/394303662): Remove in favour of `vsync_interval_dur`.
  vsync_interval_ms DOUBLE,
  -- Vsync interval (in nanoseconds).
  vsync_interval_dur DURATION,
  -- Whether the corresponding frame is janky based on the
  -- Event.ScrollJank.DelayedFramesPercentage.FixedWindow metric. This comes
  -- directly from `perfetto.protos.EventLatency.is_janky_scrolled_frame`.
  is_janky BOOL,
  -- Whether the corresponding scroll is inertial (fling).
  is_inertial BOOL,
  -- Sum of all input deltas for all scroll updates in this frame.
  -- These values are based on the delta of the OS input events.
  total_input_delta_y DOUBLE,
  -- Presented delta (change in page offset) for the given frame.
  -- This delta is computed by Chrome (based on the input events).
  presented_scrolled_delta_y DOUBLE,
  -- Duration from the start of the browser process to the first
  -- input generation timestamp.
  browser_uptime_dur DURATION,
  -- Input generation timestamp (from the Android system) for the first input.
  first_input_generation_ts TIMESTAMP,
  --  Duration from the generation timestamp to the end of InputReader's work.
  -- Only populated when atrace 'input' category is enabled.
  input_reader_dur DURATION,
  -- Duration of InputDispatcher's work.
  -- Only populated when atrace 'input' category is enabled.
  input_dispatcher_dur DURATION,
  -- Duration from the previous input (last input that wasn't part of this frame)
  -- to the first input in this frame.
  previous_last_input_to_first_input_generation_dur DURATION,
  -- Presentation timestamp for the frame.
  presentation_ts TIMESTAMP,
  -- Utid for the browser main thread.
  browser_utid JOINID(thread.id),
  -- Duration from input generation to when the browser received the first input
  -- in this frame.
  first_input_generation_to_browser_main_dur DURATION,
  -- Difference between `first_input_generation_to_browser_main_dur` for this
  -- frame and the previous frame in the same scroll.
  first_input_generation_to_browser_main_delta_dur DURATION,
  -- Duration for processing  a `TouchMove` event for the first input in this
  -- frame.
  first_input_touch_move_processing_dur DURATION,
  -- Difference between `first_input_touch_move_processing_dur` for this
  -- frame and the previous frame in the same scroll.
  first_input_touch_move_processing_delta_dur DURATION,
  -- Utid for the renderer compositor thread.
  compositor_utid JOINID(thread.id),
  -- Duration between the browser and compositor dispatch for the first input
  -- in this frame.
  first_input_browser_to_compositor_delay_dur DURATION,
  -- Difference between `first_input_browser_to_compositor_delay_dur` for this
  -- frame and the previous frame in the same scroll.
  first_input_browser_to_compositor_delay_delta_dur DURATION,
  -- Duration for the compositor dispatch for the first input in this frame.
  first_input_compositor_dispatch_dur DURATION,
  -- Difference between `first_input_compositor_dispatch_dur` for this frame and
  -- the previous frame in the same scroll.
  first_input_compositor_dispatch_delta_dur DURATION,
  -- Duration between the compositor dispatch and the "OnBeginFrame" work for the
  -- first input in this frame.
  first_input_compositor_dispatch_to_on_begin_frame_delay_dur DURATION,
  -- Difference between `first_input_compositor_dispatch_to_on_begin_frame_delay_dur`
  -- for this frame and the previous frame in the same scroll.
  first_input_compositor_dispatch_to_on_begin_frame_delay_delta_dur DURATION,
  -- Duration of the "OnBeginFrame" work for this frame.
  compositor_on_begin_frame_dur DURATION,
  -- Difference between `compositor_on_begin_frame_dur` for this frame and the
  -- previous frame in the same scroll.
  compositor_on_begin_frame_delta_dur DURATION,
  -- Duration between the "OnBeginFrame" work and the generation of this frame.
  compositor_on_begin_frame_to_generation_delay_dur DURATION,
  -- Difference between `compositor_on_begin_frame_to_generation_delay_dur` for
  -- this frame and the previous frame in the same scroll.
  compositor_on_begin_frame_to_generation_delay_delta_dur DURATION,
  -- Duration between the generation and submission of this frame.
  compositor_generate_frame_to_submit_frame_dur DURATION,
  -- Difference between `compositor_generate_frame_to_submit_frame_dur` for this
  -- frame and the previous frame in the same scroll.
  compositor_generate_frame_to_submit_frame_delta_dur DURATION,
  -- Duration for submitting this frame.
  compositor_submit_frame_dur DURATION,
  -- Difference between `compositor_submit_frame_dur` for this frame and the
  -- previous frame in the same scroll.
  compositor_submit_frame_delta_dur DURATION,
  -- Utid for the viz compositor thread.
  viz_compositor_utid JOINID(thread.id),
  -- Delay when a compositor frame is sent from the renderer to viz.
  compositor_to_viz_delay_dur DURATION,
  -- Difference between `compositor_to_viz_delay_dur` for this frame and the
  -- previous frame in the same scroll.
  compositor_to_viz_delay_delta_dur DURATION,
  -- Duration of the viz work done on receiving the compositor frame.
  viz_receive_compositor_frame_dur DURATION,
  -- Difference between `viz_receive_compositor_frame_dur` for this frame and the
  -- previous frame in the same scroll.
  viz_receive_compositor_frame_delta_dur DURATION,
  -- Duration between viz receiving the compositor frame to frame draw.
  viz_wait_for_draw_dur DURATION,
  -- Difference between `viz_wait_for_draw_dur` for this frame and the previous
  -- frame in the same scroll.
  viz_wait_for_draw_delta_dur DURATION,
  -- Duration of the viz drawing/swapping work for this frame.
  viz_draw_and_swap_dur DURATION,
  -- Difference between `viz_draw_and_swap_dur` for this frame and the previous
  -- frame in the same scroll.
  viz_draw_and_swap_delta_dur DURATION,
  -- Utid for the viz `CompositorGpuThread`.
  viz_gpu_thread_utid JOINID(thread.id),
  -- Delay between viz work on compositor thread and `CompositorGpuThread`.
  viz_to_gpu_delay_dur DURATION,
  -- Difference between `viz_to_gpu_delay_dur` for this frame and the previous
  -- frame in the same scroll.
  viz_to_gpu_delay_delta_dur DURATION,
  -- Duration of frame buffer swapping work on viz.
  viz_swap_buffers_dur DURATION,
  -- Difference between `viz_swap_buffers_dur` for this frame and the previous
  -- frame in the same scroll.
  viz_swap_buffers_delta_dur DURATION,
  -- Time between buffers ready until Choreographer's latch.
  viz_swap_buffers_to_latch_dur DURATION,
  -- Difference between `viz_swap_buffers_to_latch_dur` for this frame and the
  -- previous frame in the same scroll.
  viz_swap_buffers_to_latch_delta_dur DURATION,
  -- Duration between Choreographer's latch and presentation.
  viz_latch_to_presentation_dur DURATION,
  -- Difference between `viz_latch_to_presentation_dur` for this frame and the
  -- previous frame in the same scroll.
  viz_latch_to_presentation_delta_dur DURATION
) AS
SELECT
  frame_display_id AS id,
  info.scroll_id,
  previous_input_id AS last_input_before_this_frame_id,
  vsync_interval_ms,
  cast_int!(vsync_interval_ms * 1e6) AS vsync_interval_dur,
  is_janky,
  is_inertial,
  (
    SELECT
      sum(delta_y)
    FROM chrome_scroll_input_offsets AS input
    JOIN chrome_scroll_update_info AS update_info
      ON input.scroll_update_id = update_info.id
    WHERE
      update_info.frame_display_id = info.frame_display_id
  ) AS total_input_delta_y,
  delta.delta_y AS presented_scrolled_delta_y,
  browser_uptime_dur,
  info.generation_ts AS first_input_generation_ts,
  input_reader_dur,
  input_dispatcher_dur,
  info.since_previous_generation_dur AS previous_last_input_to_first_input_generation_dur,
  info.browser_utid,
  info.generation_to_browser_main_dur AS first_input_generation_to_browser_main_dur,
  presentation_timestamp AS presentation_ts,
  _chrome_scroll_frame_stage_delta!(generation_to_browser_main_dur) AS first_input_generation_to_browser_main_delta_dur,
  info.touch_move_processing_dur AS first_input_touch_move_processing_dur,
  _chrome_scroll_frame_stage_delta!(touch_move_processing_dur) AS first_input_touch_move_processing_delta_dur,
  info.compositor_utid,
  info.browser_to_compositor_delay_dur AS first_input_browser_to_compositor_delay_dur,
  _chrome_scroll_frame_stage_delta!(browser_to_compositor_delay_dur) AS first_input_browser_to_compositor_delay_delta_dur,
  info.compositor_dispatch_dur AS first_input_compositor_dispatch_dur,
  _chrome_scroll_frame_stage_delta!(compositor_dispatch_dur) AS first_input_compositor_dispatch_delta_dur,
  info.compositor_dispatch_to_on_begin_frame_delay_dur AS first_input_compositor_dispatch_to_on_begin_frame_delay_dur,
  _chrome_scroll_frame_stage_delta!(compositor_dispatch_to_on_begin_frame_delay_dur) AS first_input_compositor_dispatch_to_on_begin_frame_delay_delta_dur,
  info.compositor_on_begin_frame_dur,
  _chrome_scroll_frame_stage_delta!(compositor_on_begin_frame_dur) AS compositor_on_begin_frame_delta_dur,
  info.compositor_on_begin_frame_to_generation_delay_dur,
  _chrome_scroll_frame_stage_delta!(compositor_on_begin_frame_to_generation_delay_dur) AS compositor_on_begin_frame_to_generation_delay_delta_dur,
  info.compositor_generate_frame_to_submit_frame_dur,
  _chrome_scroll_frame_stage_delta!(compositor_generate_frame_to_submit_frame_dur) AS compositor_generate_frame_to_submit_frame_delta_dur,
  info.compositor_submit_frame_dur,
  _chrome_scroll_frame_stage_delta!(compositor_submit_frame_dur) AS compositor_submit_frame_delta_dur,
  viz_compositor_utid,
  info.compositor_to_viz_delay_dur,
  _chrome_scroll_frame_stage_delta!(compositor_to_viz_delay_dur) AS compositor_to_viz_delay_delta_dur,
  info.viz_receive_compositor_frame_dur,
  _chrome_scroll_frame_stage_delta!(viz_receive_compositor_frame_dur) AS viz_receive_compositor_frame_delta_dur,
  info.viz_wait_for_draw_dur,
  _chrome_scroll_frame_stage_delta!(viz_wait_for_draw_dur) AS viz_wait_for_draw_delta_dur,
  info.viz_draw_and_swap_dur,
  _chrome_scroll_frame_stage_delta!(viz_draw_and_swap_dur) AS viz_draw_and_swap_delta_dur,
  viz_gpu_thread_utid,
  info.viz_to_gpu_delay_dur,
  _chrome_scroll_frame_stage_delta!(viz_to_gpu_delay_dur) AS viz_to_gpu_delay_delta_dur,
  info.viz_swap_buffers_dur,
  _chrome_scroll_frame_stage_delta!(viz_swap_buffers_dur) AS viz_swap_buffers_delta_dur,
  info.viz_swap_buffers_to_latch_dur,
  _chrome_scroll_frame_stage_delta!(viz_swap_buffers_to_latch_dur) AS viz_swap_buffers_to_latch_delta_dur,
  info.viz_latch_to_presentation_dur,
  _chrome_scroll_frame_stage_delta!(viz_latch_to_presentation_dur) AS viz_latch_to_presentation_delta_dur
FROM chrome_scroll_update_info AS info
LEFT JOIN chrome_presented_scroll_offsets AS delta
  ON info.id = delta.scroll_update_id
-- TODO(b:380286381, b:393051057): remove the frame_display_id condition when dropped frames are handled.
WHERE
  is_first_scroll_update_in_frame AND info.frame_display_id IS NOT NULL;

-- Source of truth for the definition of the stages of a scroll. Mainly intended
-- for visualization purposes (e.g. in Chrome Scroll Jank plugin).
CREATE PERFETTO TABLE chrome_scroll_update_info_step_templates (
  -- The name of a stage of a scroll.
  step_name STRING,
  -- The name of the column in `chrome_scroll_update_info` which contains the
  -- timestamp of the stage.
  ts_column_name STRING,
  -- The name of the column in `chrome_scroll_update_info` which contains the
  -- duration of the stage. NULL if the stage doesn't have a duration.
  dur_column_name STRING
) AS
WITH
  steps(step_name, ts_column_name, dur_column_name) AS (
    SELECT
      *
    FROM (VALUES
      ('GenerationToBrowserMain', 'generation_ts', 'generation_to_browser_main_dur'),
      ('TouchMoveProcessing', 'touch_move_received_ts', 'touch_move_processing_dur'),
      (
        'ScrollUpdateProcessing',
        'scroll_update_created_ts',
        'scroll_update_processing_dur'
      ),
      (
        'BrowserMainToRendererCompositor',
        'scroll_update_created_end_ts',
        'browser_to_compositor_delay_dur'
      ),
      (
        'RendererCompositorDispatch',
        'compositor_dispatch_ts',
        'compositor_dispatch_dur'
      ),
      (
        'RendererCompositorDispatchToOnBeginFrame',
        'compositor_dispatch_end_ts',
        'compositor_dispatch_to_on_begin_frame_delay_dur'
      ),
      (
        'RendererCompositorBeginFrame',
        'compositor_on_begin_frame_ts',
        'compositor_on_begin_frame_dur'
      ),
      (
        'RendererCompositorBeginToGenerateFrame',
        'compositor_on_begin_frame_end_ts',
        'compositor_on_begin_frame_to_generation_delay_dur'
      ),
      (
        'RendererCompositorGenerateToSubmitFrame',
        'compositor_generate_compositor_frame_ts',
        'compositor_generate_frame_to_submit_frame_dur'
      ),
      (
        'RendererCompositorSubmitFrame',
        'compositor_submit_compositor_frame_ts',
        'compositor_submit_frame_dur'
      ),
      (
        'RendererCompositorToViz',
        'compositor_submit_compositor_frame_end_ts',
        'compositor_to_viz_delay_dur'
      ),
      (
        'VizReceiveFrame',
        'viz_receive_compositor_frame_ts',
        'viz_receive_compositor_frame_dur'
      ),
      (
        'VizReceiveToDrawFrame',
        'viz_receive_compositor_frame_end_ts',
        'viz_wait_for_draw_dur'
      ),
      ('VizDrawToSwapFrame', 'viz_draw_and_swap_ts', 'viz_draw_and_swap_dur'),
      ('VizToGpu', 'viz_send_buffer_swap_end_ts', 'viz_to_gpu_delay_dur'),
      ('VizSwapBuffers', 'viz_swap_buffers_ts', 'viz_swap_buffers_dur'),
      (
        'VizSwapBuffersToLatch',
        'viz_swap_buffers_end_ts',
        'viz_swap_buffers_to_latch_dur'
      ),
      ('VizLatchToPresentation', 'latch_timestamp', 'viz_latch_to_presentation_dur'),
      ('Presentation', 'presentation_timestamp', NULL)) AS _values
  )
SELECT
  step_name,
  ts_column_name,
  dur_column_name
FROM steps;
