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'; 29 30export default class implements PerfettoPlugin { 31 static readonly id = `org.kernel.Wattson`; 32 33 async onTraceLoad(ctx: Trace): Promise<void> { 34 // Short circuit if Wattson is not supported for this Perfetto trace 35 if (!(await hasWattsonSupport(ctx.engine))) return; 36 37 const group = new TrackNode({title: 'Wattson', isSummary: true}); 38 ctx.workspace.addChildInOrder(group); 39 40 // CPUs estimate as part of CPU subsystem 41 const cpus = ctx.traceInfo.cpus; 42 for (const cpu of cpus) { 43 const queryKey = `cpu${cpu}_mw`; 44 const uri = `/wattson/cpu_subsystem_estimate_cpu${cpu}`; 45 const title = `Cpu${cpu} Estimate`; 46 ctx.tracks.registerTrack({ 47 uri, 48 title, 49 track: new CpuSubsystemEstimateTrack(ctx, uri, queryKey), 50 tags: { 51 kind: CPUSS_ESTIMATE_TRACK_KIND, 52 wattson: `CPU${cpu}`, 53 groupName: `Wattson`, 54 }, 55 }); 56 group.addChildInOrder(new TrackNode({uri, title})); 57 } 58 59 const uri = `/wattson/cpu_subsystem_estimate_dsu_scu`; 60 const title = `DSU/SCU Estimate`; 61 ctx.tracks.registerTrack({ 62 uri, 63 title, 64 track: new CpuSubsystemEstimateTrack(ctx, uri, `dsu_scu_mw`), 65 tags: { 66 kind: CPUSS_ESTIMATE_TRACK_KIND, 67 wattson: 'Dsu_Scu', 68 groupName: `Wattson`, 69 }, 70 }); 71 group.addChildInOrder(new TrackNode({uri, title})); 72 73 // Register selection aggregators. 74 // NOTE: the registration order matters because the laste two aggregators 75 // depend on views created by the first two. 76 ctx.selection.registerAreaSelectionAggregator( 77 new WattsonEstimateSelectionAggregator(), 78 ); 79 ctx.selection.registerAreaSelectionAggregator( 80 new WattsonThreadSelectionAggregator(), 81 ); 82 ctx.selection.registerAreaSelectionAggregator( 83 new WattsonProcessSelectionAggregator(), 84 ); 85 ctx.selection.registerAreaSelectionAggregator( 86 new WattsonPackageSelectionAggregator(), 87 ); 88 } 89} 90 91class CpuSubsystemEstimateTrack extends BaseCounterTrack { 92 readonly queryKey: string; 93 94 constructor(trace: Trace, uri: string, queryKey: string) { 95 super(trace, uri); 96 this.queryKey = queryKey; 97 } 98 99 async onInit() { 100 await this.engine.query( 101 `INCLUDE PERFETTO MODULE wattson.curves.estimates;`, 102 ); 103 } 104 105 protected getDefaultCounterOptions(): CounterOptions { 106 const options = super.getDefaultCounterOptions(); 107 options.yRangeSharingKey = `CpuSubsystem`; 108 options.unit = `mW`; 109 return options; 110 } 111 112 getSqlSource() { 113 return `select ts, ${this.queryKey} as value from _system_state_mw`; 114 } 115} 116 117async function hasWattsonSupport(engine: Engine): Promise<boolean> { 118 // These tables are hard requirements and are the bare minimum needed for 119 // Wattson to run, so check that these tables are populated 120 const queryChecks: string[] = [ 121 ` 122 INCLUDE PERFETTO MODULE wattson.device_infos; 123 SELECT COUNT(*) as numRows FROM _wattson_device 124 `, 125 ` 126 INCLUDE PERFETTO MODULE linux.cpu.frequency; 127 SELECT COUNT(*) as numRows FROM cpu_frequency_counters 128 `, 129 ` 130 INCLUDE PERFETTO MODULE linux.cpu.idle; 131 SELECT COUNT(*) as numRows FROM cpu_idle_counters 132 `, 133 ]; 134 for (const queryCheck of queryChecks) { 135 const checkValue = await engine.query(queryCheck); 136 if (checkValue.firstRow({numRows: NUM}).numRows === 0) return false; 137 } 138 139 return true; 140} 141