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

INCLUDE PERFETTO MODULE android.app_process_starts;
INCLUDE PERFETTO MODULE android.broadcasts;
INCLUDE PERFETTO MODULE android.garbage_collection;
INCLUDE PERFETTO MODULE android.oom_adjuster;
INCLUDE PERFETTO MODULE android.process_metadata;

DROP VIEW IF EXISTS android_oom_adj_intervals_with_detailed_bucket_name;
CREATE PERFETTO VIEW android_oom_adj_intervals_with_detailed_bucket_name AS
SELECT
  ts,
  dur,
  score,
  android_oom_adj_score_to_detailed_bucket_name(score, android_appid) AS bucket,
  upid,
  process_name,
  oom_adj_id,
  oom_adj_ts,
  oom_adj_dur,
  oom_adj_track_id,
  oom_adj_thread_name,
  oom_adj_reason,
  oom_adj_trigger
FROM _oom_adjuster_intervals;

CREATE OR REPLACE PERFETTO FUNCTION get_durations(process_name STRING)
RETURNS TABLE(uint_sleep_dur LONG, total_dur LONG) AS
SELECT
    SUM(CASE WHEN thread_state.state="D" then thread_state.dur ELSE 0 END) AS uint_sleep_dur,
    SUM(thread_state.dur) as total_dur
FROM android_process_metadata
INNER JOIN thread ON thread.upid=android_process_metadata.upid
INNER JOIN thread_state ON thread.utid=thread_state.utid WHERE android_process_metadata.process_name=$process_name;

CREATE OR REPLACE PERFETTO FUNCTION first_user_unlocked() RETURNS INT AS
SELECT COALESCE(MIN(ts), 0) FROM thread_slice
WHERE name GLOB "*android.intent.action.USER_UNLOCKED*";

CREATE PERFETTO TABLE _oom_adj_events_with_src_bucket
AS
SELECT
  LAG(bucket) OVER (PARTITION BY upid ORDER BY ts) AS src_bucket,
  ts,
  bucket,
  process_name,
  oom_adj_reason
FROM android_oom_adj_intervals_with_detailed_bucket_name;

DROP VIEW IF EXISTS oom_adj_events_by_process_name;
CREATE PERFETTO VIEW oom_adj_events_by_process_name AS
SELECT
  src_bucket,
  bucket,
  count(ts) as count,
  process_name
FROM _oom_adj_events_with_src_bucket
  WHERE ts > first_user_unlocked()
GROUP BY process_name, bucket, src_bucket;

DROP VIEW IF EXISTS oom_adj_events_global_by_bucket;
CREATE PERFETTO VIEW oom_adj_events_global_by_bucket AS
SELECT
  src_bucket,
  bucket,
  count(ts) as count,
  NULL as name
FROM _oom_adj_events_with_src_bucket
WHERE
  ts > first_user_unlocked()
GROUP BY bucket, src_bucket;

DROP VIEW IF EXISTS oom_adj_events_by_oom_adj_reason;
CREATE PERFETTO VIEW oom_adj_events_by_oom_adj_reason AS
SELECT
  src_bucket,
  bucket,
  count(ts) as count,
  oom_adj_reason as name
FROM _oom_adj_events_with_src_bucket
WHERE ts > first_user_unlocked()
GROUP BY bucket, src_bucket, oom_adj_reason;

