-- Copyright 2024 The Chromium Authors
-- Use of this source code is governed by a BSD-style license that can be
-- found in the LICENSE file.

-- List Speedometer 2.1 test marks. Used to find relevant slices.
CREATE PERFETTO VIEW _chrome_speedometer_2_1_mark_name (
  -- Expected slice name
  name STRING,
  -- Suite name
  suite_name STRING,
  -- Test name
  test_name STRING,
  -- Mark type
  mark_type STRING
) AS
WITH
  data(suite_name, test_name) AS (
    SELECT
      *
    FROM (VALUES
      ('Angular2-TypeScript-TodoMVC', 'Adding100Items'),
      ('Angular2-TypeScript-TodoMVC', 'CompletingAllItems'),
      ('Angular2-TypeScript-TodoMVC', 'DeletingItems'),
      ('AngularJS-TodoMVC', 'Adding100Items'),
      ('AngularJS-TodoMVC', 'CompletingAllItems'),
      ('AngularJS-TodoMVC', 'DeletingAllItems'),
      ('BackboneJS-TodoMVC', 'Adding100Items'),
      ('BackboneJS-TodoMVC', 'CompletingAllItems'),
      ('BackboneJS-TodoMVC', 'DeletingAllItems'),
      ('Elm-TodoMVC', 'Adding100Items'),
      ('Elm-TodoMVC', 'CompletingAllItems'),
      ('Elm-TodoMVC', 'DeletingItems'),
      ('EmberJS-Debug-TodoMVC', 'Adding100Items'),
      ('EmberJS-Debug-TodoMVC', 'CompletingAllItems'),
      ('EmberJS-Debug-TodoMVC', 'DeletingItems'),
      ('EmberJS-TodoMVC', 'Adding100Items'),
      ('EmberJS-TodoMVC', 'CompletingAllItems'),
      ('EmberJS-TodoMVC', 'DeletingItems'),
      ('Flight-TodoMVC', 'Adding100Items'),
      ('Flight-TodoMVC', 'CompletingAllItems'),
      ('Flight-TodoMVC', 'DeletingItems'),
      ('Inferno-TodoMVC', 'Adding100Items'),
      ('Inferno-TodoMVC', 'CompletingAllItems'),
      ('Inferno-TodoMVC', 'DeletingItems'),
      ('Preact-TodoMVC', 'Adding100Items'),
      ('Preact-TodoMVC', 'CompletingAllItems'),
      ('Preact-TodoMVC', 'DeletingItems'),
      ('React-Redux-TodoMVC', 'Adding100Items'),
      ('React-Redux-TodoMVC', 'CompletingAllItems'),
      ('React-Redux-TodoMVC', 'DeletingItems'),
      ('React-TodoMVC', 'Adding100Items'),
      ('React-TodoMVC', 'CompletingAllItems'),
      ('React-TodoMVC', 'DeletingAllItems'),
      ('Vanilla-ES2015-Babel-Webpack-TodoMVC', 'Adding100Items'),
      ('Vanilla-ES2015-Babel-Webpack-TodoMVC', 'CompletingAllItems'),
      ('Vanilla-ES2015-Babel-Webpack-TodoMVC', 'DeletingItems'),
      ('Vanilla-ES2015-TodoMVC', 'Adding100Items'),
      ('Vanilla-ES2015-TodoMVC', 'CompletingAllItems'),
      ('Vanilla-ES2015-TodoMVC', 'DeletingItems'),
      ('VanillaJS-TodoMVC', 'Adding100Items'),
      ('VanillaJS-TodoMVC', 'CompletingAllItems'),
      ('VanillaJS-TodoMVC', 'DeletingAllItems'),
      ('VueJS-TodoMVC', 'Adding100Items'),
      ('VueJS-TodoMVC', 'CompletingAllItems'),
      ('VueJS-TodoMVC', 'DeletingAllItems'),
      ('jQuery-TodoMVC', 'Adding100Items'),
      ('jQuery-TodoMVC', 'CompletingAllItems'),
      ('jQuery-TodoMVC', 'DeletingAllItems')) AS _values
  ),
  mark_type(mark_type) AS (
    SELECT
      *
    FROM (VALUES
      ('start'),
      ('sync-end'),
      ('async-end')) AS _values
  )
SELECT
  suite_name || '.' || test_name || '-' || mark_type AS name,
  suite_name,
  test_name,
  mark_type
FROM data, mark_type;

