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

-- Needed for the scroll_jank table to tell which updates were janky.
SELECT RUN_METRIC('chrome/scroll_jank.sql');

--------------------------------------------------------------------------------
-- Get all the track ids relevant to the critical path.
--------------------------------------------------------------------------------

-- Grab the track of the browser. sendTouchEvent is a Java category event which
-- only occurs on the browser. This saves us the trouble of dealing with all the
-- different possible names of the browser (when including system tracing).
DROP VIEW IF EXISTS browser_main_track_id;
CREATE VIEW browser_main_track_id AS
SELECT
  track_id AS id
FROM slice
WHERE
  name = "sendTouchEvent"
LIMIT 1;

DROP VIEW IF EXISTS viz_compositor_track_id;
CREATE VIEW viz_compositor_track_id AS
SELECT
  id
FROM thread_track
WHERE
  utid = (
    SELECT
      utid
    FROM thread
    WHERE
      name = "VizCompositorThread"
  )
LIMIT 1;

-- Grab the track of the GPU. gpu/command_buffer is a toplevel category event
-- which only occurs on the gpu main. This saves us the trouble of dealing with
-- all the different possible names of the GPU process (when including system
-- tracing).
DROP VIEW IF EXISTS gpu_main_track_id;
CREATE VIEW gpu_main_track_id AS
SELECT
  track_id AS id
FROM slice
WHERE
  EXTRACT_ARG(arg_set_id, "task.posted_from.file_name") GLOB
      "*gpu/command_buffer/service/scheduler.cc"
LIMIT 1;

-- TODO(nuskos): Determine a good way to get all the renderer track_ids (each
--               scroll will have a single renderer main and a single renderer
--               compositor however different scroll updates could have
--               DIFFERENT renderers so bit tricky). Ignore this complexity for
--               now until we have a single task we want to blame jank on.

--------------------------------------------------------------------------------
-- Grab the last LatencyInfo.Flow for each trace_id on the browser main.
--------------------------------------------------------------------------------
DROP VIEW IF EXISTS browser_flows;
CREATE VIEW browser_flows AS
SELECT
  EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id,
  EXTRACT_ARG(arg_set_id, "chrome_latency_info.step") AS flow_step,
  track_id,
  max(ts) AS ts
FROM slice
WHERE
  track_id = (
    SELECT id FROM browser_main_track_id
  ) AND
  name = "LatencyInfo.Flow"
  GROUP BY trace_id;

-- Grab the last LatencyInfo.Flow for each trace_id on the VizCompositor.
DROP VIEW IF EXISTS viz_flows;
CREATE VIEW viz_flows AS
SELECT
  EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id,
  EXTRACT_ARG(arg_set_id, "chrome_latency_info.step") AS flow_step,
  track_id,
  max(ts) AS ts
FROM slice
WHERE
  track_id = (
    SELECT id FROM viz_compositor_track_id
  ) AND
  name = "LatencyInfo.Flow"
  GROUP BY trace_id;

-- Grab the last LatencyInfo.Flow for each trace_id on the GPU main.
DROP VIEW IF EXISTS gpu_flows;
CREATE VIEW gpu_flows AS
SELECT
  EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id,
  EXTRACT_ARG(arg_set_id, "chrome_latency_info.step") AS flow_step,
  track_id,
  max(ts) AS ts
FROM slice
WHERE
  track_id = (
    SELECT id FROM gpu_main_track_id
  ) AND
  name = "LatencyInfo.Flow"
  GROUP BY trace_id;

--------------------------------------------------------------------------------
-- Finally join the relevant tracks/flows to the individual scrolls.
--------------------------------------------------------------------------------

-- Keeping only the GestureScrollUpdates join the maximum flows with their
-- associated scrolls. We only keep non-coalesced scrolls.
DROP VIEW IF EXISTS scroll_with_browser_gpu_and_viz_flows;
CREATE VIEW scroll_with_browser_gpu_and_viz_flows AS
SELECT
  scroll.trace_id,
  scroll.scroll_id,
  scroll.ts,
  scroll.dur,
  scroll.track_id,
  browser_flows.ts AS browser_flow_ts,
  browser_flows.flow_step AS browser_flow_step,
  browser_flows.track_id AS browser_track_id,
  viz_flows.ts AS viz_flow_ts,
  viz_flows.flow_step AS viz_flow_step,
  viz_flows.track_id AS viz_track_id,
  gpu_flows.ts AS gpu_flow_ts,
  gpu_flows.flow_step AS gpu_flow_step,
  gpu_flows.track_id AS gpu_track_id
FROM (
  SELECT
    trace_id,
    id AS scroll_id,
    ts,
    dur,
    track_id
  FROM scroll_jank
) scroll JOIN browser_flows ON
    scroll.trace_id = browser_flows.trace_id
  JOIN viz_flows ON viz_flows.trace_id = scroll.trace_id
  JOIN gpu_flows ON gpu_flows.trace_id = scroll.trace_id;

