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

CREATE PERFETTO FUNCTION _extract_freezer_pid(
    name STRING
)
RETURNS LONG AS
SELECT
  cast_int!(reverse(str_split(reverse(str_split($name, ' ', 1)), ':', 0)));

-- Converts a pid to a upid using the timestamp of occurence of an event from
-- |pid| to disambiguate duplicate pids.
--
-- This is still best effort because it relies on having information about
-- process start and end in the trace. In the edge case that we are missing this,
-- it best effort returns the last upid.
CREATE PERFETTO FUNCTION _pid_to_upid(
    -- Pid to convert from.
    pid LONG,
    -- Timestamp of an event from the |pid|.
    event_ts TIMESTAMP
)
-- Returns the converted upid.
RETURNS LONG AS
WITH
  process_lifetime AS (
    SELECT
      pid,
      upid,
      coalesce(start_ts, trace_start()) AS start_ts,
      coalesce(end_ts, trace_end()) AS end_ts
    FROM process
  )
SELECT
  upid
FROM process_lifetime
WHERE
  pid = $pid AND $event_ts BETWEEN start_ts AND end_ts
ORDER BY
  upid DESC
LIMIT 1;

-- Translate unfreeze reason from INT to STRING.
-- See: frameworks/proto_logging/stats/atoms.proto
CREATE PERFETTO FUNCTION _translate_unfreeze_reason(
    reason LONG
)
RETURNS STRING AS
SELECT
  CASE
    WHEN $reason = 0
    THEN 'none'
    WHEN $reason = 1
    THEN 'activity'
    WHEN $reason = 2
    THEN 'finish_receiver'
    WHEN $reason = 3
    THEN 'start_receiver'
    WHEN $reason = 4
    THEN 'bind_service'
    WHEN $reason = 5
    THEN 'unbind_service'
    WHEN $reason = 6
    THEN 'start_service'
    WHEN $reason = 7
    THEN 'get_provider'
    WHEN $reason = 8
    THEN 'remove_provider'
    WHEN $reason = 9
    THEN 'ui_visibility'
    WHEN $reason = 10
    THEN 'allowlist'
    WHEN $reason = 11
    THEN 'process_begin'
    WHEN $reason = 12
    THEN 'process_end'
    WHEN $reason = 13
    THEN 'trim_memory'
    WHEN $reason = 15
    THEN 'ping'
    WHEN $reason = 16
    THEN 'file_locks'
    WHEN $reason = 17
    THEN 'file_lock_check_failure'
    WHEN $reason = 18
    THEN 'binder_txns'
    WHEN $reason = 19
    THEN 'feature_flags'
    WHEN $reason = 20
    THEN 'short_fgs_timeout'
    WHEN $reason = 21
    THEN 'system_init'
    WHEN $reason = 22
    THEN 'backup'
    WHEN $reason = 23
    THEN 'shell'
    WHEN $reason = 24
    THEN 'remove_task'
    WHEN $reason = 25
    THEN 'uid_idle'
    WHEN $reason = 26
    THEN 'stop_service'
    WHEN $reason = 27
    THEN 'executing_service'
    WHEN $reason = 28
    THEN 'restriction_change'
    WHEN $reason = 29
    THEN 'component_disabled'
    ELSE NULL
  END;

-- All frozen processes and their frozen duration.
CREATE PERFETTO TABLE android_freezer_events (
  -- Upid of frozen process
  upid JOINID(process.id),
  -- Pid of frozen process
  pid LONG,
  -- Timestamp process was frozen.
  ts TIMESTAMP,
  -- Duration process was frozen for.
  dur DURATION,
  -- Unfreeze reason Integer.
  unfreeze_reason_int LONG,
  -- Unfreeze reason String.
  unfreeze_reason_str STRING
) AS
WITH
  freeze AS (
    SELECT
      ts,
      _extract_freezer_pid(name) AS pid,
      _pid_to_upid(_extract_freezer_pid(name), ts) AS upid,
      'freeze' AS type,
      NULL AS unfreeze_reason
    FROM slice
    WHERE
      name GLOB 'Freeze *:*'
  ),
  unfreeze AS (
    SELECT
      ts,
      _extract_freezer_pid(name) AS pid,
      _pid_to_upid(_extract_freezer_pid(name), ts) AS upid,
      'unfreeze' AS type,
      str_split(name, ' ', 2) AS unfreeze_reason
    FROM slice
    WHERE
      name GLOB 'Unfreeze *:*'
  ),
  merged AS (
    SELECT
      *
    FROM freeze
    UNION ALL
    SELECT
      *
    FROM unfreeze
  ),
  starts AS (
    SELECT
      type,
      upid,
      pid,
      ts,
      coalesce(lead(ts) OVER (PARTITION BY upid ORDER BY ts), trace_end()) - ts AS dur,
      cast_int!(lead(unfreeze_reason) OVER (PARTITION BY upid ORDER BY ts)) AS unfreeze_reason
    FROM merged
  )
SELECT
  upid,
  pid,
  ts,
  dur,
  unfreeze_reason AS unfreeze_reason_int,
  _translate_unfreeze_reason(unfreeze_reason) AS unfreeze_reason_str
FROM starts
WHERE
  starts.type = 'freeze' AND upid IS NOT NULL;
