brintos

brintos / linux-shallow public Read only

0
0
Text · 5.2 KiB · 9ab4aaf Raw
179 lines · python
1#!/bin/env python32# SPDX-License-Identifier: GPL-2.03 4import subprocess5from shutil import which6from os import pread7 8class PerfCounterInfo:9	def __init__(self, subsys, event):10		self.subsys = subsys11		self.event = event12 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 False41 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 False49 50		if b'<not supported>' in proc_perf.stderr:51			print(f'SKIP: Could not read {counter_name} perf counter.')52			return False53 54		return True55 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 False63 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 True68 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.event103 104	cparams = counter.get_turbostat_perf_id(105		counter_scope = counter_scope,106		counter_type = counter_type,107		column_name = column_name108	)109	expected_columns.append(column_name.encode())110	counters_argv.extend(['--add', cparams])111 112expected_columns_debug = EXPECTED_COLUMNS_DEBUG_DEFAULT + expected_columns113 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 return128	return ret[:-4]129 130#131# Run turbostat for some time and send SIGINT132#133timeout_argv = [timeout, '--preserve-status', '-s', 'SIGINT', '-k', '3', '0.2s']134turbostat_argv = [turbostat, '-i', '0.50', '--show', 'CPU'] + counters_argv135 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 = False142	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 = True146 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 --debug163#164# We explicitly specify '--show CPU' to make sure turbostat165# 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')179