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

SELECT IMPORT('android.startup.startups');

-- Helper function to build a Slice proto from a duration.
SELECT CREATE_FUNCTION('STARTUP_SLICE_PROTO(dur INT)', 'PROTO', '
  SELECT AndroidStartupMetric_Slice(
    "dur_ns", $dur,
    "dur_ms", $dur / 1e6
  )
');

-- View containing all the slices for all launches. Generally, this view
-- should not be used. Instead, one of the helper functions below which wrap
-- this view should be used.
DROP VIEW IF EXISTS thread_slices_for_all_launches;
CREATE VIEW thread_slices_for_all_launches AS
SELECT * FROM android_thread_slices_for_all_startups;


-- Given a launch id and GLOB for a slice name,
-- summing the slice durations across the whole startup.
SELECT CREATE_FUNCTION(
  'ANDROID_SUM_DUR_FOR_STARTUP_AND_SLICE(startup_id LONG, slice_name STRING)',
  'INT',
  '
    SELECT SUM(slice_dur)
    FROM android_thread_slices_for_all_startups
    WHERE startup_id = $startup_id AND slice_name GLOB $slice_name
  '
);

-- Given a launch id and GLOB for a slice name, returns the startup slice proto,
-- summing the slice durations across the whole startup.
SELECT CREATE_FUNCTION(
  'DUR_SUM_SLICE_PROTO_FOR_LAUNCH(startup_id LONG, slice_name STRING)',
  'PROTO',
  '
    SELECT NULL_IF_EMPTY(
      STARTUP_SLICE_PROTO(
        ANDROID_SUM_DUR_FOR_STARTUP_AND_SLICE($startup_id, $slice_name)
      )
    )
  '
);

-- Same as |DUR_SUM_SLICE_PROTO_FOR_LAUNCH| except only counting slices happening
-- on the main thread.
SELECT CREATE_FUNCTION(
  'DUR_SUM_MAIN_THREAD_SLICE_PROTO_FOR_LAUNCH(startup_id LONG, slice_name STRING)',
  'PROTO',
  '
    SELECT NULL_IF_EMPTY(
      STARTUP_SLICE_PROTO(
        ANDROID_SUM_DUR_ON_MAIN_THREAD_FOR_STARTUP_AND_SLICE($startup_id, $slice_name)
      )
    )
  '
);

-- Given a launch id and GLOB for a slice name, returns the startup slice proto by
-- taking the duration between the start of the launch and start of the slice.
-- If multiple slices match, picks the latest one which started during the launch.
SELECT CREATE_FUNCTION(
  'LAUNCH_TO_MAIN_THREAD_SLICE_PROTO(startup_id INT, slice_name STRING)',
  'PROTO',
  '
    SELECT NULL_IF_EMPTY(STARTUP_SLICE_PROTO(MAX(slice_ts) - startup_ts))
    FROM android_thread_slices_for_all_startups s
    JOIN thread t USING (utid)
    WHERE
      s.slice_name GLOB $slice_name AND
      s.startup_id = $startup_id AND
      s.is_main_thread AND
      (t.end_ts IS NULL OR t.end_ts >= s.startup_ts_end)
  '
);

-- Given a lauch id, returns the total time spent in GC
SELECT CREATE_FUNCTION(
  'TOTAL_GC_TIME_BY_LAUNCH(startup_id LONG)',
  'INT',
  '
    SELECT SUM(slice_dur)
        FROM android_thread_slices_for_all_startups slice
        WHERE
          slice.startup_id = $startup_id AND
          (
            slice_name GLOB "*semispace GC" OR
            slice_name GLOB "*mark sweep GC" OR
            slice_name GLOB "*concurrent copying GC"
          )
  '
);

-- Given a launch id and package name, returns if baseline or cloud profile is missing.
SELECT CREATE_FUNCTION(
  'MISSING_BASELINE_PROFILE_FOR_LAUNCH(startup_id LONG, pkg_name STRING)',
  'BOOL',
  '
    SELECT (COUNT(slice_name) > 0)
    FROM (
      SELECT *
      FROM ANDROID_SLICES_FOR_STARTUP_AND_SLICE_NAME(
        $startup_id,
        "location=* status=* filter=* reason=*"
      )
      ORDER BY slice_name
    )
    WHERE
      -- when location is the package odex file and the reason is "install" or "install-dm",
      -- if the compilation filter is not "speed-profile", baseline/cloud profile is missing.
      SUBSTR(STR_SPLIT(slice_name, " status=", 0), LENGTH("location=") + 1)
        GLOB ("*" || $pkg_name || "*odex")
      AND (STR_SPLIT(slice_name, " reason=", 1) = "install"
        OR STR_SPLIT(slice_name, " reason=", 1) = "install-dm")
      AND STR_SPLIT(STR_SPLIT(slice_name, " filter=", 1), " reason=", 0) != "speed-profile"
  '
);

-- Given a launch id, returns if there is a main thread run-from-apk slice.
-- Add an exception if "split_config" is in parent slice's name(b/277809828).
-- TODO: remove the exception after Sep 2023 (b/78772867)
SELECT CREATE_FUNCTION(
  'RUN_FROM_APK_FOR_LAUNCH(launch_id LONG)',
  'BOOL',
  '
    SELECT EXISTS(
      SELECT slice_name, startup_id, is_main_thread
      FROM android_thread_slices_for_all_startups s
      JOIN slice ON slice.id = s.slice_id
      LEFT JOIN slice parent ON slice.parent_id = parent.id
      WHERE
        startup_id = $launch_id AND is_main_thread AND
        slice_name GLOB "location=* status=* filter=* reason=*" AND
        STR_SPLIT(STR_SPLIT(slice_name, " filter=", 1), " reason=", 0)
          GLOB ("*" || "run-from-apk" || "*") AND
        (parent.name IS NULL OR
          parent.name NOT GLOB ("OpenDexFilesFromOat(*split_config*apk)"))
    )
  '
);

SELECT CREATE_VIEW_FUNCTION(
  'BINDER_TRANSACTION_REPLY_SLICES_FOR_LAUNCH(startup_id INT, threshold DOUBLE)',
  'name STRING',
  '
    SELECT reply.name AS name
    FROM ANDROID_BINDER_TRANSACTION_SLICES_FOR_STARTUP($startup_id, $threshold) request
    JOIN following_flow(request.id) arrow
    JOIN slice reply ON reply.id = arrow.slice_in
    WHERE reply.dur > $threshold AND request.is_main_thread
  '
);

-- Given a launch id, return if unlock is running by systemui during the launch.
SELECT CREATE_FUNCTION(
  'IS_UNLOCK_RUNNING_DURING_LAUNCH(startup_id LONG)',
  'BOOL',
  '
    SELECT EXISTS(
      SELECT slice.name
      FROM slice, android_startups launches
      JOIN thread_track ON slice.track_id = thread_track.id
      JOIN thread USING(utid)
      JOIN process USING(upid)
      WHERE launches.startup_id = $startup_id
      AND slice.name = "KeyguardUpdateMonitor#onAuthenticationSucceeded"
      AND process.name = "com.android.systemui"
      AND slice.ts >= launches.ts
      AND (slice.ts + slice.dur) <= launches.ts_end
    )
  '
);
