metrics: Move Wattson metric to stdlib for reusability

Move Wattson power estimate metric to Perfetto stdlib so that the code
can be re-used.

There are many use-cases for getting estimates from a trace, and the
only difference is the particular time window of interest. Leave the
time window calculation to be metric SQL file specific, but move the
generic estimation and hierarchy breakdown to stdlib.

Test: tools/diff_test_trace_processor.py out/linux/trace_processor_shell --name-filter '.*wattson.*'
Bug: 352627297
Change-Id: I2d974faa4b65d5b3c28fc6706805a3019ec76ff0
Signed-off-by: Samuel Wu <wusamuel@google.com>
diff --git a/Android.bp b/Android.bp
index 021e268..1144fdf 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12959,6 +12959,7 @@
         "src/trace_processor/metrics/sql/android/sysui_update_notif_on_ui_mode_changed_metric.sql",
         "src/trace_processor/metrics/sql/android/unsymbolized_frames.sql",
         "src/trace_processor/metrics/sql/android/wattson_app_startup.sql",
+        "src/trace_processor/metrics/sql/android/wattson_rail_relations.sql",
         "src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql",
         "src/trace_processor/metrics/sql/chrome/actual_power_by_rail_mode.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_args_class_names.sql",
diff --git a/BUILD b/BUILD
index 4ee563d..844d32a 100644
--- a/BUILD
+++ b/BUILD
@@ -2193,6 +2193,7 @@
         "src/trace_processor/metrics/sql/android/sysui_update_notif_on_ui_mode_changed_metric.sql",
         "src/trace_processor/metrics/sql/android/unsymbolized_frames.sql",
         "src/trace_processor/metrics/sql/android/wattson_app_startup.sql",
+        "src/trace_processor/metrics/sql/android/wattson_rail_relations.sql",
     ],
 )
 
diff --git a/src/trace_processor/metrics/sql/android/BUILD.gn b/src/trace_processor/metrics/sql/android/BUILD.gn
index 69b5915..f23eb47 100644
--- a/src/trace_processor/metrics/sql/android/BUILD.gn
+++ b/src/trace_processor/metrics/sql/android/BUILD.gn
@@ -143,5 +143,6 @@
     "sysui_update_notif_on_ui_mode_changed_metric.sql",
     "unsymbolized_frames.sql",
     "wattson_app_startup.sql",
+    "wattson_rail_relations.sql",
   ]
 }
diff --git a/src/trace_processor/metrics/sql/android/wattson_app_startup.sql b/src/trace_processor/metrics/sql/android/wattson_app_startup.sql
index 7a8df8b..6dc970b 100644
--- a/src/trace_processor/metrics/sql/android/wattson_app_startup.sql
+++ b/src/trace_processor/metrics/sql/android/wattson_app_startup.sql
@@ -14,8 +14,6 @@
 -- limitations under the License.
 
 INCLUDE PERFETTO MODULE android.startup.startups;
-INCLUDE PERFETTO MODULE wattson.device_infos;
-INCLUDE PERFETTO MODULE wattson.curves.ungrouped;
 
 DROP VIEW IF EXISTS _app_startup_window;
 CREATE PERFETTO VIEW _app_startup_window AS
@@ -25,183 +23,10 @@
   startup_id
 FROM android_startups;
 
