amd-debug-tools 0.2.0__tar.gz → 0.2.1__tar.gz
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.
- {amd_debug_tools-0.2.0/src/amd_debug_tools.egg-info → amd_debug_tools-0.2.1}/PKG-INFO +1 -1
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/pyproject.toml +19 -1
- amd_debug_tools-0.2.1/setup.cfg +4 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/common.py +24 -0
- amd_debug_tools-0.2.1/src/amd_debug/display.py +34 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/installer.py +36 -4
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/prerequisites.py +84 -46
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/s2idle.py +25 -10
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1/src/amd_debug_tools.egg-info}/PKG-INFO +1 -1
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug_tools.egg-info/SOURCES.txt +19 -17
- amd_debug_tools-0.2.1/src/amd_debug_tools.egg-info/top_level.txt +18 -0
- amd_debug_tools-0.2.1/src/launcher.py +35 -0
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_acpi.py +0 -4
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_batteries.py +0 -3
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_bios.py +0 -4
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_common.py +59 -3
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_database.py +0 -6
- amd_debug_tools-0.2.1/src/test_display.py +143 -0
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_failures.py +0 -3
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_installer.py +100 -6
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_kernel.py +0 -4
- amd_debug_tools-0.2.1/src/test_launcher.py +53 -0
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_prerequisites.py +52 -9
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_pstate.py +0 -5
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_s2idle.py +14 -17
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_sleep_report.py +0 -4
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_validator.py +0 -4
- {amd_debug_tools-0.2.0/test → amd_debug_tools-0.2.1/src}/test_wake.py +0 -4
- amd_debug_tools-0.2.0/setup.cfg +0 -23
- amd_debug_tools-0.2.0/src/amd_debug_tools.egg-info/top_level.txt +0 -1
- amd_debug_tools-0.2.0/test/test_launcher.py +0 -96
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/LICENSE +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/README.md +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/__init__.py +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/acpi.py +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/bash/amd-s2idle +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/battery.py +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/bios.py +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/database.py +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/failures.py +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/kernel.py +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/pstate.py +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/s2idle-hook +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/sleep_report.py +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/templates/html +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/templates/md +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/templates/stdout +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/templates/txt +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/validator.py +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug/wake.py +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug_tools.egg-info/dependency_links.txt +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug_tools.egg-info/entry_points.txt +0 -0
- {amd_debug_tools-0.2.0 → amd_debug_tools-0.2.1}/src/amd_debug_tools.egg-info/requires.txt +0 -0
|
@@ -2,9 +2,22 @@
|
|
|
2
2
|
requires = ["setuptools>=59.8.0", "setuptools-git-versioning>=2.0,<3"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
|
+
[tool.setuptools]
|
|
6
|
+
package-dir = {"" = "src"}
|
|
7
|
+
|
|
8
|
+
[tool.setuptools.package-data]
|
|
9
|
+
amd_debug = ["s2idle-hook"]
|
|
10
|
+
"amd_debug.templates" = ["*"]
|
|
11
|
+
"amd_debug.bash" = ["amd-s2idle"]
|
|
12
|
+
|
|
5
13
|
[tool.setuptools-git-versioning]
|
|
6
14
|
enabled = true
|
|
7
15
|
|
|
16
|
+
[tool.coverage.run]
|
|
17
|
+
branch = true
|
|
18
|
+
source = ["src"]
|
|
19
|
+
omit = ["src/launcher.py"]
|
|
20
|
+
|
|
8
21
|
[project]
|
|
9
22
|
name = "amd-debug-tools"
|
|
10
23
|
authors = [{ name = "Mario Limonciello", email = "superm1@kernel.org" }]
|
|
@@ -27,8 +40,13 @@ dependencies = [
|
|
|
27
40
|
"matplotlib",
|
|
28
41
|
"seaborn",
|
|
29
42
|
]
|
|
30
|
-
dynamic = ["version"
|
|
43
|
+
dynamic = ["version"]
|
|
31
44
|
license = "MIT"
|
|
32
45
|
|
|
33
46
|
[project.urls]
|
|
34
47
|
"Homepage" = "https://web.git.kernel.org/pub/scm/linux/kernel/git/superm1/amd-debug-tools.git/"
|
|
48
|
+
|
|
49
|
+
[project.scripts]
|
|
50
|
+
amd-s2idle = "amd_debug:amd_s2idle"
|
|
51
|
+
amd-bios = "amd_debug:amd_bios"
|
|
52
|
+
amd-pstate = "amd_debug:amd_pstate"
|
|
@@ -81,6 +81,15 @@ def print_color(message, group) -> None:
|
|
|
81
81
|
print(f"{prefix}{color}{message}{suffix}")
|
|
82
82
|
|
|
83
83
|
|
|
84
|
+
def colorize_choices(choices, default) -> str:
|
|
85
|
+
"""Output a list of choices with colors, where the default is highlighted"""
|
|
86
|
+
if default not in choices:
|
|
87
|
+
raise ValueError(f"Default choice '{default}' not in choices")
|
|
88
|
+
choices = [c for c in choices if c != default]
|
|
89
|
+
choices = [f"{Colors.OK}{default}{Colors.ENDC}"] + choices
|
|
90
|
+
return ", ".join(choices)
|
|
91
|
+
|
|
92
|
+
|
|
84
93
|
def fatal_error(message):
|
|
85
94
|
"""Prints a fatal error message and exits"""
|
|
86
95
|
_configure_log(None)
|
|
@@ -88,6 +97,21 @@ def fatal_error(message):
|
|
|
88
97
|
sys.exit(1)
|
|
89
98
|
|
|
90
99
|
|
|
100
|
+
def apply_prefix_wrapper(header, message):
|
|
101
|
+
"""Apply a prefix to wrap a newline delimitted message"""
|
|
102
|
+
s = f"{header.strip()}\n"
|
|
103
|
+
lines = message.strip().split("\n")
|
|
104
|
+
for i, line in enumerate(lines):
|
|
105
|
+
line = line.strip()
|
|
106
|
+
if not line:
|
|
107
|
+
continue
|
|
108
|
+
if i == len(lines) - 1:
|
|
109
|
+
s += f"└─ {line}\n"
|
|
110
|
+
continue
|
|
111
|
+
s += f"│ {line}\n"
|
|
112
|
+
return s
|
|
113
|
+
|
|
114
|
+
|
|
91
115
|
def show_log_info():
|
|
92
116
|
"""Show log information"""
|
|
93
117
|
logger = logging.getLogger()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
"""Display analysis"""
|
|
4
|
+
import os
|
|
5
|
+
from pyudev import Context
|
|
6
|
+
|
|
7
|
+
from amd_debug.common import read_file
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Display:
|
|
11
|
+
"""Display analysis"""
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.pyudev = Context()
|
|
15
|
+
self.edid = {}
|
|
16
|
+
|
|
17
|
+
for dev in self.pyudev.list_devices(subsystem="drm"):
|
|
18
|
+
if not "card" in dev.device_path:
|
|
19
|
+
continue
|
|
20
|
+
p = os.path.join(dev.sys_path, "status")
|
|
21
|
+
if not os.path.exists(p):
|
|
22
|
+
continue
|
|
23
|
+
f = read_file(p)
|
|
24
|
+
if f != "connected":
|
|
25
|
+
continue
|
|
26
|
+
p = os.path.join(dev.sys_path, "enabled")
|
|
27
|
+
f = read_file(p)
|
|
28
|
+
if f != "enabled":
|
|
29
|
+
continue
|
|
30
|
+
self.edid[dev.sys_name] = os.path.join(dev.sys_path, "edid")
|
|
31
|
+
|
|
32
|
+
def get_edid(self) -> list:
|
|
33
|
+
"""Get the path for EDID data for all connected displays"""
|
|
34
|
+
return self.edid
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/python3
|
|
2
2
|
# SPDX-License-Identifier: MIT
|
|
3
3
|
|
|
4
|
+
"""
|
|
5
|
+
This module contains installer support for amd-debug-tools.
|
|
6
|
+
"""
|
|
7
|
+
|
|
4
8
|
import argparse
|
|
5
9
|
import os
|
|
6
10
|
import shutil
|
|
@@ -21,6 +25,7 @@ class Headers: # pylint: disable=too-few-public-methods
|
|
|
21
25
|
"""Headers for the script"""
|
|
22
26
|
|
|
23
27
|
MissingIasl = "ACPI extraction tool `iasl` is missing"
|
|
28
|
+
MissingEdidDecode = "EDID decoding tool `edid-decode` is missing"
|
|
24
29
|
MissingEthtool = "Ethtool is missing"
|
|
25
30
|
InstallAction = "Attempting to install"
|
|
26
31
|
MissingFwupd = "Firmware update library `fwupd` is missing"
|
|
@@ -171,6 +176,18 @@ class EthtoolPackage(DistroPackage):
|
|
|
171
176
|
)
|
|
172
177
|
|
|
173
178
|
|
|
179
|
+
class EdidDecodePackage(DistroPackage):
|
|
180
|
+
"""Edid-Decode package"""
|
|
181
|
+
|
|
182
|
+
def __init__(self):
|
|
183
|
+
super().__init__(
|
|
184
|
+
deb="edid-decode",
|
|
185
|
+
rpm="edid-decode",
|
|
186
|
+
arch=None,
|
|
187
|
+
message=Headers.MissingEdidDecode,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
174
191
|
class FwupdPackage(DistroPackage):
|
|
175
192
|
"""Fwupd package"""
|
|
176
193
|
|
|
@@ -202,14 +219,14 @@ class Installer(AmdTool):
|
|
|
202
219
|
# test if fwupd can report device firmware versions
|
|
203
220
|
try:
|
|
204
221
|
import gi # pylint: disable=import-outside-toplevel
|
|
205
|
-
from gi.repository import (
|
|
222
|
+
from gi.repository import ( # pylint: disable=import-outside-toplevel
|
|
206
223
|
GLib as _,
|
|
207
|
-
)
|
|
224
|
+
)
|
|
208
225
|
|
|
209
226
|
gi.require_version("Fwupd", "2.0")
|
|
210
|
-
from gi.repository import (
|
|
227
|
+
from gi.repository import ( # pylint: disable=import-outside-toplevel
|
|
211
228
|
Fwupd as _,
|
|
212
|
-
)
|
|
229
|
+
)
|
|
213
230
|
|
|
214
231
|
self.fwupd = True
|
|
215
232
|
except ImportError:
|
|
@@ -244,6 +261,20 @@ class Installer(AmdTool):
|
|
|
244
261
|
package = EthtoolPackage()
|
|
245
262
|
if not package.install():
|
|
246
263
|
return False
|
|
264
|
+
if "edid-decode" in self.requirements:
|
|
265
|
+
try:
|
|
266
|
+
edid = (
|
|
267
|
+
subprocess.call(
|
|
268
|
+
["edid-decode", "--help"], stdout=subprocess.DEVNULL
|
|
269
|
+
)
|
|
270
|
+
== 255
|
|
271
|
+
)
|
|
272
|
+
except FileNotFoundError:
|
|
273
|
+
edid = False
|
|
274
|
+
if not edid:
|
|
275
|
+
package = EdidDecodePackage()
|
|
276
|
+
if not package.install():
|
|
277
|
+
return False
|
|
247
278
|
if "fwupd" in self.requirements and not self.fwupd:
|
|
248
279
|
package = FwupdPackage()
|
|
249
280
|
if not package.install():
|
|
@@ -396,6 +427,7 @@ def install_dep_superset() -> bool:
|
|
|
396
427
|
"pandas",
|
|
397
428
|
"seaborn",
|
|
398
429
|
"tabulate",
|
|
430
|
+
"edid-decode",
|
|
399
431
|
)
|
|
400
432
|
ret = tool.install_dependencies()
|
|
401
433
|
if ret:
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/python3
|
|
2
2
|
# SPDX-License-Identifier: MIT
|
|
3
3
|
|
|
4
|
+
"""
|
|
5
|
+
This module contains the s0i3 prerequisite validator for amd-debug-tools.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import configparser
|
|
4
9
|
import logging
|
|
5
10
|
import os
|
|
6
11
|
import platform
|
|
@@ -9,13 +14,16 @@ import shutil
|
|
|
9
14
|
import subprocess
|
|
10
15
|
import tempfile
|
|
11
16
|
import struct
|
|
12
|
-
import pyudev
|
|
13
17
|
from datetime import datetime
|
|
14
18
|
from packaging import version
|
|
15
19
|
|
|
20
|
+
import pyudev
|
|
21
|
+
|
|
16
22
|
from amd_debug.wake import WakeIRQ
|
|
23
|
+
from amd_debug.display import Display
|
|
17
24
|
from amd_debug.kernel import get_kernel_log, SystemdLogger, DmesgLogger
|
|
18
25
|
from amd_debug.common import (
|
|
26
|
+
apply_prefix_wrapper,
|
|
19
27
|
BIT,
|
|
20
28
|
clear_temporary_message,
|
|
21
29
|
get_distro,
|
|
@@ -111,12 +119,37 @@ class PrerequisiteValidator(AmdTool):
|
|
|
111
119
|
self.irqs = []
|
|
112
120
|
self.smu_version = ""
|
|
113
121
|
self.smu_program = ""
|
|
122
|
+
self.display = Display()
|
|
114
123
|
|
|
115
124
|
def capture_once(self):
|
|
116
125
|
"""Capture the prerequisites once"""
|
|
117
126
|
if not self.db.get_last_prereq_ts():
|
|
118
127
|
self.run()
|
|
119
128
|
|
|
129
|
+
def capture_edid(self):
|
|
130
|
+
"""Capture and decode the EDID data"""
|
|
131
|
+
edids = self.display.get_edid()
|
|
132
|
+
if len(edids) == 0:
|
|
133
|
+
self.db.record_debug("No EDID data found")
|
|
134
|
+
return True
|
|
135
|
+
|
|
136
|
+
for name, p in edids.items():
|
|
137
|
+
try:
|
|
138
|
+
cmd = ["edid-decode", p]
|
|
139
|
+
output = subprocess.check_output(cmd, stderr=subprocess.DEVNULL).decode(
|
|
140
|
+
"utf-8"
|
|
141
|
+
)
|
|
142
|
+
except FileNotFoundError:
|
|
143
|
+
self.db.record_prereq(
|
|
144
|
+
"edid-decode not installed, unable to decode EDID", "👀"
|
|
145
|
+
)
|
|
146
|
+
return True
|
|
147
|
+
except subprocess.CalledProcessError as e:
|
|
148
|
+
self.db.record_prereq(f"Failed to capture EDID table: {e.output}", "👀")
|
|
149
|
+
return False
|
|
150
|
+
self.db.record_debug(apply_prefix_wrapper(f"EDID for {name}:", output))
|
|
151
|
+
return True
|
|
152
|
+
|
|
120
153
|
def check_amdgpu(self):
|
|
121
154
|
"""Check for the AMDGPU driver"""
|
|
122
155
|
for device in self.pyudev.list_devices(subsystem="pci"):
|
|
@@ -167,10 +200,10 @@ class PrerequisiteValidator(AmdTool):
|
|
|
167
200
|
if self.kernel_log.match_pattern("ath11k_pci.*wcn6855"):
|
|
168
201
|
match = self.kernel_log.match_pattern("ath11k_pci.*fw_version")
|
|
169
202
|
if match:
|
|
170
|
-
self.db.record_debug("WCN6855 version string:
|
|
203
|
+
self.db.record_debug(f"WCN6855 version string: {match}")
|
|
171
204
|
objects = match.split()
|
|
172
|
-
for i in
|
|
173
|
-
if
|
|
205
|
+
for i, obj in enumerate(objects):
|
|
206
|
+
if obj == "fw_build_id":
|
|
174
207
|
wcn6855 = objects[i + 1]
|
|
175
208
|
|
|
176
209
|
if wcn6855:
|
|
@@ -346,6 +379,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
346
379
|
|
|
347
380
|
def check_usb3(self):
|
|
348
381
|
"""Check for the USB4 controller"""
|
|
382
|
+
slots = []
|
|
349
383
|
for device in self.pyudev.list_devices(subsystem="pci", PCI_CLASS="C0330"):
|
|
350
384
|
slot = device.properties["PCI_SLOT_NAME"]
|
|
351
385
|
if device.properties.get("DRIVER") != "xhci_hcd":
|
|
@@ -354,18 +388,27 @@ class PrerequisiteValidator(AmdTool):
|
|
|
354
388
|
)
|
|
355
389
|
self.failures += [MissingXhciHcd()]
|
|
356
390
|
return False
|
|
357
|
-
|
|
391
|
+
slots += [slot]
|
|
392
|
+
if slots:
|
|
393
|
+
self.db.record_prereq(
|
|
394
|
+
f"USB3 driver `xhci_hcd` bound to {', '.join(slots)}", "✅"
|
|
395
|
+
)
|
|
358
396
|
return True
|
|
359
397
|
|
|
360
398
|
def check_usb4(self):
|
|
361
399
|
"""Check if the thunderbolt driver is loaded"""
|
|
400
|
+
slots = []
|
|
362
401
|
for device in self.pyudev.list_devices(subsystem="pci", PCI_CLASS="C0340"):
|
|
363
402
|
slot = device.properties["PCI_SLOT_NAME"]
|
|
364
403
|
if device.properties.get("DRIVER") != "thunderbolt":
|
|
365
404
|
self.db.record_prereq("USB4 driver `thunderbolt` missing", "❌")
|
|
366
405
|
self.failures += [MissingThunderbolt()]
|
|
367
406
|
return False
|
|
368
|
-
|
|
407
|
+
slots += [slot]
|
|
408
|
+
if slots:
|
|
409
|
+
self.db.record_prereq(
|
|
410
|
+
f"USB4 driver `thunderbolt` bound to {', '.join(slots)}", "✅"
|
|
411
|
+
)
|
|
369
412
|
return True
|
|
370
413
|
|
|
371
414
|
def check_sleep_mode(self):
|
|
@@ -478,8 +521,8 @@ class PrerequisiteValidator(AmdTool):
|
|
|
478
521
|
continue
|
|
479
522
|
if self.cpu_family and self.cpu_model and self.cpu_model_string:
|
|
480
523
|
self.db.record_prereq(
|
|
481
|
-
"
|
|
482
|
-
|
|
524
|
+
f"{self.cpu_model_string} "
|
|
525
|
+
f"(family {self.cpu_family:x} model {self.cpu_model:x})",
|
|
483
526
|
"💻",
|
|
484
527
|
)
|
|
485
528
|
break
|
|
@@ -487,7 +530,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
487
530
|
|
|
488
531
|
# See https://github.com/torvalds/linux/commit/ec6c0503190417abf8b8f8e3e955ae583a4e50d4
|
|
489
532
|
def check_fadt(self):
|
|
490
|
-
"""Check the kernel emitted a message
|
|
533
|
+
"""Check the kernel emitted a message indicating FADT had a bit set."""
|
|
491
534
|
found = False
|
|
492
535
|
if not self.kernel_log:
|
|
493
536
|
message = "Unable to test FADT from kernel log"
|
|
@@ -575,10 +618,10 @@ class PrerequisiteValidator(AmdTool):
|
|
|
575
618
|
"""Check if the user has permissions to write to /sys/power/state"""
|
|
576
619
|
p = os.path.join("/", "sys", "power", "state")
|
|
577
620
|
try:
|
|
578
|
-
with open(p, "w") as
|
|
621
|
+
with open(p, "w", encoding="utf-8") as _w:
|
|
579
622
|
pass
|
|
580
623
|
except PermissionError:
|
|
581
|
-
self.db.record_prereq("
|
|
624
|
+
self.db.record_prereq(f"{Headers.RootError}", "👀")
|
|
582
625
|
return False
|
|
583
626
|
except FileNotFoundError:
|
|
584
627
|
self.db.record_prereq("Kernel doesn't support power management", "❌")
|
|
@@ -608,7 +651,8 @@ class PrerequisiteValidator(AmdTool):
|
|
|
608
651
|
)
|
|
609
652
|
if show_warning:
|
|
610
653
|
self.db.record_prereq(
|
|
611
|
-
"Timer based wakeup doesn't work properly for your
|
|
654
|
+
"Timer based wakeup doesn't work properly for your "
|
|
655
|
+
"ASIC/firmware, please manually wake the system",
|
|
612
656
|
"🚦",
|
|
613
657
|
)
|
|
614
658
|
return True
|
|
@@ -631,7 +675,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
631
675
|
for line in contents.split("\n"):
|
|
632
676
|
if "WAKE_INT_MASTER_REG:" in line:
|
|
633
677
|
val = "en" if int(line.split()[1], 16) & BIT(15) else "dis"
|
|
634
|
-
self.db.record_debug("Windows GPIO 0 debounce:
|
|
678
|
+
self.db.record_debug(f"Windows GPIO 0 debounce: {val}abled")
|
|
635
679
|
continue
|
|
636
680
|
if not header and re.search("trigger", line):
|
|
637
681
|
debug_str += line + "\n"
|
|
@@ -671,7 +715,9 @@ class PrerequisiteValidator(AmdTool):
|
|
|
671
715
|
self.db.record_prereq(f"{interface} has WoL enabled", "✅")
|
|
672
716
|
else:
|
|
673
717
|
self.db.record_prereq(
|
|
674
|
-
|
|
718
|
+
"Platform may have low hardware sleep residency "
|
|
719
|
+
"with Wake-on-lan disabled. Run `ethtool -s "
|
|
720
|
+
f"{interface} wol g` to enable it if necessary.",
|
|
675
721
|
"🚦",
|
|
676
722
|
)
|
|
677
723
|
return True
|
|
@@ -726,7 +772,8 @@ class PrerequisiteValidator(AmdTool):
|
|
|
726
772
|
# Dictionary of instance id to firmware version mappings that
|
|
727
773
|
# have been "reported" to be problematic
|
|
728
774
|
device_map = {
|
|
729
|
-
|
|
775
|
+
# https://gitlab.freedesktop.org/drm/amd/-/issues/3443
|
|
776
|
+
"8c36f7ee-cc11-4a36-b090-6363f54ecac2": "0.1.26",
|
|
730
777
|
}
|
|
731
778
|
interesting_plugins = ["nvme", "tpm", "uefi_capsule"]
|
|
732
779
|
if device.get_plugin() in interesting_plugins:
|
|
@@ -743,7 +790,8 @@ class PrerequisiteValidator(AmdTool):
|
|
|
743
790
|
item in device.get_guids() or item in device.get_instance_ids()
|
|
744
791
|
) and ver in device.get_version():
|
|
745
792
|
self.db.record_prereq(
|
|
746
|
-
|
|
793
|
+
"Platform may have problems resuming. Upgrade the "
|
|
794
|
+
f"firmware for '{device.get_name()}' if you have problems.",
|
|
747
795
|
"🚦",
|
|
748
796
|
)
|
|
749
797
|
return True
|
|
@@ -792,12 +840,13 @@ class PrerequisiteValidator(AmdTool):
|
|
|
792
840
|
else:
|
|
793
841
|
acpi_hid = ""
|
|
794
842
|
# set prefix if last device
|
|
795
|
-
prefix = "
|
|
796
|
-
debug_str += "{prefix}{name} [{acpi_hid}] : {acpi_path}\n"
|
|
797
|
-
prefix=prefix, name=name, acpi_hid=acpi_hid, acpi_path=acpi_path
|
|
798
|
-
)
|
|
843
|
+
prefix = "│ " if dev != devices[-1] else "└─"
|
|
844
|
+
debug_str += f"{prefix}{name} [{acpi_hid}] : {acpi_path}\n"
|
|
799
845
|
if "IDEA5002" in name:
|
|
800
|
-
remediation =
|
|
846
|
+
remediation = (
|
|
847
|
+
f"echo {parent.sys_path.split('/')[-1]} | "
|
|
848
|
+
f"sudo tee /sys/bus/i2c/drivers/{parent.driver}/unbind"
|
|
849
|
+
)
|
|
801
850
|
|
|
802
851
|
self.db.record_prereq(f"{name} may cause spurious wakeups", "❌")
|
|
803
852
|
self.failures += [I2CHidBug(name, remediation)]
|
|
@@ -834,22 +883,13 @@ class PrerequisiteValidator(AmdTool):
|
|
|
834
883
|
if os.path.exists(p):
|
|
835
884
|
acpi = read_file(p)
|
|
836
885
|
debug_str += (
|
|
837
|
-
"{prefix}{pci_slot_name} :
|
|
838
|
-
|
|
839
|
-
pci_slot_name=pci_slot_name,
|
|
840
|
-
vendor=database_vendor,
|
|
841
|
-
cls=database_class,
|
|
842
|
-
id=pci_id,
|
|
843
|
-
acpi=acpi,
|
|
844
|
-
)
|
|
886
|
+
f"{prefix}{pci_slot_name} : "
|
|
887
|
+
f"{database_vendor} {database_class} [{pci_id}] : {acpi}\n"
|
|
845
888
|
)
|
|
846
889
|
else:
|
|
847
|
-
debug_str +=
|
|
848
|
-
prefix
|
|
849
|
-
|
|
850
|
-
pci_slot_name=pci_slot_name,
|
|
851
|
-
cls=database_class,
|
|
852
|
-
id=pci_id,
|
|
890
|
+
debug_str += (
|
|
891
|
+
f"{prefix}{pci_slot_name} : "
|
|
892
|
+
f"{database_vendor} {database_class} [{pci_id}]\n"
|
|
853
893
|
)
|
|
854
894
|
if debug_str:
|
|
855
895
|
self.db.record_debug(debug_str)
|
|
@@ -908,7 +948,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
908
948
|
stdout=subprocess.DEVNULL,
|
|
909
949
|
stderr=subprocess.DEVNULL,
|
|
910
950
|
)
|
|
911
|
-
self.db.record_debug_file("
|
|
951
|
+
self.db.record_debug_file(f"{prefix}.dsl")
|
|
912
952
|
except subprocess.CalledProcessError as e:
|
|
913
953
|
self.db.record_prereq(
|
|
914
954
|
f"Failed to capture ACPI table: {e.output}", "👀"
|
|
@@ -925,10 +965,10 @@ class PrerequisiteValidator(AmdTool):
|
|
|
925
965
|
self.db.record_prereq(desc, "🔋")
|
|
926
966
|
|
|
927
967
|
def capture_logind(self):
|
|
968
|
+
"""Capture logind.conf settings"""
|
|
928
969
|
base = os.path.join("/", "etc", "systemd", "logind.conf")
|
|
929
970
|
if not os.path.exists(base):
|
|
930
971
|
return True
|
|
931
|
-
import configparser
|
|
932
972
|
|
|
933
973
|
config = configparser.ConfigParser()
|
|
934
974
|
config.read(base)
|
|
@@ -982,7 +1022,8 @@ class PrerequisiteValidator(AmdTool):
|
|
|
982
1022
|
_, cpu_count, _, _ = read_cpuid(0, 0x80000026, 1)
|
|
983
1023
|
if cpu_count > max_cpus:
|
|
984
1024
|
self.db.record_prereq(
|
|
985
|
-
f"The kernel has been limited to {max_cpus} CPU cores,
|
|
1025
|
+
f"The kernel has been limited to {max_cpus} CPU cores, "
|
|
1026
|
+
f"but the system has {cpu_count} cores",
|
|
986
1027
|
"❌",
|
|
987
1028
|
)
|
|
988
1029
|
self.failures += [LimitedCores(cpu_count, max_cpus)]
|
|
@@ -996,12 +1037,6 @@ class PrerequisiteValidator(AmdTool):
|
|
|
996
1037
|
except PermissionError:
|
|
997
1038
|
self.db.record_prereq("CPUID checks unavailable", "🚦")
|
|
998
1039
|
|
|
999
|
-
if valid:
|
|
1000
|
-
self.db.record_prereq(
|
|
1001
|
-
f"{self.cpu_model_string} (family {self.cpu_family:x} model {self.cpu_model:x})",
|
|
1002
|
-
"✅",
|
|
1003
|
-
)
|
|
1004
|
-
|
|
1005
1040
|
return True
|
|
1006
1041
|
|
|
1007
1042
|
def check_msr(self):
|
|
@@ -1020,7 +1055,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1020
1055
|
if not check_bits(val, expect_val):
|
|
1021
1056
|
self.failures += [MSRFailure()]
|
|
1022
1057
|
return False
|
|
1023
|
-
|
|
1058
|
+
self.db.record_prereq("PC6 and CC6 enabled", "✅")
|
|
1024
1059
|
except FileNotFoundError:
|
|
1025
1060
|
self.db.record_prereq(
|
|
1026
1061
|
"Unable to check MSRs: MSR kernel module not loaded", "❌"
|
|
@@ -1111,7 +1146,9 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1111
1146
|
if "pcie_port_pm=off" in cmdline:
|
|
1112
1147
|
return True
|
|
1113
1148
|
self.db.record_prereq(
|
|
1114
|
-
"Platform may hang resuming.
|
|
1149
|
+
"Platform may hang resuming. "
|
|
1150
|
+
"Upgrade your firmware or add pcie_port_pm=off to kernel command "
|
|
1151
|
+
"line if you have problems.",
|
|
1115
1152
|
"🚦",
|
|
1116
1153
|
)
|
|
1117
1154
|
return False
|
|
@@ -1139,6 +1176,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1139
1176
|
self.capture_linux_firmware,
|
|
1140
1177
|
self.capture_logind,
|
|
1141
1178
|
self.capture_pci_acpi,
|
|
1179
|
+
self.capture_edid,
|
|
1142
1180
|
]
|
|
1143
1181
|
checks = []
|
|
1144
1182
|
|
|
@@ -8,7 +8,14 @@ import subprocess
|
|
|
8
8
|
import sqlite3
|
|
9
9
|
|
|
10
10
|
from datetime import date, timedelta, datetime
|
|
11
|
-
from amd_debug.common import
|
|
11
|
+
from amd_debug.common import (
|
|
12
|
+
colorize_choices,
|
|
13
|
+
is_root,
|
|
14
|
+
relaunch_sudo,
|
|
15
|
+
show_log_info,
|
|
16
|
+
version,
|
|
17
|
+
running_ssh,
|
|
18
|
+
)
|
|
12
19
|
|
|
13
20
|
from amd_debug.validator import SleepValidator
|
|
14
21
|
from amd_debug.installer import Installer
|
|
@@ -51,12 +58,14 @@ def display_report_file(fname, fmt) -> None:
|
|
|
51
58
|
return
|
|
52
59
|
user = os.environ.get("SUDO_USER")
|
|
53
60
|
if user:
|
|
54
|
-
# ensure that xdg tools will know how to display the file
|
|
61
|
+
# ensure that xdg tools will know how to display the file
|
|
62
|
+
# (user may need to call tool with sudo -E)
|
|
55
63
|
if os.environ.get("XDG_SESSION_TYPE"):
|
|
56
64
|
subprocess.call(["sudo", "-E", "-u", user, "xdg-open", fname])
|
|
57
65
|
else:
|
|
58
66
|
print(
|
|
59
|
-
|
|
67
|
+
"To display report automatically in browser launch tool "
|
|
68
|
+
f"with '-E' argument (Example: sudo -E {sys.argv[0]})"
|
|
60
69
|
)
|
|
61
70
|
|
|
62
71
|
|
|
@@ -98,7 +107,9 @@ def prompt_report_arguments(since, until, fname, fmt) -> str:
|
|
|
98
107
|
sys.exit(f"Invalid date, use YYYY-MM-DD: {e}")
|
|
99
108
|
|
|
100
109
|
if not fmt:
|
|
101
|
-
fmt = input(
|
|
110
|
+
fmt = input(
|
|
111
|
+
f"{Headers.FormatDescription} ({colorize_choices(Defaults.format_choices, get_report_format())})? "
|
|
112
|
+
)
|
|
102
113
|
if not fmt:
|
|
103
114
|
fmt = get_report_format()
|
|
104
115
|
if fmt not in Defaults.format_choices:
|
|
@@ -176,12 +187,12 @@ def report(since, until, fname, fmt, tool_debug, report_debug) -> bool:
|
|
|
176
187
|
return True
|
|
177
188
|
|
|
178
189
|
|
|
179
|
-
def
|
|
190
|
+
def run_test_cycle(
|
|
180
191
|
duration, wait, count, fmt, fname, force, debug, rand, logind, bios_debug
|
|
181
192
|
) -> bool:
|
|
182
193
|
"""Run a test"""
|
|
183
194
|
app = Installer(tool_debug=debug)
|
|
184
|
-
app.set_requirements("iasl", "ethtool")
|
|
195
|
+
app.set_requirements("iasl", "ethtool", "edid-decode")
|
|
185
196
|
if not app.install_dependencies():
|
|
186
197
|
print("Failed to install dependencies")
|
|
187
198
|
return False
|
|
@@ -232,7 +243,7 @@ def test(
|
|
|
232
243
|
def install(debug) -> None:
|
|
233
244
|
"""Install the tool"""
|
|
234
245
|
installer = Installer(tool_debug=debug)
|
|
235
|
-
installer.set_requirements("iasl", "ethtool")
|
|
246
|
+
installer.set_requirements("iasl", "ethtool", "edid-decode")
|
|
236
247
|
if not installer.install_dependencies():
|
|
237
248
|
sys.exit("Failed to install dependencies")
|
|
238
249
|
try:
|
|
@@ -259,7 +270,8 @@ def parse_args():
|
|
|
259
270
|
"""Parse command line arguments"""
|
|
260
271
|
parser = argparse.ArgumentParser(
|
|
261
272
|
description="Swiss army knife for analyzing Linux s2idle problems",
|
|
262
|
-
epilog="The tool can run an immediate test with the 'test' command
|
|
273
|
+
epilog="The tool can run an immediate test with the 'test' command "
|
|
274
|
+
"or can be used to hook into systemd for building reports later.\n"
|
|
263
275
|
"All optional arguments will be prompted if needed.\n"
|
|
264
276
|
"To use non-interactively, please populate all optional arguments.",
|
|
265
277
|
)
|
|
@@ -282,7 +294,10 @@ def parse_args():
|
|
|
282
294
|
test_cmd.add_argument(
|
|
283
295
|
"--random",
|
|
284
296
|
action="store_true",
|
|
285
|
-
help=
|
|
297
|
+
help=(
|
|
298
|
+
"Run sleep cycles for random durations and wait, using the "
|
|
299
|
+
"--duration and --wait arguments as an upper bound",
|
|
300
|
+
),
|
|
286
301
|
)
|
|
287
302
|
test_cmd.add_argument(
|
|
288
303
|
"--force",
|
|
@@ -385,7 +400,7 @@ def main():
|
|
|
385
400
|
)
|
|
386
401
|
elif args.action == "test":
|
|
387
402
|
relaunch_sudo()
|
|
388
|
-
ret =
|
|
403
|
+
ret = run_test_cycle(
|
|
389
404
|
args.duration,
|
|
390
405
|
args.wait,
|
|
391
406
|
args.count,
|