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/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)