Skip to content

Commit

Permalink
tools/power turbostat: Add selftests for added perf counters
Browse files Browse the repository at this point in the history
Test adds several perf counters from msr, cstate_core and cstate_pkg
groups and checks if the columns for those counters show up.
The test skips the counters that are not present. It is not an error,
but the test may not be as exhaustive.

Signed-off-by: Patryk Wlazlyn <patryk.wlazlyn@linux.intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
  • Loading branch information
Patryk Wlazlyn authored and Len Brown committed Jul 26, 2024
1 parent 1f8add1 commit f3065f9
Showing 1 changed file with 178 additions and 0 deletions.
178 changes: 178 additions & 0 deletions tools/testing/selftests/turbostat/added_perf_counters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0

import subprocess
from shutil import which
from os import pread

class PerfCounterInfo:
def __init__(self, subsys, event):
self.subsys = subsys
self.event = event

def get_perf_event_name(self):
return f'{self.subsys}/{self.event}/'

def get_turbostat_perf_id(self, counter_scope, counter_type, column_name):
return f'perf/{self.subsys}/{self.event},{counter_scope},{counter_type},{column_name}'

PERF_COUNTERS_CANDIDATES = [
PerfCounterInfo('msr', 'mperf'),
PerfCounterInfo('msr', 'aperf'),
PerfCounterInfo('msr', 'tsc'),
PerfCounterInfo('cstate_core', 'c1-residency'),
PerfCounterInfo('cstate_core', 'c6-residency'),
PerfCounterInfo('cstate_core', 'c7-residency'),
PerfCounterInfo('cstate_pkg', 'c2-residency'),
PerfCounterInfo('cstate_pkg', 'c3-residency'),
PerfCounterInfo('cstate_pkg', 'c6-residency'),
PerfCounterInfo('cstate_pkg', 'c7-residency'),
PerfCounterInfo('cstate_pkg', 'c8-residency'),
PerfCounterInfo('cstate_pkg', 'c9-residency'),
PerfCounterInfo('cstate_pkg', 'c10-residency'),
]
present_perf_counters = []

def check_perf_access():
perf = which('perf')
if perf is None:
print('SKIP: Could not find perf binary, thus could not determine perf access.')
return False

def has_perf_counter_access(counter_name):
proc_perf = subprocess.run([perf, 'stat', '-e', counter_name, '--timeout', '10'],
capture_output = True)

if proc_perf.returncode != 0:
print(f'SKIP: Could not read {counter_name} perf counter.')
return False

if b'<not supported>' in proc_perf.stderr:
print(f'SKIP: Could not read {counter_name} perf counter.')
return False

return True

for counter in PERF_COUNTERS_CANDIDATES:
if has_perf_counter_access(counter.get_perf_event_name()):
present_perf_counters.append(counter)

if len(present_perf_counters) == 0:
print('SKIP: Could not read any perf counter.')
return False

if len(present_perf_counters) != len(PERF_COUNTERS_CANDIDATES):
print(f'WARN: Could not access all of the counters - some will be left untested')

return True

if not check_perf_access():
exit(0)

turbostat_counter_source_opts = ['']

turbostat = which('turbostat')
if turbostat is None:
print('Could not find turbostat binary')
exit(1)

timeout = which('timeout')
if timeout is None:
print('Could not find timeout binary')
exit(1)

proc_turbostat = subprocess.run([turbostat, '--list'], capture_output = True)
if proc_turbostat.returncode != 0:
print(f'turbostat failed with {proc_turbostat.returncode}')
exit(1)

EXPECTED_COLUMNS_DEBUG_DEFAULT = [b'usec', b'Time_Of_Day_Seconds', b'APIC', b'X2APIC']

expected_columns = [b'CPU']
counters_argv = []
for counter in present_perf_counters:
if counter.subsys == 'cstate_core':
counter_scope = 'core'
elif counter.subsys == 'cstate_pkg':
counter_scope = 'package'
else:
counter_scope = 'cpu'

counter_type = 'delta'
column_name = counter.event

cparams = counter.get_turbostat_perf_id(
counter_scope = counter_scope,
counter_type = counter_type,
column_name = column_name
)
expected_columns.append(column_name.encode())
counters_argv.extend(['--add', cparams])

expected_columns_debug = EXPECTED_COLUMNS_DEBUG_DEFAULT + expected_columns

def gen_user_friendly_cmdline(argv_):
argv = argv_[:]
ret = ''

while len(argv) != 0:
arg = argv.pop(0)
arg_next = ''

if arg in ('-i', '--show', '--add'):
arg_next = argv.pop(0) if len(argv) > 0 else ''

ret += f'{arg} {arg_next} \\\n\t'

# Remove the last separator and return
return ret[:-4]

#
# Run turbostat for some time and send SIGINT
#
timeout_argv = [timeout, '--preserve-status', '-s', 'SIGINT', '-k', '3', '0.2s']
turbostat_argv = [turbostat, '-i', '0.50', '--show', 'CPU'] + counters_argv

def check_columns_or_fail(expected_columns: list, actual_columns: list):
if len(actual_columns) != len(expected_columns):
print(f'turbostat column check failed\n{expected_columns=}\n{actual_columns=}')
exit(1)

failed = False
for expected_column in expected_columns:
if expected_column not in actual_columns:
print(f'turbostat column check failed: missing column {expected_column.decode()}')
failed = True

if failed:
exit(1)

cmdline = gen_user_friendly_cmdline(turbostat_argv)
print(f'Running turbostat with:\n\t{cmdline}\n... ', end = '', flush = True)
proc_turbostat = subprocess.run(timeout_argv + turbostat_argv, capture_output = True)
if proc_turbostat.returncode != 0:
print(f'turbostat failed with {proc_turbostat.returncode}')
exit(1)

actual_columns = proc_turbostat.stdout.split(b'\n')[0].split(b'\t')
check_columns_or_fail(expected_columns, actual_columns)
print('OK')

#
# Same, but with --debug
#
# We explicitly specify '--show CPU' to make sure turbostat
# don't show a bunch of default counters instead.
#
turbostat_argv.append('--debug')

cmdline = gen_user_friendly_cmdline(turbostat_argv)
print(f'Running turbostat (in debug mode) with:\n\t{cmdline}\n... ', end = '', flush = True)
proc_turbostat = subprocess.run(timeout_argv + turbostat_argv, capture_output = True)
if proc_turbostat.returncode != 0:
print(f'turbostat failed with {proc_turbostat.returncode}')
exit(1)

actual_columns = proc_turbostat.stdout.split(b'\n')[0].split(b'\t')
check_columns_or_fail(expected_columns_debug, actual_columns)
print('OK')

0 comments on commit f3065f9

Please sign in to comment.