| -- Copyright 2023 The Chromium Authors |
| -- Use of this source code is governed by a BSD-style license that can be |
| -- found in the LICENSE file. |
| |
| -- 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. |
| -- 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 PERFETTO VIEW chrome_cpu_power_slice( |
| -- The timestamp at the start of the slice. |
| ts INT, |
| -- The duration of the slice. |
| dur INT, |
| -- The CPU on which the transition occurred |
| cpu INT, |
| -- The power state that the CPU was in at time 'ts' for duration 'dur'. |
| power_state INT, |
| -- The power state that the CPU was previously in. |
| previous_power_state INT, |
| -- A unique ID for the CPU power-up. |
| powerup_id INT |
| ) 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 PERFETTO 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 <cpu active> B <cpu idling> 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. |
| CREATE PERFETTO TABLE chrome_cpu_power_first_sched_slice_after_powerup( |
| -- The timestamp at the start of the slice. |
| ts INT, |
| -- The duration of the slice. |
| dur INT, |
| -- The cpu on which the slice executed. |
| cpu INT, |
| -- Id for the sched_slice table. |
| sched_id INT, |
| -- Unique id for the thread that ran within the slice. |
| utid INT, |
| -- The CPU's power state before this slice. |
| previous_power_state INT, |
| -- A unique ID for the CPU power-up. |
| powerup_id INT |
| ) 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 PERFETTO 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. |
| CREATE PERFETTO VIEW chrome_cpu_power_first_toplevel_slice_after_powerup( |
| -- ID of the slice in the slice table. |
| slice_id INT, |
| -- The power state of the CPU prior to power-up. |
| previous_power_state INT |
| ) 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; |