amd-debug-tools 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of amd-debug-tools might be problematic. Click here for more details.
- amd_debug/__init__.py +45 -0
- amd_debug/acpi.py +107 -0
- amd_debug/bash/amd-s2idle +89 -0
- amd_debug/battery.py +87 -0
- amd_debug/bios.py +138 -0
- amd_debug/common.py +324 -0
- amd_debug/database.py +331 -0
- amd_debug/failures.py +588 -0
- amd_debug/installer.py +404 -0
- amd_debug/kernel.py +389 -0
- amd_debug/prerequisites.py +1215 -0
- amd_debug/pstate.py +314 -0
- amd_debug/s2idle-hook +72 -0
- amd_debug/s2idle.py +406 -0
- amd_debug/sleep_report.py +453 -0
- amd_debug/templates/html +427 -0
- amd_debug/templates/md +39 -0
- amd_debug/templates/stdout +13 -0
- amd_debug/templates/txt +23 -0
- amd_debug/validator.py +863 -0
- amd_debug/wake.py +111 -0
- amd_debug_tools-0.2.0.dist-info/METADATA +180 -0
- amd_debug_tools-0.2.0.dist-info/RECORD +27 -0
- amd_debug_tools-0.2.0.dist-info/WHEEL +5 -0
- amd_debug_tools-0.2.0.dist-info/entry_points.txt +4 -0
- amd_debug_tools-0.2.0.dist-info/licenses/LICENSE +19 -0
- amd_debug_tools-0.2.0.dist-info/top_level.txt +1 -0
amd_debug/pstate.py
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
"""CPPC triage script for AMD systems"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import argparse
|
|
7
|
+
import re
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
import pandas as pd
|
|
11
|
+
from tabulate import tabulate
|
|
12
|
+
from pyudev import Context
|
|
13
|
+
|
|
14
|
+
from amd_debug.common import (
|
|
15
|
+
AmdTool,
|
|
16
|
+
get_pretty_distro,
|
|
17
|
+
print_color,
|
|
18
|
+
read_file,
|
|
19
|
+
read_msr,
|
|
20
|
+
relaunch_sudo,
|
|
21
|
+
show_log_info,
|
|
22
|
+
version,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class MSR: # pylint: disable=too-few-public-methods
|
|
27
|
+
"""MSR addresses for CPPC"""
|
|
28
|
+
|
|
29
|
+
MSR_AMD_CPPC_CAP1 = 0xC00102B0
|
|
30
|
+
MSR_AMD_CPPC_ENABLE = 0xC00102B1
|
|
31
|
+
MSR_AMD_CPPC_CAP2 = 0xC00102B2
|
|
32
|
+
MSR_AMD_CPPC_REQ = 0xC00102B3
|
|
33
|
+
MSR_AMD_CPPC_STATUS = 0xC00102B4
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def amd_cppc_cap_lowest_perf(x):
|
|
37
|
+
"""Return the lowest performance value from the given input."""
|
|
38
|
+
return x & 0xFF
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def amd_cppc_cap_lownonlin_perf(x):
|
|
42
|
+
"""Return the lowest nonlinear performance value from the given input."""
|
|
43
|
+
return (x >> 8) & 0xFF
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def amd_cppc_cap_nominal_perf(x):
|
|
47
|
+
"""Return the nominal performance value from the given input."""
|
|
48
|
+
return (x >> 16) & 0xFF
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def amd_cppc_cap_highest_perf(x):
|
|
52
|
+
"""Return the highest performance value from the given input."""
|
|
53
|
+
return (x >> 24) & 0xFF
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def amd_cppc_max_perf(x):
|
|
57
|
+
"""Return the maximum performance value from the given input."""
|
|
58
|
+
return x & 0xFF
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def amd_cppc_min_perf(x):
|
|
62
|
+
"""Return the minimum performance value from the given input."""
|
|
63
|
+
return (x >> 8) & 0xFF
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def amd_cppc_des_perf(x):
|
|
67
|
+
"""Return the desired performance value from the given input."""
|
|
68
|
+
return (x >> 16) & 0xFF
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def amd_cppc_epp_perf(x):
|
|
72
|
+
"""Return the energy performance preference value from the given input."""
|
|
73
|
+
return (x >> 24) & 0xFF
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class AmdPstateTriage(AmdTool):
|
|
77
|
+
"""Class for handling the triage process"""
|
|
78
|
+
|
|
79
|
+
def __init__(self, logging):
|
|
80
|
+
log_prefix = "pstate" if logging else None
|
|
81
|
+
super().__init__(log_prefix)
|
|
82
|
+
relaunch_sudo()
|
|
83
|
+
|
|
84
|
+
pretty = get_pretty_distro()
|
|
85
|
+
print_color(f"{pretty}", "🐧")
|
|
86
|
+
|
|
87
|
+
self.context = Context()
|
|
88
|
+
|
|
89
|
+
def gather_amd_pstate_info(self):
|
|
90
|
+
"""Gather AMD Pstate global information"""
|
|
91
|
+
for f in ("status", "prefcore"):
|
|
92
|
+
p = os.path.join("/", "sys", "devices", "system", "cpu", "amd_pstate", f)
|
|
93
|
+
if os.path.exists(p):
|
|
94
|
+
print_color(f"'{f}':\t{read_file(p)}", "○")
|
|
95
|
+
|
|
96
|
+
def gather_kernel_info(self):
|
|
97
|
+
"""Gather kernel information"""
|
|
98
|
+
print_color(f"Kernel:\t{os.uname().release}", "🐧")
|
|
99
|
+
|
|
100
|
+
def gather_scheduler_info(self):
|
|
101
|
+
"""Gather information about the scheduler"""
|
|
102
|
+
procfs = os.path.join("/", "proc", "sys", "kernel", "sched_itmt_enabled")
|
|
103
|
+
debugfs = os.path.join(
|
|
104
|
+
"/", "sys", "kernel", "debug", "x86", "sched_itmt_enabled"
|
|
105
|
+
)
|
|
106
|
+
for p in [procfs, debugfs]:
|
|
107
|
+
if os.path.exists(p):
|
|
108
|
+
val = read_file(p)
|
|
109
|
+
print_color(f"ITMT:\t{val}", "🐧")
|
|
110
|
+
|
|
111
|
+
def gather_cpu_info(self):
|
|
112
|
+
"""Gather a dataframe of CPU information"""
|
|
113
|
+
|
|
114
|
+
df = pd.DataFrame(
|
|
115
|
+
columns=[
|
|
116
|
+
"CPU #",
|
|
117
|
+
"CPU Min Freq",
|
|
118
|
+
"CPU Nonlinear Freq",
|
|
119
|
+
"CPU Max Freq",
|
|
120
|
+
"Scaling Min Freq",
|
|
121
|
+
"Scaling Max Freq",
|
|
122
|
+
"Energy Performance Preference",
|
|
123
|
+
"Prefcore",
|
|
124
|
+
"Boost",
|
|
125
|
+
]
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
for device in self.context.list_devices(subsystem="cpu"):
|
|
129
|
+
p = os.path.join(device.sys_path, "cpufreq")
|
|
130
|
+
if not os.path.exists(p):
|
|
131
|
+
continue
|
|
132
|
+
row = [
|
|
133
|
+
int(re.findall(r"\d+", f"{device.sys_name}")[0]),
|
|
134
|
+
read_file(os.path.join(p, "cpuinfo_min_freq")),
|
|
135
|
+
read_file(os.path.join(p, "amd_pstate_lowest_nonlinear_freq")),
|
|
136
|
+
read_file(os.path.join(p, "cpuinfo_max_freq")),
|
|
137
|
+
read_file(os.path.join(p, "scaling_min_freq")),
|
|
138
|
+
read_file(os.path.join(p, "scaling_max_freq")),
|
|
139
|
+
read_file(os.path.join(p, "energy_performance_preference")),
|
|
140
|
+
read_file(os.path.join(p, "amd_pstate_prefcore_ranking")),
|
|
141
|
+
read_file(os.path.join(p, "boost")),
|
|
142
|
+
]
|
|
143
|
+
df = pd.concat(
|
|
144
|
+
[pd.DataFrame([row], columns=df.columns), df], ignore_index=True
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
cpuinfo = read_file("/proc/cpuinfo")
|
|
148
|
+
model = re.findall(r"model name\s+:\s+(.*)", cpuinfo)[0]
|
|
149
|
+
print_color(f"CPU:\t\t{model}", "💻")
|
|
150
|
+
|
|
151
|
+
df = df.sort_values(by="CPU #")
|
|
152
|
+
print_color(
|
|
153
|
+
"Per-CPU sysfs files\n%s"
|
|
154
|
+
% tabulate(df, headers="keys", tablefmt="psql", showindex=False),
|
|
155
|
+
"🔋",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def gather_msrs(self):
|
|
159
|
+
"""Gather MSR information"""
|
|
160
|
+
cpus = []
|
|
161
|
+
for device in self.context.list_devices(subsystem="cpu"):
|
|
162
|
+
cpu = int(re.findall(r"\d+", f"{device.sys_name}")[0])
|
|
163
|
+
cpus.append(cpu)
|
|
164
|
+
cpus.sort()
|
|
165
|
+
|
|
166
|
+
df = pd.DataFrame(
|
|
167
|
+
columns=[
|
|
168
|
+
"CPU #",
|
|
169
|
+
"Min Perf",
|
|
170
|
+
"Max Perf",
|
|
171
|
+
"Desired Perf",
|
|
172
|
+
"Energy Performance Perf",
|
|
173
|
+
]
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
msr_df = pd.DataFrame(
|
|
177
|
+
columns=[
|
|
178
|
+
"CPU #",
|
|
179
|
+
"Enable",
|
|
180
|
+
"Status",
|
|
181
|
+
"Cap 1",
|
|
182
|
+
"Cap 2",
|
|
183
|
+
"Request",
|
|
184
|
+
]
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
cap_df = pd.DataFrame(
|
|
188
|
+
columns=[
|
|
189
|
+
"CPU #",
|
|
190
|
+
"Lowest Perf",
|
|
191
|
+
"Nonlinear Perf",
|
|
192
|
+
"Nominal Perf",
|
|
193
|
+
"Highest Perf",
|
|
194
|
+
]
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
for cpu in cpus:
|
|
199
|
+
enable = read_msr(MSR.MSR_AMD_CPPC_ENABLE, cpu)
|
|
200
|
+
status = read_msr(MSR.MSR_AMD_CPPC_STATUS, cpu)
|
|
201
|
+
cap1 = read_msr(MSR.MSR_AMD_CPPC_CAP1, cpu)
|
|
202
|
+
cap2 = read_msr(MSR.MSR_AMD_CPPC_CAP2, cpu)
|
|
203
|
+
|
|
204
|
+
req = read_msr(MSR.MSR_AMD_CPPC_REQ, cpu)
|
|
205
|
+
row = [
|
|
206
|
+
cpu,
|
|
207
|
+
amd_cppc_min_perf(req),
|
|
208
|
+
amd_cppc_max_perf(req),
|
|
209
|
+
amd_cppc_des_perf(req),
|
|
210
|
+
amd_cppc_epp_perf(req),
|
|
211
|
+
]
|
|
212
|
+
df = pd.concat(
|
|
213
|
+
[pd.DataFrame([row], columns=df.columns), df], ignore_index=True
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
row = [
|
|
217
|
+
cpu,
|
|
218
|
+
enable,
|
|
219
|
+
status,
|
|
220
|
+
hex(cap1),
|
|
221
|
+
hex(cap2),
|
|
222
|
+
hex(req),
|
|
223
|
+
]
|
|
224
|
+
msr_df = pd.concat(
|
|
225
|
+
[pd.DataFrame([row], columns=msr_df.columns), msr_df],
|
|
226
|
+
ignore_index=True,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
row = [
|
|
230
|
+
cpu,
|
|
231
|
+
amd_cppc_cap_lowest_perf(cap1),
|
|
232
|
+
amd_cppc_cap_lownonlin_perf(cap1),
|
|
233
|
+
amd_cppc_cap_nominal_perf(cap1),
|
|
234
|
+
amd_cppc_cap_highest_perf(cap1),
|
|
235
|
+
]
|
|
236
|
+
cap_df = pd.concat(
|
|
237
|
+
[pd.DataFrame([row], columns=cap_df.columns), cap_df],
|
|
238
|
+
ignore_index=True,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
except FileNotFoundError:
|
|
242
|
+
print_color("Unable to check MSRs: MSR kernel module not loaded", "❌")
|
|
243
|
+
return False
|
|
244
|
+
except PermissionError:
|
|
245
|
+
print_color("MSR checks unavailable", "🚦")
|
|
246
|
+
return
|
|
247
|
+
|
|
248
|
+
msr_df = msr_df.sort_values(by="CPU #")
|
|
249
|
+
print_color(
|
|
250
|
+
"CPPC MSRs\n%s"
|
|
251
|
+
% tabulate(msr_df, headers="keys", tablefmt="psql", showindex=False),
|
|
252
|
+
"🔋",
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
cap_df = cap_df.sort_values(by="CPU #")
|
|
256
|
+
print_color(
|
|
257
|
+
"MSR_AMD_CPPC_CAP1 (decoded)\n%s"
|
|
258
|
+
% tabulate(cap_df, headers="keys", tablefmt="psql", showindex=False),
|
|
259
|
+
"🔋",
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
df = df.sort_values(by="CPU #")
|
|
263
|
+
print_color(
|
|
264
|
+
"MSR_AMD_CPPC_REQ (decoded)\n%s"
|
|
265
|
+
% tabulate(df, headers="keys", tablefmt="psql", showindex=False),
|
|
266
|
+
"🔋",
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
def run(self):
|
|
270
|
+
"""Run the triage process"""
|
|
271
|
+
self.gather_kernel_info()
|
|
272
|
+
self.gather_amd_pstate_info()
|
|
273
|
+
self.gather_scheduler_info()
|
|
274
|
+
try:
|
|
275
|
+
self.gather_cpu_info()
|
|
276
|
+
except FileNotFoundError:
|
|
277
|
+
print_color("Unable to gather CPU information", "❌")
|
|
278
|
+
return False
|
|
279
|
+
self.gather_msrs()
|
|
280
|
+
return True
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def parse_args():
|
|
284
|
+
"""Parse command line arguments."""
|
|
285
|
+
parser = argparse.ArgumentParser(
|
|
286
|
+
description="Collect useful information for debugging amd-pstate issues.",
|
|
287
|
+
epilog="Arguments are optional",
|
|
288
|
+
)
|
|
289
|
+
subparsers = parser.add_subparsers(help="Possible commands", dest="command")
|
|
290
|
+
triage_cmd = subparsers.add_parser("triage", help="Run amd-pstate triage")
|
|
291
|
+
triage_cmd.add_argument(
|
|
292
|
+
"--tool-debug",
|
|
293
|
+
action="store_true",
|
|
294
|
+
help="Enable tool debug logging",
|
|
295
|
+
)
|
|
296
|
+
subparsers.add_parser("version", help="Show version information")
|
|
297
|
+
if len(sys.argv) == 1:
|
|
298
|
+
parser.print_help(sys.stderr)
|
|
299
|
+
sys.exit(1)
|
|
300
|
+
return parser.parse_args()
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def main():
|
|
304
|
+
"""Main function"""
|
|
305
|
+
args = parse_args()
|
|
306
|
+
ret = False
|
|
307
|
+
if args.command == "version":
|
|
308
|
+
print(version())
|
|
309
|
+
return True
|
|
310
|
+
elif args.command == "triage":
|
|
311
|
+
triage = AmdPstateTriage(args.tool_debug)
|
|
312
|
+
ret = triage.run()
|
|
313
|
+
show_log_info()
|
|
314
|
+
return ret
|
amd_debug/s2idle-hook
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import logging
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main(action, debug):
|
|
10
|
+
"""Main function to run the systemd hook"""
|
|
11
|
+
try:
|
|
12
|
+
from amd_debug.validator import (
|
|
13
|
+
SleepValidator,
|
|
14
|
+
) # pylint: disable=import-outside-toplevel
|
|
15
|
+
from amd_debug.prerequisites import (
|
|
16
|
+
PrerequisiteValidator,
|
|
17
|
+
) # pylint: disable=import-outside-toplevel
|
|
18
|
+
except ModuleNotFoundError:
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
app = SleepValidator(tool_debug=debug, bios_debug=False)
|
|
22
|
+
if action == "pre":
|
|
23
|
+
prereq = PrerequisiteValidator(tool_debug=debug)
|
|
24
|
+
prereq.capture_once()
|
|
25
|
+
app.systemd_pre_hook()
|
|
26
|
+
elif action == "post":
|
|
27
|
+
app.systemd_post_hook()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def parse_args():
|
|
31
|
+
"""Parse command line arguments"""
|
|
32
|
+
parser = argparse.ArgumentParser(
|
|
33
|
+
description="amd-s2idle systemd hook",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"action",
|
|
38
|
+
choices=["pre", "post"],
|
|
39
|
+
help="Action to perform",
|
|
40
|
+
)
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"--debug",
|
|
43
|
+
action="store_true",
|
|
44
|
+
)
|
|
45
|
+
parser.add_argument(
|
|
46
|
+
"mode",
|
|
47
|
+
help="Mode to perform",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
parser.add_argument("--log", default="/dev/null")
|
|
51
|
+
parser.add_argument("--path", default="")
|
|
52
|
+
|
|
53
|
+
if len(sys.argv) == 1:
|
|
54
|
+
parser.print_help(sys.stderr)
|
|
55
|
+
sys.exit(1)
|
|
56
|
+
return parser.parse_args()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
args = parse_args()
|
|
61
|
+
|
|
62
|
+
if args.mode != "suspend":
|
|
63
|
+
sys.exit(0)
|
|
64
|
+
|
|
65
|
+
if args.path:
|
|
66
|
+
sys.path.append(args.path)
|
|
67
|
+
|
|
68
|
+
logging.basicConfig(
|
|
69
|
+
filename=args.log, level=logging.DEBUG if args.debug else logging.WARNING
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
main(args.action, args.debug)
|