[trace processor] Add RAIL Mode power metrics

Adds 3 metrics for measuring power according to the current RAIL mode as
reported by Scheduler.RAILMode:
1) True power rail values broken down by subsystem if the device has
power rails
2) Estimated power estimates calculated using CPU clock frequency and
power profiles
3) CPU times for when a system trace is not available

Additionally this makes android/android_cpu_agg.sql and
android/android_proxy_power.sql loadable via RUN_METRIC multiple times.

Bug: b/169053635
Change-Id: Id80d84e4e9e6db193836ff964a07c6d739ba7a32
diff --git a/Android.bp b/Android.bp
index 2a2d18b..7f5b3fd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6923,6 +6923,7 @@
     "src/trace_processor/metrics/android/java_heap_histogram.sql",
     "src/trace_processor/metrics/android/java_heap_stats.sql",
     "src/trace_processor/metrics/android/mem_stats_priority_breakdown.sql",
+    "src/trace_processor/metrics/android/power_drain_in_watts.sql",
     "src/trace_processor/metrics/android/power_profile_data.sql",
     "src/trace_processor/metrics/android/process_counter_span_view.sql",
     "src/trace_processor/metrics/android/process_mem.sql",
@@ -6931,8 +6932,12 @@
     "src/trace_processor/metrics/android/process_unagg_mem_view.sql",
     "src/trace_processor/metrics/android/span_view_stats.sql",
     "src/trace_processor/metrics/android/unsymbolized_frames.sql",
+    "src/trace_processor/metrics/chrome/actual_power_by_rail_mode.sql",
     "src/trace_processor/metrics/chrome/chrome_processes.sql",
     "src/trace_processor/metrics/chrome/chrome_thread_slice_with_cpu_time.sql",
+    "src/trace_processor/metrics/chrome/cpu_time_by_rail_mode.sql",
+    "src/trace_processor/metrics/chrome/estimated_power_by_rail_mode.sql",
+    "src/trace_processor/metrics/chrome/rail_modes.sql",
     "src/trace_processor/metrics/chrome/scroll_flow_event.sql",
     "src/trace_processor/metrics/chrome/scroll_flow_event_queuing_delay.sql",
     "src/trace_processor/metrics/chrome/scroll_jank.sql",
diff --git a/BUILD b/BUILD
index f699a84..eccf7fd 100644
--- a/BUILD
+++ b/BUILD
@@ -798,6 +798,7 @@
         "src/trace_processor/metrics/android/java_heap_histogram.sql",
         "src/trace_processor/metrics/android/java_heap_stats.sql",
         "src/trace_processor/metrics/android/mem_stats_priority_breakdown.sql",
+        "src/trace_processor/metrics/android/power_drain_in_watts.sql",
         "src/trace_processor/metrics/android/power_profile_data.sql",
         "src/trace_processor/metrics/android/process_counter_span_view.sql",
         "src/trace_processor/metrics/android/process_mem.sql",
@@ -806,8 +807,12 @@
         "src/trace_processor/metrics/android/process_unagg_mem_view.sql",
         "src/trace_processor/metrics/android/span_view_stats.sql",
         "src/trace_processor/metrics/android/unsymbolized_frames.sql",
+        "src/trace_processor/metrics/chrome/actual_power_by_rail_mode.sql",
         "src/trace_processor/metrics/chrome/chrome_processes.sql",
         "src/trace_processor/metrics/chrome/chrome_thread_slice_with_cpu_time.sql",
+        "src/trace_processor/metrics/chrome/cpu_time_by_rail_mode.sql",
+        "src/trace_processor/metrics/chrome/estimated_power_by_rail_mode.sql",
+        "src/trace_processor/metrics/chrome/rail_modes.sql",
         "src/trace_processor/metrics/chrome/scroll_flow_event.sql",
         "src/trace_processor/metrics/chrome/scroll_flow_event_queuing_delay.sql",
         "src/trace_processor/metrics/chrome/scroll_jank.sql",
