trace_processor: add linux cpuidle time in state calculation

This counter tracks the percentage of time spent in each state over time.
The module also surfaces the:
- total residency of each state, and
- (time_slice) time spent in all states
which are used to calculate the percentage.

Bug: 337135369
Test: diff test

Change-Id: I74ac58bd74939c5e129c4e810c16f6ba80546adb
diff --git a/Android.bp b/Android.bp
index e5c5c4c..fc6184b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -13320,6 +13320,7 @@
         "src/trace_processor/perfetto_sql/stdlib/linux/cpu/frequency.sql",
         "src/trace_processor/perfetto_sql/stdlib/linux/cpu/idle.sql",
         "src/trace_processor/perfetto_sql/stdlib/linux/cpu/idle_stats.sql",
+        "src/trace_processor/perfetto_sql/stdlib/linux/cpu/idle_time_in_state.sql",
         "src/trace_processor/perfetto_sql/stdlib/linux/cpu/utilization/general.sql",
         "src/trace_processor/perfetto_sql/stdlib/linux/cpu/utilization/process.sql",
         "src/trace_processor/perfetto_sql/stdlib/linux/cpu/utilization/system.sql",
diff --git a/BUILD b/BUILD
index 5c2393c..a336835 100644
--- a/BUILD
+++ b/BUILD
@@ -2753,6 +2753,7 @@
         "src/trace_processor/perfetto_sql/stdlib/linux/cpu/frequency.sql",
         "src/trace_processor/perfetto_sql/stdlib/linux/cpu/idle.sql",
         "src/trace_processor/perfetto_sql/stdlib/linux/cpu/idle_stats.sql",
+        "src/trace_processor/perfetto_sql/stdlib/linux/cpu/idle_time_in_state.sql",
     ],
 )
 
diff --git a/src/trace_processor/perfetto_sql/stdlib/linux/cpu/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/linux/cpu/BUILD.gn
index 42654eb..60d462d 100644
--- a/src/trace_processor/perfetto_sql/stdlib/linux/cpu/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/linux/cpu/BUILD.gn
@@ -19,6 +19,7 @@
     "frequency.sql",
     "idle.sql",
     "idle_stats.sql",
+    "idle_time_in_state.sql",
   ]
   deps = [ "utilization" ]
 }
diff --git a/src/trace_processor/perfetto_sql/stdlib/linux/cpu/idle_time_in_state.sql b/src/trace_processor/perfetto_sql/stdlib/linux/cpu/idle_time_in_state.sql
new file mode 100644
index 0000000..8bff4b3
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/linux/cpu/idle_time_in_state.sql
@@ -0,0 +1,74 @@
+--
+-- 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.
+INCLUDE PERFETTO MODULE time.conversion;
+
+-- Counter information for sysfs cpuidle states.
+-- Tracks the percentage of time spent in each state between two timestamps, by
+-- dividing the incremental time spent in one state, by time all CPUS spent in
+-- any state.
+CREATE PERFETTO TABLE cpu_idle_time_in_state_counters(
+  -- Timestamp.
+  ts LONG,
+  -- State name.
+  state_name STRING,
+  -- Percentage of time all CPUS spent in this state.
+  idle_percentage DOUBLE,
+  -- Incremental time spent in this state (residency), in microseconds.
+  total_residency DOUBLE,
+  -- Time all CPUS spent in any state, in microseconds.
+  time_slice INT
+) AS
+WITH residency_deltas AS (
+  SELECT
+  ts,
+  c.name as state_name,
+  value - (LAG(value) OVER (PARTITION BY c.name, cct.cpu ORDER BY ts)) as delta
+  FROM counters c
+  JOIN cpu_counter_track cct on c.track_id=cct.id
+  WHERE c.name GLOB 'cpuidle.*'
+),
+total_residency_calc AS (
+SELECT
+  ts,
+  state_name,
+  sum(delta) as total_residency,
+  -- Perfetto timestamp is in nanoseconds whereas sysfs cpuidle time
+  -- is in microseconds.
+  (
+    (SELECT count(distinct cpu) from cpu_counter_track) *
+    (time_to_us(ts - LAG(ts,1) over (partition by state_name order by ts)))
+  )  as time_slice
+  FROM residency_deltas
+GROUP BY ts, state_name
+)
+SELECT
+  ts,
+  state_name,
+  MIN(100, (total_residency / time_slice) * 100) as idle_percentage,
+  total_residency,
+  time_slice
+FROM total_residency_calc
+WHERE time_slice IS NOT NULL
+UNION ALL
+-- Calculate c0 state by subtracting all other states from total time.
+SELECT
+  ts,
+  'cpuidle.C0' as state_name,
+  (MAX(0,time_slice - SUM(total_residency)) / time_slice) * 100 AS idle_percentage,
+  time_slice - SUM(total_residency),
+  time_slice
+FROM total_residency_calc
+WHERE time_slice IS NOT NULL
+GROUP BY ts;
diff --git a/test/trace_processor/diff_tests/stdlib/linux/cpu.py b/test/trace_processor/diff_tests/stdlib/linux/cpu.py
index 6dfe8bd..5251151 100644
--- a/test/trace_processor/diff_tests/stdlib/linux/cpu.py
+++ b/test/trace_processor/diff_tests/stdlib/linux/cpu.py
@@ -323,3 +323,58 @@
          "cpu","state","count","dur","avg_dur","idle_percent"
          0,2,2,2000000,1000000,40.000000
          """))
+
+  def test_linux_cpu_idle_time_in_state(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          sys_stats {
+            cpuidle_state {
+              cpu_id: 0
+              cpuidle_state_entry {
+                state: "C8"
+                duration_us: 1000000
+              }
+            }
+          }
+          timestamp: 200000000000
+          trusted_packet_sequence_id: 2
+        }
+        packet {
+          sys_stats {
+            cpuidle_state {
+              cpu_id: 0
+              cpuidle_state_entry {
+                state: "C8"
+                duration_us: 1000100
+              }
+            }
+          }
+          timestamp: 200001000000
+          trusted_packet_sequence_id: 2
+        }
+        packet {
+          sys_stats {
+            cpuidle_state {
+              cpu_id: 0
+              cpuidle_state_entry {
+                state: "C8"
+                duration_us: 1000200
+              }
+            }
+          }
+          timestamp: 200002000000
+          trusted_packet_sequence_id: 2
+        }
+         """),
+        query="""
+         INCLUDE PERFETTO MODULE linux.cpu.idle_time_in_state;
+         SELECT * FROM cpu_idle_time_in_state_counters;
+         """,
+        out=Csv("""
+         "ts","state_name","idle_percentage","total_residency","time_slice"
+          200001000000,"cpuidle.C8",10.000000,100.000000,1000
+          200002000000,"cpuidle.C8",10.000000,100.000000,1000
+          200001000000,"cpuidle.C0",90.000000,900.000000,1000
+          200002000000,"cpuidle.C0",90.000000,900.000000,1000
+         """))