2 # SPDX-License-Identifier: GPL-2.0
5 from shutil
import which
9 def __init__(self
, subsys
, event
):
13 def get_perf_event_name(self
):
14 return f
'{self.subsys}/{self.event}/'
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}'
19 PERF_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'),
34 present_perf_counters
= []
36 def check_perf_access():
39 print('SKIP: Could not find perf binary, thus could not determine perf access.')
42 def has_perf_counter_access(counter_name
):
43 proc_perf
= subprocess
.run([perf
, 'stat', '-e', counter_name
, '--timeout', '10'],
44 capture_output
= True)
46 if proc_perf
.returncode
!= 0:
47 print(f
'SKIP: Could not read {counter_name} perf counter.')
50 if b
'<not supported>' in proc_perf
.stderr
:
51 print(f
'SKIP: Could not read {counter_name} perf counter.')
56 for counter
in PERF_COUNTERS_CANDIDATES
:
57 if has_perf_counter_access(counter
.get_perf_event_name()):
58 present_perf_counters
.append(counter
)
60 if len(present_perf_counters
) == 0:
61 print('SKIP: Could not read any perf counter.')
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')
69 if not check_perf_access():
72 turbostat_counter_source_opts
= ['']
74 turbostat
= which('turbostat')
76 print('Could not find turbostat binary')
79 timeout
= which('timeout')
81 print('Could not find timeout binary')
84 proc_turbostat
= subprocess
.run([turbostat
, '--list'], capture_output
= True)
85 if proc_turbostat
.returncode
!= 0:
86 print(f
'turbostat failed with {proc_turbostat.returncode}')
89 EXPECTED_COLUMNS_DEBUG_DEFAULT
= [b
'usec', b
'Time_Of_Day_Seconds', b
'APIC', b
'X2APIC']
91 expected_columns
= [b
'CPU']
93 for 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'
101 counter_type
= 'delta'
102 column_name
= counter
.event
104 cparams
= counter
.get_turbostat_perf_id(
105 counter_scope
= counter_scope
,
106 counter_type
= counter_type
,
107 column_name
= column_name
109 expected_columns
.append(column_name
.encode())
110 counters_argv
.extend(['--add', cparams
])
112 expected_columns_debug
= EXPECTED_COLUMNS_DEBUG_DEFAULT
+ expected_columns
114 def gen_user_friendly_cmdline(argv_
):
118 while len(argv
) != 0:
122 if arg
in ('-i', '--show', '--add'):
123 arg_next
= argv
.pop(0) if len(argv
) > 0 else ''
125 ret
+= f
'{arg} {arg_next} \\\n\t'
127 # Remove the last separator and return
131 # Run turbostat for some time and send SIGINT
133 timeout_argv
= [timeout
, '--preserve-status', '-s', 'SIGINT', '-k', '3', '0.2s']
134 turbostat_argv
= [turbostat
, '-i', '0.50', '--show', 'CPU'] + counters_argv
136 def 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=}')
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()}')
150 cmdline
= gen_user_friendly_cmdline(turbostat_argv
)
151 print(f
'Running turbostat with:\n\t{cmdline}\n... ', end
= '', flush
= True)
152 proc_turbostat
= subprocess
.run(timeout_argv
+ turbostat_argv
, capture_output
= True)
153 if proc_turbostat
.returncode
!= 0:
154 print(f
'turbostat failed with {proc_turbostat.returncode}')
157 actual_columns
= proc_turbostat
.stdout
.split(b
'\n')[0].split(b
'\t')
158 check_columns_or_fail(expected_columns
, actual_columns
)
162 # Same, but with --debug
164 # We explicitly specify '--show CPU' to make sure turbostat
165 # don't show a bunch of default counters instead.
167 turbostat_argv
.append('--debug')
169 cmdline
= gen_user_friendly_cmdline(turbostat_argv
)
170 print(f
'Running turbostat (in debug mode) with:\n\t{cmdline}\n... ', end
= '', flush
= True)
171 proc_turbostat
= subprocess
.run(timeout_argv
+ turbostat_argv
, capture_output
= True)
172 if proc_turbostat
.returncode
!= 0:
173 print(f
'turbostat failed with {proc_turbostat.returncode}')
176 actual_columns
= proc_turbostat
.stdout
.split(b
'\n')[0].split(b
'\t')
177 check_columns_or_fail(expected_columns_debug
, actual_columns
)