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