xref: /aosp_15_r20/external/perfetto/ui/src/plugins/org.kernel.Wattson/index.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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