--
-- Copyright 2022 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.
--
-- 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.

-- The final table includes the time between the arrival of gesture update
-- input timestamp, and the time it started being processed by CrBrowserMain.

SELECT RUN_METRIC('chrome/chrome_tasks.sql');

SELECT CREATE_FUNCTION(
  -- Given a slice id of an event, get timestamp of the most recent flow
  -- event on the Chrome IO thread that preceded this slice.
  -- This helps us identify the last flow event on the IO thread before
  -- letting the browser main thread be aware of input.
  -- We need this for flings (generated by the GPU process) and blocked
  -- touch moves that are forwarded from the renderer.
  'PRECEDING_IO_THREAD_EVENT_FLOW_ID(id LONG)',
  -- Returning the slice id for the flow_out on the chrome IO thread.
  'LONG',
  'SELECT MAX(slice.id) AS id
  FROM
    (SELECT * FROM PRECEDING_FLOW(($id))) flow
    JOIN slice
      ON flow.slice_out = slice.id
    JOIN thread_track
      ON thread_track.id = slice.track_id
    JOIN thread
      ON thread.utid = thread_track.utid
      AND thread.name = "Chrome_IOThread"
      AND slice.name = "SequenceManager PostTask"');

SELECT CREATE_FUNCTION(
  'GET_MOJO_PARENT_INTERFACE_TAG(id LONG)',
  'STRING',
  'SELECT
    interface_name
    FROM ancestor_slice(($id)) ancestors
    JOIN chrome_mojo_slices_tbl USING(id)'
);

SELECT CREATE_FUNCTION(
  'GET_SCROLL_TYPE(blocked_gesture BOOL, mojo_interface_tag STRING)',
  'STRING',
  'SELECT
    CASE WHEN ($blocked_gesture)
    THEN (SELECT
            CASE WHEN ($mojo_interface_tag) = "viz.mojom.BeginFrameObserver"
            THEN "fling"
            WHEN ($mojo_interface_tag) = "blink.mojom.WidgetInputHandler"
            THEN "blocking_touch_move"
            ELSE "unkown" END)
    ELSE "regular" END AS delay_type');

-- Get all InputLatency::GestureScrollUpdate events, to use their
-- flows later on to decide how much time we waited from queueing the event
-- until we started processing it.
DROP VIEW IF EXISTS chrome_valid_gesture_updates;
CREATE VIEW chrome_valid_gesture_updates
AS
SELECT
  name,
  EXTRACT_ARG(
    arg_set_id, 'chrome_latency_info.trace_id') AS trace_id,
  ts,
  id,
  dur
FROM
  slice
WHERE
  name = 'InputLatency::GestureScrollUpdate'
  AND EXTRACT_ARG(
    arg_set_id, "chrome_latency_info.is_coalesced")
    = 0
ORDER BY trace_id;

-- Get all chrome_latency_info_for_gesture_slices where trace_ids are not -1,
-- as those are faulty, then join with the GestureScrollUpdate table to get
-- only slices associated with update events.
DROP VIEW IF EXISTS chrome_flow_slices_for_gestures;
CREATE VIEW chrome_flow_slices_for_gestures
AS
SELECT
  slice.ts,
  EXTRACT_ARG(
    slice.arg_set_id, 'chrome_latency_info.trace_id') AS trace_id,
  slice.arg_set_id,
  slice.id,
  slice.track_id,
  slice.dur
FROM
  slice
JOIN chrome_valid_gesture_updates
  ON
    chrome_valid_gesture_updates.trace_id = EXTRACT_ARG(
    slice.arg_set_id, 'chrome_latency_info.trace_id')
    AND slice.ts >= chrome_valid_gesture_updates.ts
    AND slice.ts + slice.dur <=
      chrome_valid_gesture_updates.ts + chrome_valid_gesture_updates.dur
WHERE
  slice.name = 'LatencyInfo.Flow'
  AND trace_id != -1;

-- Tie chrome_latency_info_for_gesture_slices slices to processes to avoid
-- calculating intervals per process as multiple chrome instances can be up
-- on system traces.
DROP VIEW IF EXISTS chrome_flow_slices_for_gestures_tied_process;
CREATE VIEW chrome_flow_slices_for_gestures_tied_process
AS
SELECT
  ts,
  trace_id,
  arg_set_id,
  chrome_flow_slices_for_gestures.id,
  dur,
  thread.upid
FROM
  chrome_flow_slices_for_gestures
JOIN thread_track ON chrome_flow_slices_for_gestures.track_id = thread_track.id
JOIN thread ON thread_track.utid = thread.utid
AND is_main_thread;

-- Index all flows per trace_id, to get the first flow event per input
-- GestureScrollUpdate, this will later be used to calculate the time
-- from receiving input to the first flow event appearing.
DROP TABLE IF EXISTS chrome_indexed_flow_per_gesture;
CREATE TABLE chrome_indexed_flow_per_gesture
AS
SELECT
  ts,
  trace_id,
  arg_set_id,
  id,
  ROW_NUMBER()
    OVER (
      PARTITION BY trace_id, upid
      ORDER BY
        ts ASC
    ) AS flow_order,
    upid,
    dur
FROM
  chrome_flow_slices_for_gestures_tied_process;

-- TODO(b/235067134) all the previous views including this one are
-- reimplementations of gesture_jank.sql with less restrictions, let's
-- merge both of them into one script or make this script a base for the
-- other.
-- Get the first flow event per gesture.
DROP VIEW IF EXISTS chrome_first_flow_per_gesture;
CREATE VIEW chrome_first_flow_per_gesture
AS
SELECT
  *
FROM
  chrome_indexed_flow_per_gesture
WHERE
  flow_order = 1;



-- The decision for processing on the browser main thread for a frame can be
-- instant, or delayed by the renderer in cases where the renderer needs to
-- decide whether the touch move is an ScrollUpdate or not, and in other cases
-- for flings, the scroll itself will be generated by the viz compositor thread
-- on each vsync interval.
DROP VIEW IF EXISTS chrome_categorized_first_flow_events;
CREATE VIEW chrome_categorized_first_flow_events
AS
SELECT
  *,
  CASE
    WHEN
      (
        SELECT
          COUNT()
        FROM
          ancestor_slice(chrome_first_flow_per_gesture.id) ancestor_slices
          JOIN chrome_tasks ON ancestor_slices.id=chrome_tasks.id
        WHERE
          -- sendTouchEvent means the event wasn't delayed by the renderer
          -- and is not a fling generated by the viz compositor thread(GPU process).
          ancestor_slices.name = "sendTouchEvent"
      )
      = 1
      THEN FALSE
    ELSE TRUE
    END AS blocked_gesture
FROM
  chrome_first_flow_per_gesture;

-- For cases where it's not blocked, get the timestamp of input as the
-- beginning of time we theoretically could have started processing
-- the input event, which is the timestamp of the GestureScrollUpdate event
-- otherwise fall back to the top level slice to check the timestamp
-- of it's calling flow
DROP VIEW IF EXISTS chrome_input_to_browser_interval_slice_ids;
CREATE VIEW chrome_input_to_browser_interval_slice_ids
AS
SELECT
  chrome_categorized_first_flow_events.id AS window_end_id,
  chrome_categorized_first_flow_events.ts AS window_end_ts,
  -- If blocked, get the flow_out slice's timestamp as our beginning
  -- given that the flow_in is coming to our toplevel parent task
  CASE
    WHEN blocked_gesture
      THEN
        PRECEDING_IO_THREAD_EVENT_FLOW_ID(chrome_categorized_first_flow_events.id)
    ELSE
    -- TODO(b/236590359): is selecting here better or join and ordering by?
    -- Let's benchmark and modify accordingly.
      (
        SELECT
          chrome_gestures.id
        FROM
          chrome_valid_gesture_updates chrome_gestures
        WHERE
          chrome_gestures.trace_id = chrome_categorized_first_flow_events.trace_id
          AND chrome_gestures.ts <= chrome_categorized_first_flow_events.ts
          AND chrome_gestures.ts + chrome_gestures.dur >= chrome_categorized_first_flow_events.ts +
            chrome_categorized_first_flow_events.dur
      )
    END AS window_start_id,
  blocked_gesture,
  upid
FROM
  chrome_categorized_first_flow_events;

DROP TABLE IF EXISTS chrome_input_to_browser_intervals;
CREATE TABLE chrome_input_to_browser_intervals
AS
SELECT
  (SELECT ts FROM slice WHERE id = window_start_id) AS window_start_ts,
  window_start_id,
  window_end_ts,
  window_end_id,
  blocked_gesture,
  upid,
  GET_SCROLL_TYPE(blocked_gesture, GET_MOJO_PARENT_INTERFACE_TAG(window_end_id)) AS scroll_type
FROM chrome_input_to_browser_interval_slice_ids;