blob: 2ca428daf9755d7472a6f83125150be633d0cf3c [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 {exists} from '../../base/utils';
import {ColumnDef, Sorting} from '../../public/aggregation';
import {AreaSelection} from '../../public/selection';
import {Engine} from '../../trace_processor/engine';
import {CPU_SLICE_TRACK_KIND} from '../../public/track_kinds';
import {AreaSelectionAggregator} from '../../public/selection';
export class WattsonThreadSelectionAggregator
implements AreaSelectionAggregator
{
readonly id = 'wattson_thread_aggregation';
async createAggregateView(engine: Engine, area: AreaSelection) {
await engine.query(`drop view if exists ${this.id};`);
const selectedCpus: number[] = [];
for (const trackInfo of area.tracks) {
if (trackInfo?.tags?.kind === CPU_SLICE_TRACK_KIND) {
exists(trackInfo.tags.cpu) && selectedCpus.push(trackInfo.tags.cpu);
}
}
if (selectedCpus.length === 0) return false;
const duration = area.end - area.start;
const cpusCsv = `(` + selectedCpus.join() + `)`;
engine.query(`
INCLUDE PERFETTO MODULE viz.summary.threads_w_processes;
INCLUDE PERFETTO MODULE wattson.curves.idle_attribution;
INCLUDE PERFETTO MODULE wattson.curves.estimates;
CREATE OR REPLACE PERFETTO TABLE _ui_selection_window AS
SELECT
${area.start} as ts,
${duration} as dur;
-- Processes filtered by CPU within the UI defined time window
DROP TABLE IF EXISTS _windowed_summary;
CREATE VIRTUAL TABLE _windowed_summary
USING
SPAN_JOIN(_ui_selection_window, _sched_w_thread_process_package_summary);
-- Only get idle attribution in user defined window and filter by selected
-- CPUs and GROUP BY thread
CREATE OR REPLACE PERFETTO TABLE _per_thread_idle_attribution AS
SELECT
ROUND(SUM(idle_cost_mws), 2) as idle_cost_mws,
utid
FROM _filter_idle_attribution(${area.start}, ${duration})
WHERE cpu in ${cpusCsv}
GROUP BY utid;
`);
this.runEstimateThreadsQuery(engine, selectedCpus, duration);
return true;
}
// This function returns a query that gets the average and estimate from
// Wattson for the selection in the UI window based on thread. The grouping by
// thread needs to 'remove' 2 dimensions; the threads need to be grouped over
// time and the threads need to be grouped over CPUs.
// 1. Window and associate thread with proper Wattson estimate slice
// 2. Group all threads over time on a per CPU basis
// 3. Group all threads over all CPUs
runEstimateThreadsQuery(
engine: Engine,
selectedCpu: number[],
duration: bigint,
) {
// Estimate and total per UTID per CPU
selectedCpu.forEach((cpu) => {
engine.query(`
-- Packages filtered by CPU
CREATE OR REPLACE PERFETTO VIEW _windowed_summary_per_cpu${cpu} AS
SELECT *
FROM _windowed_summary WHERE cpu = ${cpu};
-- CPU specific track with slices for curves
CREATE OR REPLACE PERFETTO VIEW _per_cpu${cpu}_curve AS
SELECT ts, dur, cpu${cpu}_curve
FROM _system_state_curves;
-- Filter out track when threads are available
DROP TABLE IF EXISTS _windowed_thread_curve${cpu};
CREATE VIRTUAL TABLE _windowed_thread_curve${cpu}
USING
SPAN_JOIN(_per_cpu${cpu}_curve, _windowed_summary_per_cpu${cpu});
-- Total estimate per UTID per CPU
CREATE OR REPLACE PERFETTO VIEW _total_per_cpu${cpu} AS
SELECT
SUM(cpu${cpu}_curve * dur) as total_pws,
SUM(dur) as dur,
tid,
pid,
uid,
utid,
upid,
thread_name,
process_name,
package_name
FROM _windowed_thread_curve${cpu}
GROUP BY utid;
`);
});
// Estimate and total per UTID, removing CPU dimension
let query = `CREATE OR REPLACE PERFETTO TABLE _unioned_per_cpu_total AS `;
selectedCpu.forEach((cpu, i) => {
query += i != 0 ? `UNION ALL\n` : ``;
query += `SELECT * from _total_per_cpu${cpu}\n`;
});
query += `
;
-- Grouped again by UTID, but this time to make it CPU agnostic
CREATE VIEW ${this.id} AS
SELECT
ROUND(SUM(total_pws) / ${duration}, 2) as active_mw,
ROUND(SUM(total_pws) / 1000000000, 2) as active_mws,
COALESCE(idle_cost_mws, 0) as idle_cost_mws,
thread_name,
utid,
tid,
pid
FROM _unioned_per_cpu_total
LEFT JOIN _per_thread_idle_attribution USING (utid)
GROUP BY utid;
`;
engine.query(query);
return;
}
getColumnDefinitions(): ColumnDef[] {
return [
{
title: 'Thread Name',
kind: 'STRING',
columnConstructor: Uint16Array,
columnId: 'thread_name',
},
{
title: 'TID',
kind: 'NUMBER',
columnConstructor: Uint16Array,
columnId: 'tid',
},
{
title: 'PID',
kind: 'NUMBER',
columnConstructor: Uint16Array,
columnId: 'pid',
},
{
title: 'Active power (estimated mW)',
kind: 'NUMBER',
columnConstructor: Float64Array,
columnId: 'active_mw',
sum: true,
},
{
title: 'Active energy (estimated mWs)',
kind: 'NUMBER',
columnConstructor: Float64Array,
columnId: 'active_mws',
sum: true,
},
{
title: 'Idle transitions overhead (estimated mWs)',
kind: 'NUMBER',
columnConstructor: Float64Array,
columnId: 'idle_cost_mws',
sum: true,
},
];
}
async getExtra() {}
getTabName() {
return 'Wattson by thread';
}
getDefaultSorting(): Sorting {
return {column: 'active_mws', direction: 'DESC'};
}
}