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


DROP TABLE IF EXISTS android_jank_process_allowlist;
CREATE TABLE android_jank_process_allowlist AS
SELECT process.name, process.upid
FROM process
WHERE process.name IN (
  'com.android.systemui',
  'com.google.android.apps.nexuslauncher',
  'com.google.android.inputmethod.latin'
);

SELECT RUN_METRIC(
  'android/android_hwui_threads.sql',
  'table_name_prefix', 'android_jank',
  'process_allowlist_table', 'android_jank_process_allowlist');

DROP TABLE IF EXISTS android_jank_thread_state_running;
CREATE TABLE android_jank_thread_state_running AS
SELECT utid, ts, dur, state
FROM thread_state
WHERE utid IN (SELECT utid FROM android_jank_main_thread_slices)
AND state = 'Running'
AND dur > 0;

DROP TABLE IF EXISTS android_jank_thread_state_scheduled;
CREATE TABLE android_jank_thread_state_scheduled AS
SELECT utid, ts, dur, state
FROM thread_state
WHERE utid IN (SELECT utid FROM android_jank_main_thread_slices)
AND (state = 'R' OR state = 'R+')
AND dur > 0;

DROP TABLE IF EXISTS android_jank_thread_state_io_wait;
CREATE TABLE android_jank_thread_state_io_wait AS
SELECT utid, ts, dur, state
FROM thread_state
WHERE utid IN (SELECT utid FROM android_jank_main_thread_slices)
AND (((state = 'D' OR state = 'DK') AND io_wait) OR (state = 'DK' AND io_wait IS NULL))
AND dur > 0;

--
-- Main Thread alerts
--

-- Expensive measure/layout

DROP TABLE IF EXISTS android_jank_measure_layout_slices;
CREATE TABLE android_jank_measure_layout_slices AS
SELECT
  process_name,
  utid,
  id,
  ts,
  dur
FROM android_jank_main_thread_slices
WHERE name in ('measure', 'layout')
AND dur >= 3000000;

CREATE VIRTUAL TABLE IF NOT EXISTS android_jank_measure_layout_slices_state
USING span_join(android_jank_measure_layout_slices PARTITIONED utid, android_jank_thread_state_running PARTITIONED utid);

DROP TABLE IF EXISTS android_jank_measure_layout_slices_high_cpu;
CREATE TABLE android_jank_measure_layout_slices_high_cpu AS
SELECT id FROM android_jank_measure_layout_slices_state
GROUP BY id
HAVING SUM(dur) > 3000000;

DROP TABLE IF EXISTS android_jank_measure_layout_alerts;
CREATE TABLE android_jank_measure_layout_alerts AS
SELECT
  process_name,
  ts,
  dur,
  'Expensive measure/layout pass' as alert_name,
  id
FROM android_jank_measure_layout_slices
JOIN android_jank_measure_layout_slices_high_cpu USING (id);

-- Inflation during ListView recycling
-- as additional alerts for expensive layout slices

DROP TABLE IF EXISTS android_jank_listview_inflation_alerts;
CREATE TABLE android_jank_listview_inflation_alerts AS
SELECT
  process_name,
  ts,
  dur,
  'Inflation during ListView recycling' as alert_name
FROM android_jank_main_thread_slices
WHERE name IN ('obtainView', 'setupListItem')
AND EXISTS (
  SELECT 1
  FROM descendant_slice(android_jank_main_thread_slices.id)
  WHERE name = 'inflate')
AND EXISTS(
  SELECT 1
  FROM android_jank_measure_layout_alerts
  JOIN ancestor_slice(android_jank_main_thread_slices.id) USING (id)
);

-- Long View#draw()

DROP TABLE IF EXISTS android_jank_view_draw_slices;
CREATE TABLE android_jank_view_draw_slices AS
SELECT
  process_name,
  utid,
  id,
  ts,
  dur
FROM android_jank_main_thread_slices
WHERE name in ('getDisplayList', 'Record View#draw()')
AND dur >= 3000000;

CREATE VIRTUAL TABLE IF NOT EXISTS android_jank_view_draw_slices_state
USING span_join(android_jank_view_draw_slices PARTITIONED utid, android_jank_thread_state_running PARTITIONED utid);

DROP TABLE IF EXISTS android_jank_view_draw_slices_high_cpu;
CREATE TABLE android_jank_view_draw_slices_high_cpu AS
SELECT id FROM android_jank_view_draw_slices_state
GROUP BY id
HAVING SUM(dur) > 3000000;

DROP TABLE IF EXISTS android_jank_view_draw_alerts;
CREATE TABLE android_jank_view_draw_alerts AS
SELECT
  process_name,
  ts,
  dur,
  'Long View#draw()' as alert_name
FROM android_jank_main_thread_slices
JOIN android_jank_view_draw_slices_high_cpu USING (id);

-- Scheduling delay and Blocking I/O delay