diff --git a/src/trace_processor/metrics/BUILD.gn b/src/trace_processor/metrics/BUILD.gn
index 8fa723d..710a205 100644
--- a/src/trace_processor/metrics/BUILD.gn
+++ b/src/trace_processor/metrics/BUILD.gn
@@ -44,6 +44,7 @@
   "android/android_hwui_metric.sql",
   "android/java_heap_histogram.sql",
   "android/java_heap_stats.sql",
+  "android/power_drain_in_watts.sql",
   "android/power_profile_data.sql",
   "android/process_unagg_mem_view.sql",
   "android/process_mem.sql",
@@ -54,8 +55,12 @@
   "android/process_counter_span_view.sql",
   "android/global_counter_span_view.sql",
   "android/unsymbolized_frames.sql",
+  "chrome/actual_power_by_rail_mode.sql",
   "chrome/chrome_processes.sql",
   "chrome/chrome_thread_slice_with_cpu_time.sql",
+  "chrome/cpu_time_by_rail_mode.sql",
+  "chrome/estimated_power_by_rail_mode.sql",
+  "chrome/rail_modes.sql",
   "chrome/scroll_jank.sql",
   "chrome/scroll_jank_cause.sql",
   "chrome/scroll_jank_cause_blocking_task.sql",
diff --git a/src/trace_processor/metrics/android/android_cpu_agg.sql b/src/trace_processor/metrics/android/android_cpu_agg.sql
index 521f580..d5f145e 100644
--- a/src/trace_processor/metrics/android/android_cpu_agg.sql
+++ b/src/trace_processor/metrics/android/android_cpu_agg.sql
@@ -16,6 +16,7 @@
 
 -- Create all the views used to aggregate CPU data.
 -- View with start and end ts for each cpu frequency, per cpu.
+DROP VIEW IF EXISTS cpu_freq_view;
 CREATE VIEW cpu_freq_view AS
 SELECT
   cpu,
@@ -28,5 +29,6 @@
 WHERE name = 'cpufreq';
 
 -- View that joins the cpufreq table with the slice table.
+DROP TABLE IF EXISTS cpu_freq_sched_per_thread;
 CREATE VIRTUAL TABLE cpu_freq_sched_per_thread
 USING SPAN_LEFT_JOIN(sched PARTITIONED cpu, cpu_freq_view PARTITIONED cpu);
diff --git a/src/trace_processor/metrics/android/android_proxy_power.sql b/src/trace_processor/metrics/android/android_proxy_power.sql
index 52bae9b..c356edd 100644
--- a/src/trace_processor/metrics/android/android_proxy_power.sql
+++ b/src/trace_processor/metrics/android/android_proxy_power.sql
@@ -42,6 +42,7 @@
 SELECT RUN_METRIC('android/android_cpu_agg.sql');
 SELECT RUN_METRIC('android/power_profile_data.sql');
 
+DROP VIEW IF EXISTS device;
 CREATE VIEW device AS
 WITH
   after_first_slash(str) AS (
@@ -55,6 +56,7 @@
   )
 SELECT str AS name FROM before_second_slash;
 
+DROP VIEW IF EXISTS power_view;
 CREATE VIEW power_view AS
 SELECT
   cpu_freq_view.cpu AS cpu,
@@ -70,10 +72,12 @@
 
 -- utid = 0 is a reserved value used to mark sched slices where CPU was idle.
 -- It doesn't correspond to any real thread.
+DROP VIEW IF EXISTS sched_real_threads;
 CREATE VIEW sched_real_threads AS
 SELECT *
 FROM sched
 WHERE utid != 0;
 
+DROP TABLE IF EXISTS power_per_thread;
 CREATE VIRTUAL TABLE power_per_thread
 USING SPAN_LEFT_JOIN(sched_real_threads PARTITIONED cpu, power_view PARTITIONED cpu);
