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

-- Table of suspended and awake slices.
--
-- Selects either the minimal or full ftrace source depending on what's
-- available, marks suspended periods, and complements them to give awake
-- periods.
CREATE PERFETTO TABLE android_suspend_state (
  -- Timestamp
  ts TIMESTAMP,
  -- Duration
  dur DURATION,
  -- 'awake' or 'suspended'
  power_state STRING
) AS
WITH
  suspend_slice_from_minimal AS (
    SELECT
      ts,
      dur
    FROM track AS t
    JOIN slice AS s
      ON s.track_id = t.id
    WHERE
      t.name = 'Suspend/Resume Minimal'
  ),
  suspend_slice AS (
    SELECT
      ts,
      dur
    FROM suspend_slice_from_minimal
    UNION ALL
    SELECT
      ts,
      dur
    FROM slice
    JOIN track
      ON slice.track_id = track.id
    WHERE
      track.name = 'Suspend/Resume Latency'
      AND (
        slice.name = 'syscore_resume(0)' OR slice.name = 'timekeeping_freeze(0)'
      )
      AND dur != -1
      AND NOT EXISTS(
        SELECT
          *
        FROM suspend_slice_from_minimal
      )
  ),
  awake_slice AS (
    -- If we don't have any rows, use the trace bounds if bounds are defined.
    SELECT
      trace_start() AS ts,
      trace_dur() AS dur
    WHERE
      (
        SELECT
          count(*)
        FROM suspend_slice
      ) = 0 AND dur > 0
    UNION ALL
    -- If we do have rows, create one slice from the trace start to the first suspend.
    SELECT
      trace_start() AS ts,
      (
        SELECT
          min(ts)
        FROM suspend_slice
      ) - trace_start() AS dur
    WHERE
      (
        SELECT
          count(*)
        FROM suspend_slice
      ) != 0
    UNION ALL
    -- And then one slice for each suspend, from the end of the suspend to the
    -- start of the next one (or the end of the trace if there is no next one).
    SELECT
      ts + dur AS ts,
      coalesce(lead(ts) OVER (ORDER BY ts), trace_end()) - ts - dur AS dur
    FROM suspend_slice
  )
SELECT
  ts,
  dur,
  'awake' AS power_state
FROM awake_slice
UNION ALL
SELECT
  ts,
  dur,
  'suspended' AS power_state
FROM suspend_slice
ORDER BY
  ts;

-- Order by will cause Perfetto table to index by ts.;

-- Extracts the duration without counting CPU suspended time from an event.
-- This is the same as converting an event duration from wall clock to monotonic clock.
-- If there was no CPU suspend, the result is same as |dur|.
CREATE PERFETTO FUNCTION _extract_duration_without_suspend(
    -- Timestamp of event.
    ts TIMESTAMP,
    -- Duration of event.
    dur DURATION
)
RETURNS LONG AS
SELECT
  to_monotonic($ts + $dur) - to_monotonic($ts);
