blob: 539366d063291b868e697f2bef2dbdce6d296e68 [file] [log] [blame]
// Copyright (C) 2024 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {
BaseCounterTrack,
CounterOptions,
} from '../../frontend/base_counter_track';
import {Trace} from '../../public/trace';
import {PerfettoPlugin} from '../../public/plugin';
import {CPUSS_ESTIMATE_TRACK_KIND} from '../../public/track_kinds';
import {TrackNode} from '../../public/workspace';
import {WattsonEstimateSelectionAggregator} from './estimate_aggregator';
import {WattsonPackageSelectionAggregator} from './package_aggregator';
import {WattsonProcessSelectionAggregator} from './process_aggregator';
import {WattsonThreadSelectionAggregator} from './thread_aggregator';
import {Engine} from '../../trace_processor/engine';
import {NUM} from '../../trace_processor/query_result';
export default class implements PerfettoPlugin {
static readonly id = `org.kernel.Wattson`;
async onTraceLoad(ctx: Trace): Promise<void> {
// Short circuit if Wattson is not supported for this Perfetto trace
if (!(await hasWattsonSupport(ctx.engine))) return;
ctx.engine.query(`INCLUDE PERFETTO MODULE wattson.curves.estimates;`);
const group = new TrackNode({title: 'Wattson', isSummary: true});
ctx.workspace.addChildInOrder(group);
// CPUs estimate as part of CPU subsystem
const cpus = ctx.traceInfo.cpus;
for (const cpu of cpus) {
const queryKey = `cpu${cpu}_curve`;
const uri = `/wattson/cpu_subsystem_estimate_cpu${cpu}`;
const title = `Cpu${cpu} Estimate`;
ctx.tracks.registerTrack({
uri,
title,
track: new CpuSubsystemEstimateTrack(ctx, uri, queryKey),
tags: {
kind: CPUSS_ESTIMATE_TRACK_KIND,
wattson: `CPU${cpu}`,
groupName: `Wattson`,
},
});
group.addChildInOrder(new TrackNode({uri, title}));
}
const uri = `/wattson/cpu_subsystem_estimate_dsu_scu`;
const title = `DSU/SCU Estimate`;
ctx.tracks.registerTrack({
uri,
title,
track: new CpuSubsystemEstimateTrack(ctx, uri, `dsu_scu`),
tags: {
kind: CPUSS_ESTIMATE_TRACK_KIND,
wattson: 'Dsu_Scu',
groupName: `Wattson`,
},
});
group.addChildInOrder(new TrackNode({uri, title}));
// Register selection aggregators.
// NOTE: the registration order matters because the laste two aggregators
// depend on views created by the first two.
ctx.selection.registerAreaSelectionAggreagtor(
new WattsonEstimateSelectionAggregator(),
);
ctx.selection.registerAreaSelectionAggreagtor(
new WattsonThreadSelectionAggregator(),
);
ctx.selection.registerAreaSelectionAggreagtor(
new WattsonPackageSelectionAggregator(),
);
ctx.selection.registerAreaSelectionAggreagtor(
new WattsonProcessSelectionAggregator(),
);
}
}
class CpuSubsystemEstimateTrack extends BaseCounterTrack {
readonly queryKey: string;
constructor(trace: Trace, uri: string, queryKey: string) {
super({
trace,
uri,
});
this.queryKey = queryKey;
}
protected getDefaultCounterOptions(): CounterOptions {
const options = super.getDefaultCounterOptions();
options.yRangeSharingKey = `CpuSubsystem`;
options.unit = `mW`;
return options;
}
getSqlSource() {
if (this.queryKey.startsWith(`cpu`)) {
return `select ts, ${this.queryKey} as value from _system_state_curves`;
} else {
return `
select
ts,
-- L3 values are scaled by 1000 because it's divided by ns and L3 LUTs
-- are scaled by 10^6. This brings to same units as static_curve (mW)
((IFNULL(l3_hit_value, 0) + IFNULL(l3_miss_value, 0)) * 1000 / dur)
+ static_curve as value
from _system_state_curves
`;
}
}
}
async function hasWattsonSupport(engine: Engine): Promise<boolean> {
// These tables are hard requirements and are the bare minimum needed for
// Wattson to run, so check that these tables are populated
const queryChecks: string[] = [
`
INCLUDE PERFETTO MODULE wattson.device_infos;
SELECT COUNT(*) as numRows FROM _wattson_device
`,
`
INCLUDE PERFETTO MODULE linux.cpu.frequency;
SELECT COUNT(*) as numRows FROM cpu_frequency_counters
`,
`
INCLUDE PERFETTO MODULE linux.cpu.idle;
SELECT COUNT(*) as numRows FROM cpu_idle_counters
`,
];
for (const queryCheck of queryChecks) {
const checkValue = await engine.query(queryCheck);
if (checkValue.firstRow({numRows: NUM}).numRows === 0) return false;
}
return true;
}