aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatryk Wlazlyn2024-07-04 20:11:33 +0200
committerLen Brown2024-07-26 13:51:25 -0400
commitf3065f9c3917fa9279992623a2f3282f1fd43515 (patch)
tree6e561560b0cbad1ba0e92186a8bbb55e68b88a68
parent1f8add13e6c86653da2ec599dfc94e7a8865cdcb (diff)
tools/power turbostat: Add selftests for added perf counters
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>
-rwxr-xr-xtools/testing/selftests/turbostat/added_perf_counters.py178
1 files changed, 178 insertions, 0 deletions
diff --git a/tools/testing/selftests/turbostat/added_perf_counters.py b/tools/testing/selftests/turbostat/added_perf_counters.py
new file mode 100755
index 000000000000..9ab4aaf45fb8
--- /dev/null
+++ b/tools/testing/selftests/turbostat/added_perf_counters.py
@@ -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')