2 # SPDX-License-Identifier: GPL-2.0
5 from shutil
import which
8 # CDLL calls dlopen underneath.
9 # Calling it with None (null), we get handle to the our own image (python interpreter).
10 # We hope to find sched_getcpu() inside ;]
11 # This is a bit ugly, but helps shipping working software, so..
15 this_image
= ctypes
.CDLL(None)
16 BASE_CPU
= this_image
.sched_getcpu()
18 BASE_CPU
= 0 # If we fail, set to 0 and pray it's not offline.
20 MSR_IA32_MPERF
= 0x000000e7
21 MSR_IA32_APERF
= 0x000000e8
23 def check_perf_access():
26 print('SKIP: Could not find perf binary, thus could not determine perf access.')
29 def has_perf_counter_access(counter_name
):
30 proc_perf
= subprocess
.run([perf
, 'stat', '-e', counter_name
, '--timeout', '10'],
31 capture_output
= True)
33 if proc_perf
.returncode
!= 0:
34 print(f
'SKIP: Could not read {counter_name} perf counter, assuming no access.')
37 if b
'<not supported>' in proc_perf
.stderr
:
38 print(f
'SKIP: Could not read {counter_name} perf counter, assuming no access.')
43 if not has_perf_counter_access('msr/mperf/'):
45 if not has_perf_counter_access('msr/aperf/'):
47 if not has_perf_counter_access('msr/smi/'):
52 def check_msr_access():
54 file_msr
= open(f
'/dev/cpu/{BASE_CPU}/msr', 'rb')
58 if len(pread(file_msr
.fileno(), 8, MSR_IA32_MPERF
)) != 8:
61 if len(pread(file_msr
.fileno(), 8, MSR_IA32_APERF
)) != 8:
66 has_perf_access
= check_perf_access()
67 has_msr_access
= check_msr_access()
69 turbostat_counter_source_opts
= ['']
72 turbostat_counter_source_opts
.append('--no-perf')
74 print('SKIP: doesn\'t have MSR access, skipping run with --no-perf')
77 turbostat_counter_source_opts
.append('--no-msr')
79 print('SKIP: doesn\'t have perf access, skipping run with --no-msr')
81 if not has_msr_access
and not has_perf_access
:
82 print('SKIP: No MSR nor perf access detected. Skipping the tests entirely')
85 turbostat
= which('turbostat')
87 print('Could not find turbostat binary')
90 timeout
= which('timeout')
92 print('Could not find timeout binary')
95 proc_turbostat
= subprocess
.run([turbostat
, '--list'], capture_output
= True)
96 if proc_turbostat
.returncode
!= 0:
97 print(f
'turbostat failed with {proc_turbostat.returncode}')
100 EXPECTED_COLUMNS_DEBUG_DEFAULT
= b
'usec\tTime_Of_Day_Seconds\tAPIC\tX2APIC'
102 SMI_APERF_MPERF_DEPENDENT_BICS
= [
109 SMI_APERF_MPERF_DEPENDENT_BICS
.append('IPC')
111 for bic
in SMI_APERF_MPERF_DEPENDENT_BICS
:
112 for counter_source_opt
in turbostat_counter_source_opts
:
114 # Ugly special case, but it is what it is..
115 if counter_source_opt
== '--no-perf' and bic
== 'IPC':
118 expected_columns
= bic
.encode()
119 expected_columns_debug
= EXPECTED_COLUMNS_DEBUG_DEFAULT
+ f
'\t{bic}'.encode()
122 # Run turbostat for some time and send SIGINT
124 timeout_argv
= [timeout
, '--preserve-status', '-s', 'SIGINT', '-k', '3', '0.2s']
125 turbostat_argv
= [turbostat
, '-i', '0.50', '--show', bic
]
127 if counter_source_opt
:
128 turbostat_argv
.append(counter_source_opt
)
130 print(f
'Running turbostat with {turbostat_argv=}... ', end
= '', flush
= True)
131 proc_turbostat
= subprocess
.run(timeout_argv
+ turbostat_argv
, capture_output
= True)
132 if proc_turbostat
.returncode
!= 0:
133 print(f
'turbostat failed with {proc_turbostat.returncode}')
136 actual_columns
= proc_turbostat
.stdout
.split(b
'\n')[0]
137 if expected_columns
!= actual_columns
:
138 print(f
'turbostat column check failed\n{expected_columns=}\n{actual_columns=}')
143 # Same, but with --debug
145 turbostat_argv
.append('--debug')
147 print(f
'Running turbostat with {turbostat_argv=}... ', end
= '', flush
= True)
148 proc_turbostat
= subprocess
.run(timeout_argv
+ turbostat_argv
, capture_output
= True)
149 if proc_turbostat
.returncode
!= 0:
150 print(f
'turbostat failed with {proc_turbostat.returncode}')
153 actual_columns
= proc_turbostat
.stdout
.split(b
'\n')[0]
154 if expected_columns_debug
!= actual_columns
:
155 print(f
'turbostat column check failed\n{expected_columns_debug=}\n{actual_columns=}')