Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1#!/bin/env python3
  2# SPDX-License-Identifier: GPL-2.0
  3
  4import subprocess
  5from shutil import which
  6from os import pread
  7
  8class PerfCounterInfo:
  9	def __init__(self, subsys, event):
 10		self.subsys = subsys
 11		self.event = event
 12
 13	def get_perf_event_name(self):
 14		return f'{self.subsys}/{self.event}/'
 15
 16	def get_turbostat_perf_id(self, counter_scope, counter_type, column_name):
 17		return f'perf/{self.subsys}/{self.event},{counter_scope},{counter_type},{column_name}'
 18
 19PERF_COUNTERS_CANDIDATES = [
 20	PerfCounterInfo('msr', 'mperf'),
 21	PerfCounterInfo('msr', 'aperf'),
 22	PerfCounterInfo('msr', 'tsc'),
 23	PerfCounterInfo('cstate_core', 'c1-residency'),
 24	PerfCounterInfo('cstate_core', 'c6-residency'),
 25	PerfCounterInfo('cstate_core', 'c7-residency'),
 26	PerfCounterInfo('cstate_pkg', 'c2-residency'),
 27	PerfCounterInfo('cstate_pkg', 'c3-residency'),
 28	PerfCounterInfo('cstate_pkg', 'c6-residency'),
 29	PerfCounterInfo('cstate_pkg', 'c7-residency'),
 30	PerfCounterInfo('cstate_pkg', 'c8-residency'),
 31	PerfCounterInfo('cstate_pkg', 'c9-residency'),
 32	PerfCounterInfo('cstate_pkg', 'c10-residency'),
 33]
 34present_perf_counters = []
 35
 36def check_perf_access():
 37	perf = which('perf')
 38	if perf is None:
 39		print('SKIP: Could not find perf binary, thus could not determine perf access.')
 40		return False
 41
 42	def has_perf_counter_access(counter_name):
 43		proc_perf = subprocess.run([perf, 'stat', '-e', counter_name, '--timeout', '10'],
 44							 capture_output = True)
 45
 46		if proc_perf.returncode != 0:
 47			print(f'SKIP: Could not read {counter_name} perf counter.')
 48			return False
 49
 50		if b'<not supported>' in proc_perf.stderr:
 51			print(f'SKIP: Could not read {counter_name} perf counter.')
 52			return False
 53
 54		return True
 55
 56	for counter in PERF_COUNTERS_CANDIDATES:
 57		if has_perf_counter_access(counter.get_perf_event_name()):
 58			present_perf_counters.append(counter)
 59
 60	if len(present_perf_counters) == 0:
 61		print('SKIP: Could not read any perf counter.')
 62		return False
 63
 64	if len(present_perf_counters) != len(PERF_COUNTERS_CANDIDATES):
 65		print(f'WARN: Could not access all of the counters - some will be left untested')
 66
 67	return True
 68
 69if not check_perf_access():
 70	exit(0)
 71
 72turbostat_counter_source_opts = ['']
 73
 74turbostat = which('turbostat')
 75if turbostat is None:
 76	print('Could not find turbostat binary')
 77	exit(1)
 78
 79timeout = which('timeout')
 80if timeout is None:
 81	print('Could not find timeout binary')
 82	exit(1)
 83
 84proc_turbostat = subprocess.run([turbostat, '--list'], capture_output = True)
 85if proc_turbostat.returncode != 0:
 86	print(f'turbostat failed with {proc_turbostat.returncode}')
 87	exit(1)
 88
 89EXPECTED_COLUMNS_DEBUG_DEFAULT = [b'usec', b'Time_Of_Day_Seconds', b'APIC', b'X2APIC']
 90
 91expected_columns = [b'CPU']
 92counters_argv = []
 93for counter in present_perf_counters:
 94	if counter.subsys == 'cstate_core':
 95		counter_scope = 'core'
 96	elif counter.subsys == 'cstate_pkg':
 97		counter_scope = 'package'
 98	else:
 99		counter_scope = 'cpu'
100
101	counter_type = 'delta'
102	column_name = counter.event
103
104	cparams = counter.get_turbostat_perf_id(
105		counter_scope = counter_scope,
106		counter_type = counter_type,
107		column_name = column_name
108	)
109	expected_columns.append(column_name.encode())
110	counters_argv.extend(['--add', cparams])
111
112expected_columns_debug = EXPECTED_COLUMNS_DEBUG_DEFAULT + expected_columns
113
114def gen_user_friendly_cmdline(argv_):
115	argv = argv_[:]
116	ret = ''
117
118	while len(argv) != 0:
119		arg = argv.pop(0)
120		arg_next = ''
121
122		if arg in ('-i', '--show', '--add'):
123			arg_next = argv.pop(0) if len(argv) > 0 else ''
124
125		ret += f'{arg} {arg_next} \\\n\t'
126
127	# Remove the last separator and return
128	return ret[:-4]
129
130#
131# Run turbostat for some time and send SIGINT
132#
133timeout_argv = [timeout, '--preserve-status', '-s', 'SIGINT', '-k', '3', '0.2s']
134turbostat_argv = [turbostat, '-i', '0.50', '--show', 'CPU'] + counters_argv
135
136def check_columns_or_fail(expected_columns: list, actual_columns: list):
137	if len(actual_columns) != len(expected_columns):
138		print(f'turbostat column check failed\n{expected_columns=}\n{actual_columns=}')
139		exit(1)
140
141	failed = False
142	for expected_column in expected_columns:
143		if expected_column not in actual_columns:
144			print(f'turbostat column check failed: missing column {expected_column.decode()}')
145			failed = True
146
147	if failed:
148		exit(1)
149
150cmdline = gen_user_friendly_cmdline(turbostat_argv)
151print(f'Running turbostat with:\n\t{cmdline}\n... ', end = '', flush = True)
152proc_turbostat = subprocess.run(timeout_argv + turbostat_argv, capture_output = True)
153if proc_turbostat.returncode != 0:
154	print(f'turbostat failed with {proc_turbostat.returncode}')
155	exit(1)
156
157actual_columns = proc_turbostat.stdout.split(b'\n')[0].split(b'\t')
158check_columns_or_fail(expected_columns, actual_columns)
159print('OK')
160
161#
162# Same, but with --debug
163#
164# We explicitly specify '--show CPU' to make sure turbostat
165# don't show a bunch of default counters instead.
166#
167turbostat_argv.append('--debug')
168
169cmdline = gen_user_friendly_cmdline(turbostat_argv)
170print(f'Running turbostat (in debug mode) with:\n\t{cmdline}\n... ', end = '', flush = True)
171proc_turbostat = subprocess.run(timeout_argv + turbostat_argv, capture_output = True)
172if proc_turbostat.returncode != 0:
173	print(f'turbostat failed with {proc_turbostat.returncode}')
174	exit(1)
175
176actual_columns = proc_turbostat.stdout.split(b'\n')[0].split(b'\t')
177check_columns_or_fail(expected_columns_debug, actual_columns)
178print('OK')