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
+ """))