DROP VIEW IF EXISTS android_boot_output;
CREATE PERFETTO VIEW android_boot_output AS
SELECT AndroidBootMetric(
    'system_server_durations', (
        SELECT NULL_IF_EMPTY(ProcessStateDurations(
            'total_dur', total_dur,
            'uninterruptible_sleep_dur', uint_sleep_dur))
        FROM get_durations('system_server')),
    'systemui_durations', (
        SELECT NULL_IF_EMPTY(ProcessStateDurations(
            'total_dur', total_dur,
            'uninterruptible_sleep_dur', uint_sleep_dur))
        FROM get_durations('com.android.systemui')),
    'launcher_durations', (
        SELECT NULL_IF_EMPTY(ProcessStateDurations(
            'total_dur', total_dur,
            'uninterruptible_sleep_dur', uint_sleep_dur))
        FROM get_durations('com.google.android.apps.nexuslauncher')),
    'gms_durations', (
        SELECT NULL_IF_EMPTY(ProcessStateDurations(
            'total_dur', total_dur,
            'uninterruptible_sleep_dur', uint_sleep_dur))
        FROM get_durations('com.google.android.gms.persistent')),
    'launcher_breakdown', (
        SELECT NULL_IF_EMPTY(AndroidBootMetric_LauncherBreakdown(
            'cold_start_dur', dur))
        FROM slice where name="LauncherColdStartup"),
    'full_trace_process_start_aggregation', (
        SELECT NULL_IF_EMPTY(AndroidBootMetric_ProcessStartAggregation(
            'total_start_sum', (SELECT SUM(total_dur) FROM android_app_process_starts),
            'num_of_processes', (SELECT COUNT(*) FROM android_app_process_starts),
            'average_start_time', (SELECT AVG(total_dur) FROM android_app_process_starts)))
          FROM android_app_process_starts),
    'post_boot_process_start_aggregation', (
        SELECT NULL_IF_EMPTY(AndroidBootMetric_ProcessStartAggregation(
            'total_start_sum', (
              SELECT SUM(total_dur)
              FROM android_app_process_starts
              WHERE proc_start_ts > first_user_unlocked()
            ),
            'num_of_processes', (
              SELECT COUNT(*)
              FROM android_app_process_starts
              WHERE proc_start_ts > first_user_unlocked()
            ),
            'average_start_time', (
              SELECT AVG(total_dur)
              FROM android_app_process_starts
              WHERE proc_start_ts > first_user_unlocked()
            )
        ))
    ),
    'full_trace_gc_aggregation', (
        SELECT NULL_IF_EMPTY(AndroidBootMetric_GarbageCollectionAggregation(
            'total_gc_count', (SELECT COUNT(*) FROM android_garbage_collection_events
            ),
            'num_of_processes_with_gc', (SELECT COUNT(process_name) FROM android_garbage_collection_events
            ),
            'num_of_threads_with_gc', (
              SELECT SUM(cnt) FROM (
                SELECT COUNT(*) AS cnt
                FROM android_garbage_collection_events
                GROUP by thread_name, process_name
              )
            ),
            'avg_gc_duration', (SELECT AVG(gc_dur) FROM android_garbage_collection_events),
            'avg_running_gc_duration', (SELECT AVG(gc_running_dur) FROM android_garbage_collection_events),
            'full_gc_count', (
              SELECT COUNT(*)
              FROM android_garbage_collection_events
              WHERE gc_type = "full"
            ),
            'collector_transition_gc_count', (
              SELECT COUNT(*)
              FROM android_garbage_collection_events
              WHERE gc_type = "collector_transition"
            ),
            'young_gc_count', (SELECT COUNT(*) FROM android_garbage_collection_events
              WHERE gc_type = "young"
            ),
            'native_alloc_gc_count', (SELECT COUNT(*) FROM android_garbage_collection_events
              WHERE gc_type = "native_alloc"
            ),
            'explicit_gc_count', (SELECT COUNT(*) FROM android_garbage_collection_events
              WHERE gc_type = "explicit_gc"
            ),
            'alloc_gc_count', (SELECT COUNT(*) FROM android_garbage_collection_events
              WHERE gc_type = "alloc_gc"
            ),
            'mb_per_ms_of_gc', (SELECT SUM(reclaimed_mb)/SUM(gc_running_dur/1e6) AS mb_per_ms_dur
              FROM android_garbage_collection_events
            )
        ))
    ),
    'post_boot_gc_aggregation', (
        SELECT NULL_IF_EMPTY(AndroidBootMetric_GarbageCollectionAggregation(
            'total_gc_count', (SELECT COUNT(*) FROM android_garbage_collection_events
              WHERE gc_ts > first_user_unlocked()
            ),
            'num_of_processes_with_gc', (SELECT COUNT(process_name) FROM android_garbage_collection_events
              WHERE gc_ts > first_user_unlocked()
            ),
            'num_of_threads_with_gc', (SELECT SUM(cnt) FROM (SELECT COUNT(*) AS cnt
              FROM android_garbage_collection_events
              WHERE gc_ts > first_user_unlocked()
              GROUP by thread_name, process_name)
            ),
            'avg_gc_duration', (SELECT AVG(gc_dur) FROM android_garbage_collection_events
              WHERE gc_ts > first_user_unlocked()
            ),
            'avg_running_gc_duration', (SELECT AVG(gc_running_dur) FROM android_garbage_collection_events
              WHERE gc_ts > first_user_unlocked()
            ),
            'full_gc_count', (SELECT COUNT(*) FROM android_garbage_collection_events
              WHERE gc_type = "full" AND gc_ts > first_user_unlocked()
            ),
            'collector_transition_gc_count', (SELECT COUNT(*) FROM android_garbage_collection_events
              WHERE gc_type = "collector_transition" AND gc_ts > (
                SELECT COALESCE(MIN(ts), 0)
                FROM thread_slice
                WHERE name GLOB "*android.intent.action.USER_UNLOCKED*"
                ORDER BY ts ASC LIMIT 1
              )
            ),
            'young_gc_count', (
              SELECT COUNT(*)
              FROM android_garbage_collection_events
              WHERE gc_type = "young" AND gc_ts > (
                SELECT COALESCE(MIN(ts), 0)
                FROM thread_slice
                WHERE name GLOB "*android.intent.action.USER_UNLOCKED*"
                ORDER BY ts ASC LIMIT 1
              )
            ),
            'native_alloc_gc_count', (
              SELECT COUNT(*)
              FROM android_garbage_collection_events
              WHERE gc_type = "native_alloc" AND gc_ts > first_user_unlocked()
            ),
            'explicit_gc_count', (
              SELECT COUNT(*) FROM android_garbage_collection_events
              WHERE gc_type = "explicit_gc" AND gc_ts > (
                SELECT COALESCE(MIN(ts), 0)
                FROM thread_slice
                WHERE name GLOB "*android.intent.action.USER_UNLOCKED*"
                ORDER BY ts ASC LIMIT 1
              )
            ),
            'alloc_gc_count', (
              SELECT COUNT(*) FROM android_garbage_collection_events
              WHERE gc_type = "alloc_gc" AND gc_ts > first_user_unlocked()
            ),
            'mb_per_ms_of_gc', (
              SELECT
                SUM(reclaimed_mb)/SUM(gc_running_dur/1e6) AS mb_per_ms_dur
              FROM android_garbage_collection_events
              WHERE gc_ts > first_user_unlocked()
            )
        ))
    ),
    'post_boot_oom_adjuster_transition_counts_by_process', (
      SELECT RepeatedField(
        AndroidBootMetric_OomAdjusterTransitionCounts(
          'name', process_name,
          'src_bucket', src_bucket,
          'dest_bucket', bucket,
          'count', count
        )
      ) FROM oom_adj_events_by_process_name
    ),
    'post_boot_oom_adjuster_transition_counts_global', (
      SELECT RepeatedField(
        AndroidBootMetric_OomAdjusterTransitionCounts(
          'name', name,
          'src_bucket', src_bucket,
          'dest_bucket', bucket,
          'count', count
        )
      )
      FROM oom_adj_events_global_by_bucket
    ),
    'post_boot_oom_adjuster_transition_counts_by_oom_adj_reason',(
      SELECT RepeatedField(
        AndroidBootMetric_OomAdjusterTransitionCounts(
          'name', name,
          'src_bucket', src_bucket,
          'dest_bucket', bucket,
          'count', count
        )
      )
      FROM oom_adj_events_by_oom_adj_reason
    ),
    'post_boot_oom_adj_bucket_duration_agg_global',(SELECT RepeatedField(
      AndroidBootMetric_OomAdjBucketDurationAggregation(
            'name', name,
            'bucket', bucket,
            'total_dur', total_dur
      ))
      FROM (
        SELECT
          NULL as name,
          bucket,
          SUM(dur) as total_dur
        FROM android_oom_adj_intervals_with_detailed_bucket_name
          WHERE ts > first_user_unlocked()
        GROUP BY bucket)
    ),
    'post_boot_oom_adj_bucket_duration_agg_by_process',(SELECT RepeatedField(
        AndroidBootMetric_OomAdjBucketDurationAggregation(
            'name', name,
            'bucket', bucket,
            'total_dur', total_dur
        )
    )
    FROM (
      SELECT
        process_name as name,
        bucket,
        SUM(dur) as total_dur
      FROM android_oom_adj_intervals_with_detailed_bucket_name
      WHERE ts > first_user_unlocked()
      AND process_name IS NOT NULL
      GROUP BY process_name, bucket)
    ),
    'post_boot_oom_adj_duration_agg',
    (SELECT RepeatedField(
        AndroidBootMetric_OomAdjDurationAggregation(
            'min_oom_adj_dur', min_oom_adj_dur,
            'max_oom_adj_dur', max_oom_adj_dur,
            'avg_oom_adj_dur', avg_oom_adj_dur,
            'oom_adj_event_count', oom_adj_event_count,
            'oom_adj_reason', oom_adj_reason
        )
    )
    FROM (
      SELECT
        MIN(oom_adj_dur) as min_oom_adj_dur,
        MAX(oom_adj_dur) as max_oom_adj_dur,
        AVG(oom_adj_dur) as avg_oom_adj_dur,
        COUNT(DISTINCT(oom_adj_id)) oom_adj_event_count,
        oom_adj_reason
      FROM android_oom_adj_intervals_with_detailed_bucket_name
      WHERE ts > first_user_unlocked()
      GROUP BY oom_adj_reason
      )
    ),
  'post_boot_broadcast_process_count_by_intent', (
    SELECT RepeatedField(
      AndroidBootMetric_BroadcastCountAggregation(
        'name', intent_action,
        'count', process_name_counts
      )
    )
    FROM (
      SELECT
        intent_action,
        COUNT(process_name) as process_name_counts
      FROM _android_broadcasts_minsdk_u
      WHERE ts > first_user_unlocked()
      GROUP BY intent_action
    )
  ),
  'post_boot_broadcast_count_by_process', (
    SELECT RepeatedField(
      AndroidBootMetric_BroadcastCountAggregation(
        'name', process_name,
        'count', broadcast_counts
      )
    )
    FROM (
      SELECT
        process_name,
        COUNT(id) as broadcast_counts
      FROM _android_broadcasts_minsdk_u
      WHERE ts > first_user_unlocked()
      GROUP BY process_name
    )
  ),
  'post_boot_brodcast_duration_agg_by_intent', (
    SELECT RepeatedField(
      AndroidBootMetric_BroadcastDurationAggregation(
        'name', intent_action,
        'avg_duration', avg_duration,
        'max_duration', max_duration,
        'sum_duration', sum_duration
      )
    )
    FROM (
      SELECT
        intent_action,
        AVG(dur) as avg_duration,
        SUM(dur) as sum_duration,
        MAX(dur) as max_duration
      FROM _android_broadcasts_minsdk_u
      WHERE ts > first_user_unlocked()
      GROUP BY intent_action
    )
  ),  'post_boot_brodcast_duration_agg_by_process', (
    SELECT RepeatedField(
      AndroidBootMetric_BroadcastDurationAggregation(
        'name', process_name,
        'avg_duration', avg_duration,
        'max_duration', max_duration,
        'sum_duration', sum_duration
      )
    )
    FROM (
      SELECT
        process_name,
        AVG(dur) as avg_duration,
        SUM(dur) as sum_duration,
        MAX(dur) as max_duration
      FROM _android_broadcasts_minsdk_u
      WHERE ts > first_user_unlocked()
      GROUP BY process_name
    )
  )
);