DROP TABLE IF EXISTS android_jank_long_do_frame_slices;
CREATE TABLE android_jank_long_do_frame_slices AS
SELECT
  process_name,
  utid,
  id,
  ts,
  dur
FROM android_jank_main_thread_slices
WHERE name GLOB 'Choreographer#doFrame*'
AND dur >= 5000000;

CREATE VIRTUAL TABLE IF NOT EXISTS android_jank_do_frame_slices_state_scheduled
USING span_join(android_jank_long_do_frame_slices PARTITIONED utid, android_jank_thread_state_scheduled PARTITIONED utid);


DROP TABLE IF EXISTS android_jank_do_frame_slices_long_scheduled;
CREATE TABLE android_jank_do_frame_slices_long_scheduled AS
SELECT id FROM android_jank_do_frame_slices_state_scheduled
GROUP BY id
HAVING SUM(dur) > 5000000;

DROP TABLE IF EXISTS android_jank_scheduling_delay_alerts;
CREATE TABLE android_jank_scheduling_delay_alerts AS
SELECT
  process_name,
  ts,
  dur,
  'Scheduling delay' as alert_name
FROM android_jank_long_do_frame_slices
JOIN android_jank_do_frame_slices_long_scheduled USING (id);

CREATE VIRTUAL TABLE IF NOT EXISTS android_jank_do_frame_slices_state_io_wait
USING span_join(android_jank_long_do_frame_slices PARTITIONED utid, android_jank_thread_state_io_wait PARTITIONED utid);

DROP TABLE IF EXISTS android_jank_do_frame_slices_long_io_wait;
CREATE TABLE android_jank_do_frame_slices_long_io_wait AS
SELECT id FROM android_jank_do_frame_slices_state_io_wait
GROUP BY id
HAVING SUM(dur) > 5000000;

DROP TABLE IF EXISTS android_jank_blocking_delay_alerts;
CREATE TABLE android_jank_blocking_delay_alerts AS
SELECT
  process_name,
  ts,
  dur,
  'Blocking I/O delay' as alert_name
FROM android_jank_do_frame_slices
JOIN android_jank_do_frame_slices_long_io_wait USING (id);

--
-- Render Thread alerts
--

-- Expensive Canvas#saveLayer()

DROP TABLE IF EXISTS android_jank_save_layer_alerts;
CREATE TABLE android_jank_save_layer_alerts AS
SELECT
  process_name,
  ts,
  dur,
  'Expensive rendering with Canvas#saveLayer()' as alert_name
FROM android_jank_render_thread_slices
WHERE name GLOB '*alpha caused *saveLayer *'
AND dur >= 1000000;

-- Path texture churn

DROP TABLE IF EXISTS android_jank_generate_path_alerts;
CREATE TABLE android_jank_generate_path_alerts AS
SELECT
  process_name,
  ts,
  dur,
  'Path texture churn' as alert_name
FROM android_jank_render_thread_slices
WHERE name = 'Generate Path Texture'
AND dur >= 3000000;

-- Expensive Bitmap uploads

DROP TABLE IF EXISTS android_jank_upload_texture_alerts;
CREATE TABLE android_jank_upload_texture_alerts AS
SELECT
  process_name,
  ts,
  dur,
  'Expensive Bitmap uploads' as alert_name
FROM android_jank_render_thread_slices
WHERE name GLOB 'Upload *x* Texture'
AND dur >= 3000000;

-- Merge all alerts tables into one table
DROP TABLE IF EXISTS android_jank_alerts;
CREATE TABLE android_jank_alerts AS
SELECT process_name, ts, dur, alert_name FROM android_jank_measure_layout_alerts
UNION ALL
SELECT process_name, ts, dur, alert_name FROM android_jank_listview_inflation_alerts
UNION ALL
SELECT process_name, ts, dur, alert_name FROM android_jank_scheduling_delay_alerts
UNION ALL
SELECT process_name, ts, dur, alert_name FROM android_jank_blocking_delay_alerts
UNION ALL
SELECT process_name, ts, dur, alert_name FROM android_jank_save_layer_alerts
UNION ALL
SELECT process_name, ts, dur, alert_name FROM android_jank_generate_path_alerts
UNION ALL
SELECT process_name, ts, dur, alert_name FROM android_jank_upload_texture_alerts;

DROP VIEW IF EXISTS android_jank_event;
CREATE VIEW android_jank_event AS
SELECT
  'slice' as track_type,
  process_name || ' warnings' as track_name,
  ts,
  0 as dur,
  group_concat(alert_name) as slice_name
FROM android_jank_alerts
GROUP BY track_type, track_name, ts;

DROP VIEW IF EXISTS android_jank_output;
CREATE VIEW android_jank_output AS
SELECT AndroidJankMetrics(
  'warnings', (
    SELECT RepeatedField(
      AndroidJankMetrics_Warning(
       'ts', ts,
       'dur', dur,
       'process_name', process_name,
       'warning_text', alert_name))
    FROM android_jank_alerts
    ORDER BY process_name, ts, dur));
