-- Copyright 2022 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. -- Find causes for CPUs powering up. -- -- The scripts below analyse traces with the following tracing options -- enabled: -- -- - Linux kernel: --- "power/*", "sched/*", "task/*", -- - Chromium: -- "toplevel", "toplevel.flow". -- Noteworthy tables: -- -- chrome_cpu_power_first_toplevel_slice_after_powerup :: The top-level -- slices that ran after a CPU power-up. -- The CPU power transitions in the trace. -- -- @column ts The timestamp at the start of the slice. -- @column dur The duration of the slice. -- @column cpu The CPU on which the transition occurred -- @column power_state The power state that the CPU was in at time 'ts' for -- duration 'dur'. -- @column previous_power_state The power state that the CPU was previously in. -- @column powerup_id A unique ID for the CPU power-up. -- -- Power states are encoded as non-negative integers, with zero representing -- full-power operation and positive values representing increasingly deep -- sleep states. -- -- On ARM systems, power state 1 represents the WFI (Wait For Interrupt) sleep -- state that the CPU enters while idle. CREATE VIEW chrome_cpu_power_slice AS WITH cpu_power_states AS ( SELECT c.id AS id, cct.cpu AS cpu, c.ts, -- Encode the 'value' field as a power state. CAST((CASE c.value WHEN 4294967295 THEN 0 ELSE c.value + 1 END) AS INT) AS power_state FROM counter AS c JOIN cpu_counter_track AS cct ON c.track_id = cct.id WHERE cct.name = 'cpuidle' ) SELECT * FROM ( SELECT ts, LEAD(ts) OVER (PARTITION BY cpu ORDER BY ts ASC) - ts AS dur, cpu, power_state, LAG(power_state) OVER (PARTITION BY cpu ORDER BY ts ASC) AS previous_power_state, id AS powerup_id FROM cpu_power_states ) WHERE dur IS NOT NULL AND previous_power_state IS NOT NULL AND power_state = 0 -- Track full-power states. AND power_state != previous_power_state -- Skip missing spans. ORDER BY ts ASC; -- We do not want scheduler slices with utid = 0 (the 'swapper' kernel thread). CREATE VIEW internal_cpu_power_valid_sched_slice AS SELECT * FROM sched_slice WHERE utid != 0; -- Join scheduler slices with the spans with CPU power slices. -- -- There multiple scheduler slices could fall into one CPU power slice. -- --- CPU Power: -- |----------------------------|....................|---------| -- A B C D -- Scheduler slices on that CPU: -- |-----T1-----| |....T2....| |---T3--| -- E F G H I J -- -- Here threads T1 and T2 executed in CPU power slice [A,B]. The -- time between F and G represents time between threads in the kernel. CREATE VIRTUAL TABLE internal_cpu_power_and_sched_slice USING SPAN_JOIN(chrome_cpu_power_slice PARTITIONED cpu, internal_cpu_power_valid_sched_slice PARTITIONED cpu); -- The Linux scheduler slices that executed immediately after a -- CPU power up. -- -- @column ts The timestamp at the start of the slice. -- @column dur The duration of the slice. -- @column cpu The cpu on which the slice executed. -- @column sched_id Id for the sched_slice table. -- @column utid Unique id for the thread that ran within the slice. -- @column previous_power_state The CPU's power state before this slice. CREATE TABLE chrome_cpu_power_first_sched_slice_after_powerup AS SELECT ts, dur, cpu, id AS sched_id, utid, previous_power_state, powerup_id FROM internal_cpu_power_and_sched_slice WHERE power_state = 0 -- Power-ups only. GROUP BY cpu, powerup_id HAVING ts = MIN(ts) -- There will only be one MIN sched slice -- per CPU power up. ORDER BY ts ASC; -- A view joining thread tracks and top-level slices. -- -- This view is intended to be intersected by time with the scheduler -- slices scheduled after a CPU power up. -- -- utid Thread unique id. -- slice_id The slice_id for the top-level slice. -- ts Starting timestamp for the slice. -- dur The duration for the slice. CREATE VIEW internal_cpu_power_thread_and_toplevel_slice AS SELECT t.utid AS utid, s.id AS slice_id, s.ts, s.dur FROM slice AS s JOIN thread_track AS t ON s.track_id = t.id WHERE s.depth = 0 -- Top-level slices only. ORDER BY ts ASC; -- A table holding the slices that executed within the scheduler -- slice that ran on a CPU immediately after power-up. -- -- @column ts Timestamp of the resulting slice -- @column dur Duration of the slice. -- @column cpu The CPU the sched slice ran on. -- @column utid Unique thread id for the slice. -- @column sched_id 'id' field from the sched_slice table. -- @column type From the sched_slice table, always 'sched_slice'. -- @column end_state The ending state for the sched_slice -- @column priority The kernel thread priority -- @column slice_id Id of the top-level slice for this (sched) slice. CREATE VIRTUAL TABLE chrome_cpu_power_post_powerup_slice USING SPAN_JOIN(chrome_cpu_power_first_sched_slice_after_powerup PARTITIONED utid, internal_cpu_power_thread_and_toplevel_slice PARTITIONED utid); -- The first top-level slice that ran after a CPU power-up. -- -- @column slice_id ID of the slice in the slice table. -- @column previous_power_state The power state of the CPU prior to power-up. CREATE VIEW chrome_cpu_power_first_toplevel_slice_after_powerup AS SELECT slice_id, previous_power_state FROM chrome_cpu_power_post_powerup_slice GROUP BY cpu, powerup_id HAVING ts = MIN(ts) ORDER BY ts ASC;