-- Augmented slices for Speedometer measurements.
-- These are the intervals of time Speedometer uses to compute the final score.
-- There are two intervals that are measured for every test: sync and async
-- sync is the time between the start and sync-end marks, async is the time
-- between the sync-end and async-end marks.
CREATE PERFETTO TABLE chrome_speedometer_2_1_measure (
  -- Start timestamp of the measure slice
  ts TIMESTAMP,
  -- Duration of the measure slice
  dur DURATION,
  -- Full measure name
  name STRING,
  -- Speedometer iteration the slice belongs to.
  iteration LONG,
  -- Suite name
  suite_name STRING,
  -- Test name
  test_name STRING,
  -- Type of the measure (sync or async)
  measure_type STRING
) AS
WITH
  mark AS (
    SELECT
      s.id AS slice_id,
      rank() OVER (PARTITION BY name ORDER BY ts ASC) AS iteration,
      m.suite_name,
      m.test_name,
      m.mark_type
    -- Join with _chrome_speedometer_2_1_mark_name to look only at slices with expected names.
    FROM slice AS s
    JOIN _chrome_speedometer_2_1_mark_name AS m
      USING (name)
    WHERE
      category = 'blink.user_timing'
  ),
  -- Get the 3 test timestamps (start, sync-end, async-end) in one row. Using a
  -- the LAG window function and partitioning by test. 2 out of the 3 rows
  -- generated per test will have some NULL ts values.
  augmented AS (
    SELECT
      iteration,
      suite_name,
      test_name,
      ts AS async_end_ts,
      lag(ts, 1) OVER (PARTITION BY iteration, suite_name, test_name ORDER BY ts ASC) AS sync_end_ts,
      lag(ts, 2) OVER (PARTITION BY iteration, suite_name, test_name ORDER BY ts ASC) AS start_ts,
      count() OVER (PARTITION BY iteration, suite_name, test_name ORDER BY ts ASC) AS mark_count
    FROM mark
    JOIN slice
      USING (slice_id)
  ),
  filtered AS (
    SELECT
      *
    FROM augmented
    -- This server 2 purposes: make sure we have all the marks (think truncated
    -- trace), and remove the NULL ts values due to the LAG window function.
    WHERE
      mark_count = 3
  ),
  base AS (
    SELECT
      sync_end_ts AS ts,
      async_end_ts - sync_end_ts AS dur,
      iteration,
      suite_name,
      test_name,
      'async' AS measure_type
    FROM filtered
    UNION ALL
    SELECT
      start_ts AS ts,
      sync_end_ts - start_ts AS dur,
      iteration,
      suite_name,
      test_name,
      'sync' AS measure_type
    FROM filtered
  )
SELECT
  ts,
  dur,
  suite_name || '.' || test_name || '-' || measure_type AS name,
  iteration,
  suite_name,
  test_name,
  measure_type
FROM base;

-- Slice that covers one Speedometer iteration.
-- This slice is actually estimated as a default Speedometer run will not emit
-- marks to cover this interval. The metrics associated are the same ones
-- Speedometer would output, but note we use ns precision (Speedometer uses
-- ~100us) so the actual values might differ a bit. Also note Speedometer
-- returns the values in ms these here and in ns.
CREATE PERFETTO TABLE chrome_speedometer_2_1_iteration (
  -- Start timestamp of the iteration
  ts TIMESTAMP,
  -- Duration of the iteration
  dur DURATION,
  -- Iteration name
  name STRING,
  -- Iteration number
  iteration LONG,
  -- Geometric mean of the suite durations for this iteration.
  geomean DOUBLE,
  -- Speedometer score for this iteration (The total score for a run in the
  -- average of all iteration scores).
  score DOUBLE
) AS
SELECT
  min(start) AS ts,
  max(end) - min(start) AS dur,
  'iteration-' || iteration AS name,
  iteration,
  -- Compute geometric mean using LN instead of multiplication to prevent
  -- overflows
  exp(avg(ln(suite_total))) AS geomean,
  1000 / exp(avg(ln(suite_total))) * 60 / 3 AS score
FROM (
  SELECT
    iteration,
    sum(dur / (
      1000.0 * 1000.0
    )) AS suite_total,
    min(ts) AS start,
    max(ts + dur) AS end
  FROM chrome_speedometer_2_1_measure
  GROUP BY
    suite_name,
    iteration
)
GROUP BY
  iteration;

-- Returns the Speedometer 2.1 score for all iterations in the trace
CREATE PERFETTO FUNCTION chrome_speedometer_2_1_score()
-- Speedometer 2.1 score
RETURNS DOUBLE AS
SELECT
  avg(score)
FROM chrome_speedometer_2_1_iteration;

-- Returns the utid for the main thread that ran Speedometer 2.1
CREATE PERFETTO FUNCTION chrome_speedometer_2_1_renderer_main_utid()
-- Renderer main utid
RETURNS LONG AS
SELECT
  utid
FROM thread_track
WHERE
  id IN (
    SELECT
      track_id
    FROM slice
    JOIN _chrome_speedometer_2_1_mark_name
      USING (name)
    WHERE
      category = 'blink.user_timing'
  );
