Skip to content

Commit 1f8add1

Browse files
Patryk Wlazlynlenb
authored andcommitted
tools/power turbostat: Add selftests for SMI, APERF and MPERF counters
The test requests BICs that are dependent on SMI, APERF and MPERF counters and checks if the columns show up in the output and the turbostat doesn't crash. Read the counters in both --no-msr and --no-perf mode. The test skips counters that are not present or user does not have permissions to read. It is not an error, but the test may not be as exhaustive. Signed-off-by: Patryk Wlazlyn <patryk.wlazlyn@linux.intel.com> Signed-off-by: Len Brown <len.brown@intel.com>
1 parent b2e4a5d commit 1f8add1

File tree

1 file changed

+157
-0
lines changed

1 file changed

+157
-0
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#!/bin/env python3
2+
# SPDX-License-Identifier: GPL-2.0
3+
4+
import subprocess
5+
from shutil import which
6+
from os import pread
7+
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..
12+
try:
13+
import ctypes
14+
15+
this_image = ctypes.CDLL(None)
16+
BASE_CPU = this_image.sched_getcpu()
17+
except:
18+
BASE_CPU = 0 # If we fail, set to 0 and pray it's not offline.
19+
20+
MSR_IA32_MPERF = 0x000000e7
21+
MSR_IA32_APERF = 0x000000e8
22+
23+
def check_perf_access():
24+
perf = which('perf')
25+
if perf is None:
26+
print('SKIP: Could not find perf binary, thus could not determine perf access.')
27+
return False
28+
29+
def has_perf_counter_access(counter_name):
30+
proc_perf = subprocess.run([perf, 'stat', '-e', counter_name, '--timeout', '10'],
31+
capture_output = True)
32+
33+
if proc_perf.returncode != 0:
34+
print(f'SKIP: Could not read {counter_name} perf counter, assuming no access.')
35+
return False
36+
37+
if b'<not supported>' in proc_perf.stderr:
38+
print(f'SKIP: Could not read {counter_name} perf counter, assuming no access.')
39+
return False
40+
41+
return True
42+
43+
if not has_perf_counter_access('msr/mperf/'):
44+
return False
45+
if not has_perf_counter_access('msr/aperf/'):
46+
return False
47+
if not has_perf_counter_access('msr/smi/'):
48+
return False
49+
50+
return True
51+
52+
def check_msr_access():
53+
try:
54+
file_msr = open(f'/dev/cpu/{BASE_CPU}/msr', 'rb')
55+
except:
56+
return False
57+
58+
if len(pread(file_msr.fileno(), 8, MSR_IA32_MPERF)) != 8:
59+
return False
60+
61+
if len(pread(file_msr.fileno(), 8, MSR_IA32_APERF)) != 8:
62+
return False
63+
64+
return True
65+
66+
has_perf_access = check_perf_access()
67+
has_msr_access = check_msr_access()
68+
69+
turbostat_counter_source_opts = ['']
70+
71+
if has_msr_access:
72+
turbostat_counter_source_opts.append('--no-perf')
73+
else:
74+
print('SKIP: doesn\'t have MSR access, skipping run with --no-perf')
75+
76+
if has_perf_access:
77+
turbostat_counter_source_opts.append('--no-msr')
78+
else:
79+
print('SKIP: doesn\'t have perf access, skipping run with --no-msr')
80+
81+
if not has_msr_access and not has_perf_access:
82+
print('SKIP: No MSR nor perf access detected. Skipping the tests entirely')
83+
exit(0)
84+
85+
turbostat = which('turbostat')
86+
if turbostat is None:
87+
print('Could not find turbostat binary')
88+
exit(1)
89+
90+
timeout = which('timeout')
91+
if timeout is None:
92+
print('Could not find timeout binary')
93+
exit(1)
94+
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}')
98+
exit(1)
99+
100+
EXPECTED_COLUMNS_DEBUG_DEFAULT = b'usec\tTime_Of_Day_Seconds\tAPIC\tX2APIC'
101+
102+
SMI_APERF_MPERF_DEPENDENT_BICS = [
103+
'SMI',
104+
'Avg_MHz',
105+
'Busy%',
106+
'Bzy_MHz',
107+
]
108+
if has_perf_access:
109+
SMI_APERF_MPERF_DEPENDENT_BICS.append('IPC')
110+
111+
for bic in SMI_APERF_MPERF_DEPENDENT_BICS:
112+
for counter_source_opt in turbostat_counter_source_opts:
113+
114+
# Ugly special case, but it is what it is..
115+
if counter_source_opt == '--no-perf' and bic == 'IPC':
116+
continue
117+
118+
expected_columns = bic.encode()
119+
expected_columns_debug = EXPECTED_COLUMNS_DEBUG_DEFAULT + f'\t{bic}'.encode()
120+
121+
#
122+
# Run turbostat for some time and send SIGINT
123+
#
124+
timeout_argv = [timeout, '--preserve-status', '-s', 'SIGINT', '-k', '3', '0.2s']
125+
turbostat_argv = [turbostat, '-i', '0.50', '--show', bic]
126+
127+
if counter_source_opt:
128+
turbostat_argv.append(counter_source_opt)
129+
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}')
134+
exit(1)
135+
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=}')
139+
exit(1)
140+
print('OK')
141+
142+
#
143+
# Same, but with --debug
144+
#
145+
turbostat_argv.append('--debug')
146+
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}')
151+
exit(1)
152+
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=}')
156+
exit(1)
157+
print('OK')

0 commit comments

Comments
 (0)