-DROP TABLE IF EXISTS _windowed_wattson;
-CREATE VIRTUAL TABLE _windowed_wattson
-USING
-  SPAN_JOIN(_app_startup_window, _system_state_curves);
-
-DROP VIEW IF EXISTS _wattson_base_components_pws;
-CREATE PERFETTO VIEW _wattson_base_components_pws AS
-SELECT
-  -- *_curve is in units of mW, so multiplying by ns gives pWs
-  SUM(static_curve * dur) as static_pws,
-  SUM(cpu0_curve * dur) as cpu0_pws,
-  SUM(cpu1_curve * dur) as cpu1_pws,
-  SUM(cpu2_curve * dur) as cpu2_pws,
-  SUM(cpu3_curve * dur) as cpu3_pws,
-  SUM(cpu4_curve * dur) as cpu4_pws,
-  SUM(cpu5_curve * dur) as cpu5_pws,
-  SUM(cpu6_curve * dur) as cpu6_pws,
-  SUM(cpu7_curve * dur) as cpu7_pws,
-  -- L3/SCU interconnect is already scaled by 10^6 in the L3 LUTs, so when
-  -- converting to pWs need to scale by 10^3
-  SUM(l3_hit_value + l3_miss_value) * 1000 as l3_pws,
-  SUM(dur) as total_dur,
-  startup_id
-FROM _windowed_wattson
-GROUP BY startup_id;
-
-DROP VIEW IF EXISTS _wattson_cpu_rail_total;
-CREATE PERFETTO VIEW _wattson_cpu_rail_total AS
-SELECT
-  startup_id,
-  total_dur,
-  'cpu_subsystem' as name,
-  (
-    cpu0_pws + cpu1_pws + cpu2_pws + cpu3_pws +
-    cpu4_pws + cpu5_pws + cpu6_pws + cpu7_pws +
-    static_pws + l3_pws
-  ) / total_dur as estimate_mw
-FROM _wattson_base_components_pws
-GROUP BY startup_id;
-
-DROP VIEW IF EXISTS _wattson_cpu_subsubrail_grouped;
-CREATE PERFETTO VIEW _wattson_cpu_subsubrail_grouped AS
-SELECT
-  startup_id,
-  'cpu0' as name,
-  map.policy,
-  cpu0_pws / total_dur as estimate_mw
-FROM _wattson_base_components_pws
-CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 0
-GROUP BY startup_id
-UNION ALL
-SELECT
-  startup_id,
-  'cpu1' as name,
-  map.policy,
-  cpu1_pws / total_dur as estimate_mw
-FROM _wattson_base_components_pws
-CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 1
-GROUP BY startup_id
-UNION ALL
-SELECT
-  startup_id,
-  'cpu2' as name,
-  map.policy,
-  cpu2_pws / total_dur as estimate_mw
-FROM _wattson_base_components_pws
-CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 2
-GROUP BY startup_id
-UNION ALL
-SELECT
-  startup_id,
-  'cpu3' as name,
-  map.policy,
-  cpu3_pws / total_dur as estimate_mw
-FROM _wattson_base_components_pws
-CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 3
-GROUP BY startup_id
-UNION ALL
-SELECT
-  startup_id,
-  'cpu4' as name,
-  map.policy,
-  cpu4_pws / total_dur as estimate_mw
-FROM _wattson_base_components_pws
-CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 4
-GROUP BY startup_id
-UNION ALL
-SELECT
-  startup_id,
-  'cpu5' as name,
-  map.policy,
-  cpu5_pws / total_dur as estimate_mw
-FROM _wattson_base_components_pws
-CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 5
-GROUP BY startup_id
-UNION ALL
-SELECT
-  startup_id,
-  'cpu6' as name,
-  map.policy,
-  cpu6_pws / total_dur as estimate_mw
-FROM _wattson_base_components_pws
-CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 6
-GROUP BY startup_id
-UNION ALL
-SELECT
-  startup_id,
-  'cpu7' as name,
-  map.policy,
-  cpu7_pws / total_dur as estimate_mw
-FROM _wattson_base_components_pws
-CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 7
-GROUP BY startup_id;
-
-DROP VIEW IF EXISTS _wattson_cpu_subrail_grouped;
-CREATE PERFETTO VIEW _wattson_cpu_subrail_grouped AS
-SELECT
-  startup_id,
-  NULL as policy,
-  'DSU_SCU' as name,
-  (static_pws + l3_pws) / total_dur as estimate_mw
-FROM _wattson_base_components_pws GROUP BY startup_id
-UNION ALL
-SELECT
-  startup_id,
-  policy,
-  CONCAT('policy', policy) as name,
-  SUM(estimate_mw) as estimate_mw
-FROM _wattson_cpu_subsubrail_grouped GROUP BY startup_id, policy;
-
--- Grouped by CPUs, the smallest building block available
-DROP VIEW IF EXISTS _cpu_subsubrail_estimate_per_startup_proto;
-CREATE PERFETTO VIEW _cpu_subsubrail_estimate_per_startup_proto AS
-SELECT
-  startup_id,
-  policy,
-  RepeatedField(
-    AndroidWattsonRailEstimate(
-      'name', name,
-      'estimate_mw', estimate_mw
-    )
-  ) AS proto
-FROM _wattson_cpu_subsubrail_grouped
-GROUP BY startup_id, policy;
-
--- Grouped by CPU policy
-DROP VIEW IF EXISTS _cpu_subrail_estimate_per_startup_proto;
-CREATE PERFETTO VIEW _cpu_subrail_estimate_per_startup_proto AS
-SELECT
-  startup_id,
-  RepeatedField(
-    AndroidWattsonRailEstimate(
-      'name', name,
-      'estimate_mw', estimate_mw,
-      'rail', _cpu_subsubrail_estimate_per_startup_proto.proto
-    )
-  ) AS proto
-FROM _wattson_cpu_subrail_grouped
--- Some subrails will not have any subsubrails, so LEFT JOIN
-LEFT JOIN _cpu_subsubrail_estimate_per_startup_proto USING (startup_id, policy)
-GROUP BY startup_id;
-
--- Grouped into single entry for entirety of CPU system
-DROP VIEW IF EXISTS _cpu_rail_estimate_per_startup_proto;
-CREATE PERFETTO VIEW _cpu_rail_estimate_per_startup_proto AS
-SELECT
-  startup_id,
-  RepeatedField(
-    AndroidWattsonRailEstimate(
-      'name', name,
-      'estimate_mw', estimate_mw,
-      'rail', _cpu_subrail_estimate_per_startup_proto.proto
-    )
-  ) AS proto
-FROM _wattson_cpu_rail_total
-JOIN _cpu_subrail_estimate_per_startup_proto USING (startup_id)
-GROUP BY startup_id;
+SELECT RUN_METRIC(
+  'android/wattson_rail_relations.sql',
+  'window_table', '_app_startup_window'
+);
 
 DROP VIEW IF EXISTS wattson_app_startup_output;
 CREATE PERFETTO VIEW wattson_app_startup_output AS
