1// Copyright (C) 2024 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {Trace} from '../../public/trace'; 16import {PerfettoPlugin} from '../../public/plugin'; 17import {CounterOptions} from '../../components/tracks/base_counter_track'; 18import {TrackNode} from '../../public/workspace'; 19import {createQueryCounterTrack} from '../../components/tracks/query_counter_track'; 20import StandardGroupsPlugin from '../dev.perfetto.StandardGroups'; 21import {NUM, STR} from '../../trace_processor/query_result'; 22 23export default class implements PerfettoPlugin { 24 static readonly id = 'dev.perfetto.CpuidleTimeInState'; 25 static readonly dependencies = [StandardGroupsPlugin]; 26 27 private async addCounterTrack( 28 ctx: Trace, 29 name: string, 30 query: string, 31 group: TrackNode, 32 options?: Partial<CounterOptions>, 33 ) { 34 const uri = `/cpuidle_time_in_state_${name}`; 35 const track = await createQueryCounterTrack({ 36 trace: ctx, 37 uri, 38 data: { 39 sqlSource: query, 40 columns: ['ts', 'value'], 41 }, 42 columns: {ts: 'ts', value: 'value'}, 43 options, 44 }); 45 ctx.tracks.registerTrack({ 46 uri, 47 title: name, 48 track, 49 }); 50 const node = new TrackNode({uri, title: name}); 51 group.addChildInOrder(node); 52 } 53 54 async addIdleStateTrack( 55 ctx: Trace, 56 state: string, 57 group: TrackNode, 58 ): Promise<void> { 59 await this.addCounterTrack( 60 ctx, 61 `cpuidle.${state}`, 62 ` 63 select 64 ts, 65 idle_percentage as value 66 from linux_cpu_idle_time_in_state_counters 67 where state = '${state}' 68 `, 69 group, 70 {unit: 'percent', yOverrideMaximum: 100, yOverrideMinimum: 0}, 71 ); 72 } 73 74 async addPerCpuIdleStateTrack( 75 ctx: Trace, 76 state: string, 77 cpu: number, 78 group: TrackNode, 79 ): Promise<void> { 80 await this.addCounterTrack( 81 ctx, 82 `cpuidle.cpu${cpu}.${state} Residency`, 83 ` 84 select 85 ts, 86 idle_percentage as value 87 from linux_per_cpu_idle_time_in_state_counters 88 where state = '${state}' AND cpu = ${cpu} 89 `, 90 group, 91 {unit: 'percent', yOverrideMaximum: 100, yOverrideMinimum: 0}, 92 ); 93 } 94 95 async onTraceLoad(ctx: Trace): Promise<void> { 96 const group = new TrackNode({ 97 title: 'CPU Idle Time In State', 98 isSummary: true, 99 }); 100 101 const e = ctx.engine; 102 await e.query(`INCLUDE PERFETTO MODULE linux.cpu.idle_time_in_state;`); 103 const states = await e.query( 104 `select distinct state from linux_cpu_idle_time_in_state_counters`, 105 ); 106 const it = states.iter({state: STR}); 107 for (; it.valid(); it.next()) { 108 await this.addIdleStateTrack(ctx, it.state, group); 109 } 110 111 if (group.hasChildren) { 112 const cpuGroup = ctx.plugins 113 .getPlugin(StandardGroupsPlugin) 114 .getOrCreateStandardGroup(ctx.workspace, 'CPU'); 115 cpuGroup.addChildInOrder(group); 116 } 117 118 const perCpuGroup = new TrackNode({ 119 title: 'CPU Idle Per Cpu Time In State', 120 isSummary: true, 121 }); 122 123 const perCpuStates = await e.query( 124 `select distinct state, cpu from linux_per_cpu_idle_time_in_state_counters`, 125 ); 126 const pIt = perCpuStates.iter({state: STR, cpu: NUM}); 127 128 for (; pIt.valid(); pIt.next()) { 129 await this.addPerCpuIdleStateTrack(ctx, pIt.state, pIt.cpu, perCpuGroup); 130 } 131 132 if (perCpuGroup.hasChildren) { 133 const cpuGroup = ctx.plugins 134 .getPlugin(StandardGroupsPlugin) 135 .getOrCreateStandardGroup(ctx.workspace, 'CPU'); 136 cpuGroup.addChildInOrder(perCpuGroup); 137 } 138 } 139} 140