diff --git a/src/trace_processor/metrics/android/power_drain_in_watts.sql b/src/trace_processor/metrics/android/power_drain_in_watts.sql
new file mode 100644
index 0000000..76bd98d
--- /dev/null
+++ b/src/trace_processor/metrics/android/power_drain_in_watts.sql
@@ -0,0 +1,73 @@
+--
+-- Copyright 2020 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.
+--
+
+-- This is a mapping from counter names on different devices
+-- to what subsystems they are measuring.
+CREATE TABLE IF NOT EXISTS power_counters (name TEXT UNIQUE, subsystem TEXT);
+
+INSERT OR IGNORE INTO power_counters
+VALUES ('power.VPH_PWR_S5C_S6C_uws', 'cpu_big'),
+  ('power.VPH_PWR_S4C_uws', 'cpu_little'),
+  ('power.VPH_PWR_S2C_S3C_uws', 'soc'),
+  ('power.VPH_PWR_OLED_uws', 'display'),
+  ('power.PPVAR_VPH_PWR_S1A_S9A_S10A_uws', 'soc'),
+  ('power.PPVAR_VPH_PWR_S2A_S3A_uws', 'cpu_big'),
+  ('power.PPVAR_VPH_PWR_S1C_uws', 'cpu_little'),
+  ('power.WCN3998_VDD13 [from PP1304_L2C]_uws', 'wifi'),
+  ('power.PPVAR_VPH_PWR_WLAN_uws', 'wifi'),
+  ('power.PPVAR_VPH_PWR_OLED_uws', 'display'),
+  ('power.PPVAR_VPH_PWR_QTM525_uws', 'cellular'),
+  ('power.PPVAR_VPH_PWR_RF_uws', 'cellular');
+
+-- Convert power counter data into table of events, where each event has
+-- start timestamp, duration and the average power drain during its duration
+-- in Watts.
+-- Note that power counters wrap around at different values on different
+-- devices. When that happens, we ignore the value before overflow, and only
+-- take into account the value after it. This underestimates the actual power
+-- drain between those counters.
+DROP VIEW IF EXISTS drain_in_watts;
+CREATE VIEW drain_in_watts AS
+SELECT name,
+  ts,
+  LEAD(ts) OVER (
+    PARTITION BY track_id
+    ORDER BY ts
+  ) - ts AS dur,
+  CASE
+    WHEN LEAD(value) OVER (
+      PARTITION BY track_id
+      ORDER BY ts
+    ) >= value THEN (
+      LEAD(value) OVER (
+        PARTITION BY track_id
+        ORDER BY ts
+      ) - value
+    )
+    ELSE LEAD(value) OVER (
+      PARTITION BY track_id
+      ORDER BY ts
+    )
+  END / (
+    LEAD(ts) OVER (
+      PARTITION BY track_id
+      ORDER BY ts
+    ) - ts
+  ) * 1e3 AS drain_w
+FROM counter
+  JOIN counter_track ON (counter.track_id = counter_track.id)
+WHERE counter_track.type = 'counter_track'
+  AND name LIKE "power.%";
diff --git a/src/trace_processor/metrics/chrome/actual_power_by_rail_mode.sql b/src/trace_processor/metrics/chrome/actual_power_by_rail_mode.sql
new file mode 100644
index 0000000..6a63e4a
--- /dev/null
+++ b/src/trace_processor/metrics/chrome/actual_power_by_rail_mode.sql
@@ -0,0 +1,50 @@
+--
+-- Copyright 2020 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 RUN_METRIC('chrome/rail_modes.sql');
+SELECT RUN_METRIC('chrome/chrome_processes.sql');
+SELECT RUN_METRIC('android/power_drain_in_watts.sql');
+
+DROP TABLE IF EXISTS real_rail_power;
+CREATE VIRTUAL TABLE real_rail_power USING SPAN_JOIN(
+    combined_overall_rail_slices,
+    drain_in_watts
+);
+
+-- Actual power usage for chrome across the RAIL mode slices contained in
+-- combined_overall_rail_slices broken down by subsystem.
+DROP VIEW IF EXISTS real_power_by_rail_mode;
+CREATE VIEW real_power_by_rail_mode AS
+SELECT s.id,
+  ts,
+  dur,
+  rail_mode,
+  subsystem,
+  joules,
+  joules / dur * 1e9 AS drain_w
+FROM (
+    SELECT id,
+      subsystem,
+      SUM(drain_w * dur / 1e9) AS joules
+    FROM real_rail_power
+      JOIN power_counters
+    WHERE real_rail_power.name = power_counters.name
+    GROUP BY id,
+      subsystem
+  ) p
+  JOIN combined_overall_rail_slices s
+WHERE s.id = p.id
+ORDER BY s.id;
diff --git a/src/trace_processor/metrics/chrome/cpu_time_by_rail_mode.sql b/src/trace_processor/metrics/chrome/cpu_time_by_rail_mode.sql
new file mode 100644
index 0000000..a57fcda
--- /dev/null
+++ b/src/trace_processor/metrics/chrome/cpu_time_by_rail_mode.sql
@@ -0,0 +1,65 @@
+--
+-- Copyright 2020 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 RUN_METRIC('chrome/rail_modes.sql');
+SELECT RUN_METRIC('chrome/chrome_processes.sql');
+
+-- CPU time slices for Chrome threads.
+DROP VIEW IF EXISTS chrome_cpu_slices;
+CREATE VIEW chrome_cpu_slices AS
+SELECT counters.id AS counter_id,
+  track_id,
+  ts,
+  lead(ts) OVER (
+    PARTITION BY track_id
+    ORDER BY ts
+  ) - ts AS dur,
+  CAST(
+    lead(value) OVER (
+      PARTITION BY track_id
+      ORDER BY ts
+    ) - value AS "INT64"
+  ) AS cpu_dur
+FROM counters,
+  (
+    SELECT thread_counter_track.id
+    FROM chrome_thread
+      JOIN thread_counter_track ON chrome_thread.utid = thread_counter_track.utid
+  ) AS t
+WHERE t.id = track_id;
+
+DROP TABLE IF EXISTS rail_cpu_time;
+CREATE VIRTUAL TABLE rail_cpu_time USING SPAN_JOIN(
+  combined_overall_rail_slices,
+  chrome_cpu_slices PARTITIONED track_id
+);
+
+-- View containing the CPU time used (across all cores) for each RAIL mode slice
+-- from combined_overall_rail_slices.
+-- This will slightly overestimate the CPU time for some RAIL mode slices as the
+-- cpu time slices don't always line up with the RAIL mode slices. However the
+-- CPU slices are small enough this makes very little difference.
+DROP VIEW IF EXISTS cpu_time_by_rail_mode;
+CREATE VIEW cpu_time_by_rail_mode AS
+SELECT s.id,
+  s.ts,
+  s.dur,
+  s.rail_mode,
+  SUM(cpu_dur) AS cpu_dur
+FROM rail_cpu_time r
+  JOIN combined_overall_rail_slices s
+WHERE r.id = s.id
+GROUP BY r.id;
diff --git a/src/trace_processor/metrics/chrome/estimated_power_by_rail_mode.sql b/src/trace_processor/metrics/chrome/estimated_power_by_rail_mode.sql
new file mode 100644
index 0000000..627e049
--- /dev/null
+++ b/src/trace_processor/metrics/chrome/estimated_power_by_rail_mode.sql
@@ -0,0 +1,65 @@
+--
+-- Copyright 2020 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 RUN_METRIC('chrome/rail_modes.sql');
+SELECT RUN_METRIC('chrome/chrome_processes.sql');
+SELECT RUN_METRIC('android/android_proxy_power.sql');
+
+-- View containing estimated power slices broken down by cpu.
+DROP VIEW IF EXISTS power_per_chrome_thread;
+CREATE VIEW power_per_chrome_thread AS
+SELECT ts,
+  dur,
+  cpu,
+  power_per_thread.utid,
+  end_state,
+  priority,
+  power_ma,
+  power_per_thread.type,
+  name AS thread_name,
+  upid,
+  is_main_thread
+FROM power_per_thread
+  JOIN chrome_thread
+WHERE power_per_thread.utid = chrome_thread.utid;
+
+DROP TABLE IF EXISTS rail_power;
+CREATE VIRTUAL TABLE rail_power USING SPAN_JOIN(
+  combined_overall_rail_slices,
+  power_per_chrome_thread
+);
+
+-- Estimated power usage for chrome across the RAIL mode slices contained in
+-- combined_overall_rail_slices.
+DROP VIEW IF EXISTS power_by_rail_mode;
+CREATE VIEW power_by_rail_mode AS
+SELECT id,
+  ts,
+  dur,
+  rail_mode,
+  mas,
+  mas / dur * 1e9 AS ma
+FROM (
+    SELECT s.id,
+      s.ts,
+      s.dur,
+      s.rail_mode,
+      SUM(r.power_ma * r.dur) / 1e9 AS mas
+    FROM rail_power r
+      JOIN combined_overall_rail_slices s
+    WHERE r.id == s.id
+    GROUP BY s.id
+  );
diff --git a/src/trace_processor/metrics/chrome/rail_modes.sql b/src/trace_processor/metrics/chrome/rail_modes.sql
new file mode 100644
index 0000000..f42d138
--- /dev/null
+++ b/src/trace_processor/metrics/chrome/rail_modes.sql
@@ -0,0 +1,105 @@
+--
+-- Copyright 2020 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.
+--
+-- Priority order for RAIL modes where response has the highest priority and
+-- idle has the lowest.
+CREATE TABLE IF NOT EXISTS rail_modes (MODE STRING UNIQUE, ordering INT);
+
+INSERT
+  OR IGNORE INTO rail_modes
+VALUES ('idle', 0),
+  ('load', 1),
+  ('animation', 2),
+  ('response', 3);
+
+-- View containing all Scheduler.RAILMode slices across all Chrome renderer
+-- processes.
+DROP VIEW IF EXISTS rail_mode_slices;
+CREATE VIEW rail_mode_slices AS
+SELECT slice.id,
+  ts,
+  CASE
+    WHEN dur == -1 THEN trace_bounds.end_ts - ts
+    ELSE dur
+  END AS dur,
+  track_id,
+  EXTRACT_ARG(slice.arg_set_id, "debug.state") AS rail_mode
+FROM trace_bounds,
+  slice
+WHERE slice.name = "Scheduler.RAILMode";
+
+-- View containing a collapsed view of rail_mode_slices where there is only one
+-- RAIL mode active at a given time. The mode is derived using the priority
+-- order in rail_modes.
+DROP VIEW IF EXISTS overall_rail_mode_slices;
+CREATE VIEW overall_rail_mode_slices AS
+SELECT s.ts,
+  s.end_ts,
+  r.rail_mode,
+  MAX(rail_modes.ordering)
+FROM (
+    SELECT ts,
+      LEAD(ts, 1, trace_bounds.end_ts) OVER (
+        ORDER BY ts
+      ) AS end_ts
+    FROM (
+        SELECT DISTINCT ts
+        FROM rail_mode_slices
+      ) start_times,
+      trace_bounds
+  ) s,
+  rail_mode_slices r,
+  rail_modes
+WHERE (
+    (
+      s.ts BETWEEN r.ts AND r.ts + r.dur
+    )
+    OR (
+      s.end_ts BETWEEN r.ts AND r.ts + r.dur
+    )
+  )
+  AND r.rail_mode == rail_modes.mode
+GROUP BY s.ts;
+
+-- Contains the same data as overall_rail_mode_slices except adjacent slices
+-- with the same RAIL mode are combined.
+CREATE TABLE IF NOT EXISTS combined_overall_rail_slices AS
+SELECT ROW_NUMBER() OVER () AS id,
+  ts,
+  end_ts - ts AS dur,
+  rail_mode
+FROM (
+    SELECT lag(l.end_ts, 1, FIRST) OVER (
+        ORDER BY l.ts
+      ) AS ts,
+      l.end_ts,
+      l.rail_mode
+    FROM (
+        SELECT ts,
+          end_ts,
+          rail_mode
+        FROM overall_rail_mode_slices s
+        WHERE NOT EXISTS (
+            SELECT NULL
+            FROM overall_rail_mode_slices s2
+            WHERE s.rail_mode = s2.rail_mode
+              AND s.end_ts = s2.ts
+          )
+      ) AS l,
+      (
+        SELECT min(ts) AS FIRST
+        FROM overall_rail_mode_slices
+      )
+  );