blob: 66ec8091f0e39891c8610d17d30f4f792cc7dbc1 [file] [log] [blame] [edit]
import m, {Vnode} from 'mithril';
import {Engine} from '../../trace_processor/engine';
import {NUM, STR_NULL} from '../../trace_processor/query_result';
import {linkify} from '../../widgets/anchor';
enum FtraceEvent {
CPU_FREQUENCY = 'power/cpu_frequency',
CPU_IDLE = 'power/cpu_idle',
DEVFREQ_FREQUENCY = 'devfreq/devfreq_frequency',
CPUHP_ENTER = 'cpuhp/cpuhp_enter',
CPUHP_EXIT = 'cpuhp/cpuhp_exit',
CPUHP_MULTI_ENTER = 'cpuhp/cpuhp_multi_enter',
PRINT = 'ftrace/print',
SUSPEND_RESUME = 'power/suspend_resume',
SCHED_SWITCH = 'sched/sched_switch',
}
// Walk through user's Perfetto Trace Configs and check
// against bare minimum configs that makes Wattson work.
// Add the missing ones to missingEvents, display in UI.
export async function hasWattsonSufficientCPUConfigs(
engine: Engine,
): Promise<string[]> {
// 1. Determine required events first
const requiredEvents = new Set<FtraceEvent>([FtraceEvent.CPU_FREQUENCY]);
const dsuDependencyQuery = await engine.query(
`
INCLUDE PERFETTO MODULE wattson.curves.utils;
SELECT count(*) AS count FROM _cpu_w_dsu_dependency;
`,
);
if (dsuDependencyQuery.firstRow({count: NUM}).count > 0) {
requiredEvents.add(FtraceEvent.DEVFREQ_FREQUENCY);
}
// 2. Check configured events
const query = `
SELECT str_value
FROM metadata
WHERE name = 'trace_config_pbtxt';
`;
const result = await engine.query(query);
const row = result.maybeFirstRow({str_value: STR_NULL});
const traceConfig = row?.str_value || '';
const foundEvents = new Set<FtraceEvent>();
if (/cpufreq_period_ms:\s*\d+/.test(traceConfig)) {
foundEvents.add(FtraceEvent.CPU_FREQUENCY);
}
// below events are included in "freq" Atrace category.
if (/atrace_categories:\s*"freq"/.test(traceConfig)) {
foundEvents.add(FtraceEvent.CPU_FREQUENCY);
foundEvents.add(FtraceEvent.DEVFREQ_FREQUENCY);
foundEvents.add(FtraceEvent.SUSPEND_RESUME);
foundEvents.add(FtraceEvent.CPUHP_ENTER);
foundEvents.add(FtraceEvent.CPUHP_EXIT);
}
for (const event of Object.values(FtraceEvent)) {
const eventPattern = new RegExp(`ftrace_events:\\s*"${event}"`);
if (eventPattern.test(traceConfig)) {
foundEvents.add(event);
}
}
// 3. Compare required events with found events
const missingEvents: string[] = [];
for (const requiredEvent of requiredEvents) {
if (!foundEvents.has(requiredEvent)) {
missingEvents.push(requiredEvent);
}
}
return missingEvents;
}
export function createCpuWarnings(
missingEvents: string[],
realCpuIdleCounters: boolean,
): Vnode | undefined {
const warningMsg: Vnode[] = [];
if (missingEvents.length > 0) {
warningMsg.push(
m(
'.pf-wattson-warning',
linkify(
`See https://source.android.com/docs/core/power/wattson/how-to-wattson for more details on Wattson's required trace configuration. The following ftrace_events are necessary for Wattson to make power estimates:`,
),
m(
'.pf-wattson-warning__list',
missingEvents.map((event) => m('li', event)),
),
),
);
}
if (!realCpuIdleCounters) {
if (warningMsg.length > 0) {
warningMsg.push(m('hr'));
}
warningMsg.push(
m(
'p',
'`cpu_idle` counters are not available in this trace; deriving cpu_idle counters from the swapper thread.',
),
);
}
return warningMsg.length > 0
? m('.pf-wattson-warning', warningMsg)
: undefined;
}