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

-- Matches slices with frames within CUJs and aggregates slices durations within each frame.
-- This allows comparing the cumulative durations of a set of slices vs what was the expected
-- duration of each frame. It can be a useful heuristic to figure out what contributed to
-- frames missing their expected deadlines.
--
-- EXAMPLE:
--
--
-- CUJ Frames:
--
--      |==== FRAME 1 ====|
--                  |====== FRAME 2 ======|
--                                             |== FRAME 3 ==|
--                                                          |== FRAME 4 ==|
--
-- Main thread frame boundaries:
--
--      |= FRAME 1 =|==== FRAME 2 ====|        |= FRAME 3 =||= FRAME 4 =|
--
-- `relevant_slice_table_name` slices:
--
--  |== binder ==|      |== binder ==|   |=b=|   |=b=||=b=|
--
--
-- OUTPUT:
--
-- Data in `*_slice_in_frame` - slices from `relevant_slice_table_name` matched
-- to frames and trimmed to their boundaries:
--
-- * FRAME 1:
--      | binder |
-- * FRAME 2:
--                      |== binder ==|
-- * FRAME 3:
--                                               |=b=||=b=|
-- * FRAME 4 - not present in the output
--
--
-- Data in `*_slice_in_frame_agg` is just an aggregation of durations in  *_slice_in_frame.


-- For simplicity we allow `relevant_slice_table_name` to be based on any
-- of the slice tables. This table filters it down to only include slices within
-- the (broadly bounded) CUJ and on the specific process / thread.
-- Using TABLE and not VIEW as this gives better, localized error messages in cases
-- `relevant_slice_table_name` is not correct (e.g. missing cuj_id).
DROP TABLE IF EXISTS {{table_name_prefix}}_query_slice;
CREATE TABLE {{table_name_prefix}}_query_slice AS
SELECT
  slice.cuj_id,
  slice.utid,
  slice.id,
  slice.name,
  slice.ts,
  slice.dur,
  slice.ts_end
FROM {{relevant_slice_table_name}} slice
JOIN {{slice_table_name}} android_jank_cuj_slice_table
  USING (cuj_id, id);

-- Flat view of frames and slices matched and "trimmed" to each frame boundaries.
DROP VIEW IF EXISTS {{table_name_prefix}}_slice_in_frame;
CREATE VIEW {{table_name_prefix}}_slice_in_frame AS
SELECT
  frame.*,
  query_slice.id AS slice_id,
  query_slice.utid AS slice_utid,
  query_slice.name AS slice_name,
  MAX(query_slice.ts, frame_boundary.ts) AS slice_ts,
  MIN(query_slice.ts_end, frame_boundary.ts_end) AS slice_ts_end,
  MIN(query_slice.ts_end, frame_boundary.ts_end) - MAX(query_slice.ts, frame_boundary.ts) AS slice_dur,
  query_slice.ts_end AS ts_end_original
FROM {{frame_table_name}} frame
-- We want to use different boundaries depending on which thread's slices the query is targetting.
JOIN {{frame_boundary_table_name}} frame_boundary USING (cuj_id, vsync)
JOIN {{table_name_prefix}}_query_slice query_slice
  ON frame_boundary.cuj_id = query_slice.cuj_id
    AND ANDROID_JANK_CUJ_SLICE_OVERLAPS(frame_boundary.ts, frame_boundary.dur, query_slice.ts, query_slice.dur);

-- Aggregated view of frames and slices overall durations within each frame boundaries.
DROP VIEW IF EXISTS {{table_name_prefix}}_slice_in_frame_agg;
CREATE VIEW {{table_name_prefix}}_slice_in_frame_agg AS
SELECT
  cuj_id,
  frame_number,
  vsync,
  dur_expected,
  app_missed,
  sf_missed,
  1.0 * SUM(slice_dur) / dur_expected AS slice_dur_div_frame_dur_expected,
  SUM(slice_dur) AS slice_dur_sum,
  MAX(slice_dur) AS slice_dur_max
FROM {{table_name_prefix}}_slice_in_frame
GROUP BY cuj_id, frame_number, vsync, dur_expected, app_missed, sf_missed;
