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 { 16 BaseCounterTrack, 17 CounterOptions, 18} from '../../components/tracks/base_counter_track'; 19import {Trace} from '../../public/trace'; 20import {PerfettoPlugin} from '../../public/plugin'; 21import {CPUSS_ESTIMATE_TRACK_KIND} from '../../public/track_kinds'; 22import {TrackNode} from '../../public/workspace'; 23import {WattsonEstimateSelectionAggregator} from './estimate_aggregator'; 24import {WattsonPackageSelectionAggregator} from './package_aggregator'; 25import {WattsonProcessSelectionAggregator} from './process_aggregator'; 26import {WattsonThreadSelectionAggregator} from './thread_aggregator'; 27import {Engine} from '../../trace_processor/engine'; 28import {NUM} from '../../trace_processor/query_result'; 29import {createAggregationToTabAdaptor} from '../../components/aggregation_adapter'; 30 31export default class implements PerfettoPlugin { 32 static readonly id = `org.kernel.Wattson`; 33 34 async onTraceLoad(ctx: Trace): Promise<void> { 35 // Short circuit if Wattson is not supported for this Perfetto trace 36 if (!(await hasWattsonSupport(ctx.engine))) return; 37 38 const group = new TrackNode({title: 'Wattson', isSummary: true}); 39 ctx.workspace.addChildInOrder(group); 40 41 // ctx.traceInfo.cpus contains all cpus seen from all events. Filter the set 42 // if it's seen in sched slices. 43 const queryRes = await ctx.engine.query( 44 `select distinct ucpu from sched order by ucpu;`, 45 ); 46 const ucpus = new Set<number>(); 47 for (const it = queryRes.iter({ucpu: NUM}); it.valid(); it.next()) { 48 ucpus.add(it.ucpu); 49 } 50 51 // CPUs estimate as part of CPU subsystem 52 const cpus = ctx.traceInfo.cpus.filter((cpu) => ucpus.has(cpu.ucpu)); 53 for (const cpu of cpus) { 54 const queryKey = `cpu${cpu.ucpu}_mw`; 55 const uri = `/wattson/cpu_subsystem_estimate_cpu${cpu.ucpu}`; 56 const title = `Cpu${cpu.toString()} Estimate`; 57 ctx.tracks.registerTrack({ 58 uri, 59 title, 60 track: new CpuSubsystemEstimateTrack(ctx, uri, queryKey), 61 tags: { 62 kind: CPUSS_ESTIMATE_TRACK_KIND, 63 wattson: `CPU${cpu.ucpu}`, 64 groupName: `Wattson`, 65 }, 66 }); 67 group.addChildInOrder(new TrackNode({uri, title})); 68 } 69 70 const uri = `/wattson/cpu_subsystem_estimate_dsu_scu`; 71 const title = `DSU/SCU Estimate`; 72 ctx.tracks.registerTrack({ 73 uri, 74 title, 75 track: new CpuSubsystemEstimateTrack(ctx, uri, `dsu_scu_mw`), 76 tags: { 77 kind: CPUSS_ESTIMATE_TRACK_KIND, 78 wattson: 'Dsu_Scu', 79 groupName: `Wattson`, 80 }, 81 }); 82 group.addChildInOrder(new TrackNode({uri, title})); 83 84 // Register selection aggregators. 85 // NOTE: the registration order matters because the laste two aggregators 86 // depend on views created by the first two. 87 ctx.selection.registerAreaSelectionTab( 88 createAggregationToTabAdaptor( 89 ctx, 90 new WattsonEstimateSelectionAggregator(), 91 ), 92 ); 93 ctx.selection.registerAreaSelectionTab( 94 createAggregationToTabAdaptor( 95 ctx, 96 new WattsonThreadSelectionAggregator(), 97 ), 98 ); 99 ctx.selection.registerAreaSelectionTab( 100 createAggregationToTabAdaptor( 101 ctx, 102 new WattsonProcessSelectionAggregator(), 103 ), 104 ); 105 ctx.selection.registerAreaSelectionTab( 106 createAggregationToTabAdaptor( 107 ctx, 108 new WattsonPackageSelectionAggregator(), 109 ), 110 ); 111 } 112} 113 114class CpuSubsystemEstimateTrack extends BaseCounterTrack { 115 readonly queryKey: string; 116 117 constructor(trace: Trace, uri: string, queryKey: string) { 118 super(trace, uri); 119 this.queryKey = queryKey; 120 } 121 122 async onInit() { 123 await this.engine.query( 124 `INCLUDE PERFETTO MODULE wattson.curves.estimates;`, 125 ); 126 } 127 128 protected getDefaultCounterOptions(): CounterOptions { 129 const options = super.getDefaultCounterOptions(); 130 options.yRangeSharingKey = `CpuSubsystem`; 131 options.unit = `mW`; 132 return options; 133 } 134 135 getSqlSource() { 136 return `select ts, ${this.queryKey} as value from _system_state_mw`; 137 } 138} 139 140async function hasWattsonSupport(engine: Engine): Promise<boolean> { 141 // These tables are hard requirements and are the bare minimum needed for 142 // Wattson to run, so check that these tables are populated 143 const queryChecks: string[] = [ 144 ` 145 INCLUDE PERFETTO MODULE wattson.device_infos; 146 SELECT COUNT(*) as numRows FROM _wattson_device 147 `, 148 ` 149 INCLUDE PERFETTO MODULE linux.cpu.frequency; 150 SELECT COUNT(*) as numRows FROM cpu_frequency_counters 151 `, 152 ` 153 INCLUDE PERFETTO MODULE linux.cpu.idle; 154 SELECT COUNT(*) as numRows FROM cpu_idle_counters 155 `, 156 ]; 157 for (const queryCheck of queryChecks) { 158 const checkValue = await engine.query(queryCheck); 159 if (checkValue.firstRow({numRows: NUM}).numRows === 0) return false; 160 } 161 162 return true; 163} 164