diff --git a/src/trace_processor/metrics/sql/android/wattson_rail_relations.sql b/src/trace_processor/metrics/sql/android/wattson_rail_relations.sql
new file mode 100644
index 0000000..539e3d9
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/wattson_rail_relations.sql
@@ -0,0 +1,201 @@
+
+-- 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.
+
+-- This file established the tables that define the relationships between rails
+-- and subrails as well as the hierarchical power estimates of each rail
+
+INCLUDE PERFETTO MODULE wattson.curves.ungrouped;
+
+-- Take only the Wattson estimations that are in the window of interest
+DROP TABLE IF EXISTS _windowed_wattson;
+CREATE VIRTUAL TABLE _windowed_wattson
+USING
+  SPAN_JOIN({{window_table}}, _system_state_mw);
+
+-- The most basic rail components that form the "building blocks" from which all
+-- other rails and components are derived
+DROP VIEW IF EXISTS _wattson_base_components_pws;
+CREATE PERFETTO VIEW _wattson_base_components_pws AS
+SELECT
+  -- *_curve is in units of mW, so multiplying by ns gives pWs
+  SUM(dur * cpu0_mw) as cpu0_pws,
+  SUM(dur * cpu1_mw) as cpu1_pws,
+  SUM(dur * cpu2_mw) as cpu2_pws,
+  SUM(dur * cpu3_mw) as cpu3_pws,
+  SUM(dur * cpu4_mw) as cpu4_pws,
+  SUM(dur * cpu5_mw) as cpu5_pws,
+  SUM(dur * cpu6_mw) as cpu6_pws,
+  SUM(dur * cpu7_mw) as cpu7_pws,
+  SUM(dur * dsu_scu_mw) as dsu_scu_pws,
+  SUM(dur) as total_dur,
+  startup_id
+FROM _windowed_wattson
+GROUP BY startup_id;
+
+-- Root node that has all possible components for the CPUSS added together
+DROP VIEW IF EXISTS _wattson_cpu_rail_total;
+CREATE PERFETTO VIEW _wattson_cpu_rail_total AS
+SELECT
+  startup_id,
+  total_dur,
+  'cpu_subsystem' as name,
+  (
+    cpu0_pws + cpu1_pws + cpu2_pws + cpu3_pws +
+    cpu4_pws + cpu5_pws + cpu6_pws + cpu7_pws +
+    dsu_scu_pws
+  ) / total_dur as estimate_mw
+FROM _wattson_base_components_pws
+GROUP BY startup_id;
+
+-- Sub-sub-rail, meaning two levels down from the CPU root node
+DROP VIEW IF EXISTS _wattson_cpu_subsubrail_grouped;
+CREATE PERFETTO VIEW _wattson_cpu_subsubrail_grouped AS
+SELECT
+  startup_id,
+  'cpu0' as name,
+  map.policy,
+  cpu0_pws / total_dur as estimate_mw
+FROM _wattson_base_components_pws
+CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 0
+GROUP BY startup_id
+UNION ALL
+SELECT
+  startup_id,
+  'cpu1' as name,
+  map.policy,
+  cpu1_pws / total_dur as estimate_mw
+FROM _wattson_base_components_pws
+CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 1
+GROUP BY startup_id
+UNION ALL
+SELECT
+  startup_id,
+  'cpu2' as name,
+  map.policy,
+  cpu2_pws / total_dur as estimate_mw
+FROM _wattson_base_components_pws
+CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 2
+GROUP BY startup_id
+UNION ALL
+SELECT
+  startup_id,
+  'cpu3' as name,
+  map.policy,
+  cpu3_pws / total_dur as estimate_mw
+FROM _wattson_base_components_pws
+CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 3
+GROUP BY startup_id
+UNION ALL
+SELECT
+  startup_id,
+  'cpu4' as name,
+  map.policy,
+  cpu4_pws / total_dur as estimate_mw
+FROM _wattson_base_components_pws
+CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 4
+GROUP BY startup_id
+UNION ALL
+SELECT
+  startup_id,
+  'cpu5' as name,
+  map.policy,
+  cpu5_pws / total_dur as estimate_mw
+FROM _wattson_base_components_pws
+CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 5
+GROUP BY startup_id
+UNION ALL
+SELECT
+  startup_id,
+  'cpu6' as name,
+  map.policy,
+  cpu6_pws / total_dur as estimate_mw
+FROM _wattson_base_components_pws
+CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 6
+GROUP BY startup_id
+UNION ALL
+SELECT
+  startup_id,
+  'cpu7' as name,
+  map.policy,
+  cpu7_pws / total_dur as estimate_mw
+FROM _wattson_base_components_pws
+CROSS JOIN _dev_cpu_policy_map as map WHERE map.cpu = 7
+GROUP BY startup_id;
+
+-- Sub-rail, meaning one level down from the CPU root node
+DROP VIEW IF EXISTS _wattson_cpu_subrail_grouped;
+CREATE PERFETTO VIEW _wattson_cpu_subrail_grouped AS
+SELECT
+  startup_id,
+  NULL as policy,
+  'DSU_SCU' as name,
+  dsu_scu_pws / total_dur as estimate_mw
+FROM _wattson_base_components_pws GROUP BY startup_id
+UNION ALL
+SELECT
+  startup_id,
+  policy,
+  CONCAT('policy', policy) as name,
+  SUM(estimate_mw) as estimate_mw
+FROM _wattson_cpu_subsubrail_grouped GROUP BY startup_id, policy;
+
+-- Grouped by CPUs, the smallest building block available
+DROP VIEW IF EXISTS _cpu_subsubrail_estimate_per_startup_proto;
+CREATE PERFETTO VIEW _cpu_subsubrail_estimate_per_startup_proto AS
+SELECT
+  startup_id,
+  policy,
+  RepeatedField(
+    AndroidWattsonRailEstimate(
+      'name', name,
+      'estimate_mw', estimate_mw
+    )
+  ) AS proto
+FROM _wattson_cpu_subsubrail_grouped
+GROUP BY startup_id, policy;
+
+-- Grouped by CPU policy
+DROP VIEW IF EXISTS _cpu_subrail_estimate_per_startup_proto;
+CREATE PERFETTO VIEW _cpu_subrail_estimate_per_startup_proto AS
+SELECT
+  startup_id,
+  RepeatedField(
+    AndroidWattsonRailEstimate(
+      'name', name,
+      'estimate_mw', estimate_mw,
+      'rail', _cpu_subsubrail_estimate_per_startup_proto.proto
+    )
+  ) AS proto
+FROM _wattson_cpu_subrail_grouped
+-- Some subrails will not have any subsubrails, so LEFT JOIN
+LEFT JOIN _cpu_subsubrail_estimate_per_startup_proto USING (startup_id, policy)
+GROUP BY startup_id;
+
+-- Grouped into single entry for entirety of CPU system
+DROP VIEW IF EXISTS _cpu_rail_estimate_per_startup_proto;
+CREATE PERFETTO VIEW _cpu_rail_estimate_per_startup_proto AS
+SELECT
+  startup_id,
+  RepeatedField(
+    AndroidWattsonRailEstimate(
+      'name', name,
+      'estimate_mw', estimate_mw,
+      'rail', _cpu_subrail_estimate_per_startup_proto.proto
+    )
+  ) AS proto
+FROM _wattson_cpu_rail_total
+JOIN _cpu_subrail_estimate_per_startup_proto USING (startup_id)
+GROUP BY startup_id;
+
diff --git a/test/trace_processor/diff_tests/metrics/android/tests.py b/test/trace_processor/diff_tests/metrics/android/tests.py
index 499b248..7924255 100644
--- a/test/trace_processor/diff_tests/metrics/android/tests.py
+++ b/test/trace_processor/diff_tests/metrics/android/tests.py
@@ -375,10 +375,10 @@
             period_dur: 385136434
             rail {
               name: "cpu_subsystem"
-              estimate_mw: 4568.159180
+              estimate_mw: 4567.958180
               rail {
                 name: "DSU_SCU"
-                estimate_mw: 1142.600708
+                estimate_mw: 1142.399708
               }
               rail {
                 name: "policy0"