• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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