--------------------------------------------------------------------------------
-- Below we determine individual causes of blocking tasks.
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Determine if a CopyOutputRequest blocked any important threads.
--------------------------------------------------------------------------------

-- These are the events that block the Browser Main or the VizCompositor thread.
DROP VIEW IF EXISTS blocking_browser_gpu_and_viz_copies;
CREATE VIEW blocking_browser_gpu_and_viz_copies AS
SELECT
  id,
  ts,
  dur,
  track_id
FROM slice
WHERE
  (
    (
      name = "viz.mojom.CopyOutputResultSender" OR
      name = "GLRenderer::CopyDrawnRenderPass"
    ) AND
    track_id = (SELECT id FROM browser_main_track_id)
  ) OR (
    EXTRACT_ARG(arg_set_id, "task.posted_from.file_name") GLOB
        "*components/viz/common/frame_sinks/copy_output_request.cc" AND
    track_id = (SELECT id FROM viz_compositor_track_id)
  ) OR (
    name = "SkiaOutputSurfaceImplOnGpu::CopyOutput" AND
    track_id = (SELECT id FROM gpu_main_track_id)
  );

-- Determine based on the LatencyInfo.Flow timestamp and the copy task overlap
-- if this scroll might have been delayed because of the copy.
DROP VIEW IF EXISTS blocking_copy_tasks;
CREATE VIEW blocking_copy_tasks AS
SELECT
  scroll.scroll_id,
  scroll.trace_id,
  copy.id,
  copy.ts,
  copy.dur,
  copy.track_id,
  CASE WHEN copy.track_id = scroll.browser_track_id THEN
    CASE WHEN copy.ts < scroll.browser_flow_ts THEN
      TRUE
    ELSE
      FALSE
    END
  WHEN copy.track_id = scroll.viz_track_id THEN
    CASE WHEN copy.ts < scroll.viz_flow_ts THEN
      TRUE
    ELSE
      FALSE
    END
  WHEN copy.track_id = scroll.gpu_track_id THEN
    CASE WHEN copy.ts < scroll.gpu_flow_ts THEN
      TRUE
    ELSE
      FALSE
    END
  ELSE
    FALSE
  END AS blocked_by_copy
FROM
  scroll_with_browser_gpu_and_viz_flows scroll JOIN
  blocking_browser_gpu_and_viz_copies copy ON
  scroll.ts + scroll.dur >= copy.ts AND
  copy.ts + copy.dur >= scroll.ts;

-- Group by scroll so we can equally join one reply to the ScrollJankAndCauses
-- view.
DROP VIEW IF EXISTS screenshot_overlapping_scrolls;
CREATE VIEW screenshot_overlapping_scrolls AS
SELECT
  scroll_id, trace_id, SUM(blocked_by_copy) > 0 AS blocked_by_copy_request
FROM blocking_copy_tasks
GROUP BY 1, 2;

--------------------------------------------------------------------------------
-- Check for blocking language_detection on the browser thread
--------------------------------------------------------------------------------
DROP VIEW IF EXISTS blocking_browser_language_detection;
CREATE VIEW blocking_browser_language_detection AS
SELECT
  id,
  ts,
  dur,
  track_id
FROM slice
WHERE
  (
    name = "language_detection.mojom.LanguageDetectionService" AND
    track_id = (SELECT id FROM browser_main_track_id)
  );

DROP VIEW IF EXISTS blocking_language_detection_tasks;
CREATE VIEW blocking_language_detection_tasks AS
SELECT
  scroll.scroll_id,
  scroll.trace_id,
  lang.id,
  lang.ts,
  lang.dur,
  lang.track_id,
  CASE WHEN lang.track_id = scroll.browser_track_id THEN
    CASE WHEN lang.ts < scroll.browser_flow_ts THEN
      TRUE
    ELSE
      FALSE
    END
  END AS blocked_by_language_detection
FROM
  scroll_with_browser_gpu_and_viz_flows scroll JOIN
  blocking_browser_language_detection lang ON
  scroll.ts + scroll.dur >= lang.ts AND
  lang.ts + lang.dur >= scroll.ts;

DROP VIEW IF EXISTS language_detection_overlapping_scrolls;
CREATE VIEW language_detection_overlapping_scrolls AS
SELECT
  scroll_id, trace_id,
  SUM(blocked_by_language_detection) > 0 AS blocked_by_language_detection
FROM blocking_language_detection_tasks
GROUP BY 1, 2;

--------------------------------------------------------------------------------
-- Finally join the causes together for easy grouping.
--------------------------------------------------------------------------------
DROP VIEW IF EXISTS scroll_jank_cause_blocking_task;
CREATE VIEW scroll_jank_cause_blocking_task AS
  SELECT
    lang.scroll_id,
    lang.blocked_by_language_detection,
    copy.blocked_by_copy_request
  FROM
    language_detection_overlapping_scrolls lang JOIN
    screenshot_overlapping_scrolls copy ON copy.scroll_id = lang.scroll_id;
