amd-debug-tools 0.2.7__py3-none-any.whl → 0.2.8__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/common.py +41 -11
- amd_debug/prerequisites.py +21 -3
- amd_debug/sleep_report.py +1 -1
- amd_debug/ttm.py +1 -0
- amd_debug_tools-0.2.8.dist-info/METADATA +75 -0
- {amd_debug_tools-0.2.7.dist-info → amd_debug_tools-0.2.8.dist-info}/RECORD +12 -12
- test_common.py +76 -51
- test_prerequisites.py +73 -0
- amd_debug_tools-0.2.7.dist-info/METADATA +0 -184
- {amd_debug_tools-0.2.7.dist-info → amd_debug_tools-0.2.8.dist-info}/WHEEL +0 -0
- {amd_debug_tools-0.2.7.dist-info → amd_debug_tools-0.2.8.dist-info}/entry_points.txt +0 -0
- {amd_debug_tools-0.2.7.dist-info → amd_debug_tools-0.2.8.dist-info}/licenses/LICENSE +0 -0
- {amd_debug_tools-0.2.7.dist-info → amd_debug_tools-0.2.8.dist-info}/top_level.txt +0 -0
amd_debug/common.py
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
This module contains common utility functions and classes for various amd-debug-tools.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import asyncio
|
|
8
9
|
import importlib.metadata
|
|
9
10
|
import logging
|
|
10
11
|
import os
|
|
@@ -47,7 +48,7 @@ def get_group_color(group) -> str:
|
|
|
47
48
|
color = Colors.WARNING
|
|
48
49
|
elif group == "🗣️":
|
|
49
50
|
color = Colors.HEADER
|
|
50
|
-
elif group
|
|
51
|
+
elif any(mk in group for mk in ["💯", "🚫"]):
|
|
51
52
|
color = Colors.UNDERLINE
|
|
52
53
|
elif any(mk in group for mk in ["🦟", "🖴"]):
|
|
53
54
|
color = Colors.DEBUG
|
|
@@ -238,18 +239,47 @@ def gb_to_pages(gb_value):
|
|
|
238
239
|
|
|
239
240
|
def reboot():
|
|
240
241
|
"""Reboot the system"""
|
|
241
|
-
try:
|
|
242
|
-
import dbus # pylint: disable=import-outside-toplevel
|
|
243
242
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
243
|
+
async def reboot_dbus_fast():
|
|
244
|
+
"""Reboot using dbus-fast"""
|
|
245
|
+
try:
|
|
246
|
+
from dbus_fast.aio import ( # pylint: disable=import-outside-toplevel
|
|
247
|
+
MessageBus,
|
|
248
|
+
)
|
|
249
|
+
from dbus_fast import BusType # pylint: disable=import-outside-toplevel
|
|
250
|
+
|
|
251
|
+
bus = await MessageBus(bus_type=BusType.SYSTEM).connect()
|
|
252
|
+
introspection = await bus.introspect(
|
|
253
|
+
"org.freedesktop.login1", "/org/freedesktop/login1"
|
|
254
|
+
)
|
|
255
|
+
proxy_obj = bus.get_proxy_object(
|
|
256
|
+
"org.freedesktop.login1", "/org/freedesktop/login1", introspection
|
|
257
|
+
)
|
|
258
|
+
interface = proxy_obj.get_interface("org.freedesktop.login1.Manager")
|
|
259
|
+
await interface.call_reboot(True)
|
|
260
|
+
|
|
261
|
+
except ImportError:
|
|
262
|
+
return False
|
|
263
|
+
return True
|
|
264
|
+
|
|
265
|
+
def reboot_dbus():
|
|
266
|
+
"""Reboot using python-dbus"""
|
|
267
|
+
try:
|
|
268
|
+
import dbus # pylint: disable=import-outside-toplevel
|
|
269
|
+
|
|
270
|
+
bus = dbus.SystemBus()
|
|
271
|
+
obj = bus.get_object("org.freedesktop.login1", "/org/freedesktop/login1")
|
|
272
|
+
intf = dbus.Interface(obj, "org.freedesktop.login1.Manager")
|
|
273
|
+
intf.Reboot(True)
|
|
274
|
+
except ImportError:
|
|
275
|
+
return False
|
|
248
276
|
return True
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
277
|
+
|
|
278
|
+
loop = asyncio.get_event_loop()
|
|
279
|
+
result = loop.run_until_complete(reboot_dbus_fast())
|
|
280
|
+
if not result:
|
|
281
|
+
return reboot_dbus()
|
|
282
|
+
|
|
253
283
|
return True
|
|
254
284
|
|
|
255
285
|
|
amd_debug/prerequisites.py
CHANGED
|
@@ -21,7 +21,7 @@ import pyudev
|
|
|
21
21
|
|
|
22
22
|
from amd_debug.wake import WakeIRQ
|
|
23
23
|
from amd_debug.display import Display
|
|
24
|
-
from amd_debug.kernel import get_kernel_log, SystemdLogger, DmesgLogger
|
|
24
|
+
from amd_debug.kernel import get_kernel_log, SystemdLogger, CySystemdLogger, DmesgLogger
|
|
25
25
|
from amd_debug.common import (
|
|
26
26
|
apply_prefix_wrapper,
|
|
27
27
|
BIT,
|
|
@@ -672,7 +672,9 @@ class PrerequisiteValidator(AmdTool):
|
|
|
672
672
|
"""Check the source for kernel logs"""
|
|
673
673
|
if isinstance(self.kernel_log, SystemdLogger):
|
|
674
674
|
self.db.record_prereq("Logs are provided via systemd", "✅")
|
|
675
|
-
|
|
675
|
+
elif isinstance(self.kernel_log, CySystemdLogger):
|
|
676
|
+
self.db.record_prereq("Logs are provided via cysystemd", "✅")
|
|
677
|
+
elif isinstance(self.kernel_log, DmesgLogger):
|
|
676
678
|
self.db.record_prereq(
|
|
677
679
|
"Logs are provided via dmesg, timestamps may not be accurate over multiple cycles",
|
|
678
680
|
"🚦",
|
|
@@ -1029,6 +1031,21 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1029
1031
|
shutil.rmtree(tmpd)
|
|
1030
1032
|
return True
|
|
1031
1033
|
|
|
1034
|
+
def capture_cstates(self):
|
|
1035
|
+
"""Capture ACPI C state information for the first CPU (assumes the same for all CPUs)"""
|
|
1036
|
+
base = os.path.join("/", "sys", "bus", "cpu", "devices", "cpu0", "cpuidle")
|
|
1037
|
+
paths = {}
|
|
1038
|
+
for root, _dirs, files in os.walk(base, topdown=False):
|
|
1039
|
+
for fname in files:
|
|
1040
|
+
target = os.path.join(root, fname)
|
|
1041
|
+
with open(target, "rb") as f:
|
|
1042
|
+
paths[target] = f.read()
|
|
1043
|
+
debug_str = "ACPI C-state information\n"
|
|
1044
|
+
for path, data in paths.items():
|
|
1045
|
+
prefix = "│ " if path != list(paths.keys())[-1] else "└─"
|
|
1046
|
+
debug_str += f"{prefix}{path}: {data.decode('utf-8', 'ignore')}"
|
|
1047
|
+
self.db.record_debug(debug_str)
|
|
1048
|
+
|
|
1032
1049
|
def capture_battery(self):
|
|
1033
1050
|
"""Capture battery information"""
|
|
1034
1051
|
obj = Batteries()
|
|
@@ -1266,6 +1283,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1266
1283
|
self.capture_pci_acpi,
|
|
1267
1284
|
self.capture_edid,
|
|
1268
1285
|
self.capture_nvidia,
|
|
1286
|
+
self.capture_cstates,
|
|
1269
1287
|
]
|
|
1270
1288
|
checks = []
|
|
1271
1289
|
|
|
@@ -1318,7 +1336,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1318
1336
|
if not check():
|
|
1319
1337
|
result = False
|
|
1320
1338
|
if not result:
|
|
1321
|
-
self.db.record_prereq(Headers.BrokenPrerequisites, "
|
|
1339
|
+
self.db.record_prereq(Headers.BrokenPrerequisites, "🚫")
|
|
1322
1340
|
self.db.sync()
|
|
1323
1341
|
clear_temporary_message(len(msg))
|
|
1324
1342
|
return result
|
amd_debug/sleep_report.py
CHANGED
|
@@ -488,7 +488,7 @@ class SleepReport(AmdTool):
|
|
|
488
488
|
text = line.strip()
|
|
489
489
|
if not text:
|
|
490
490
|
continue
|
|
491
|
-
for group in ["🗣️", "❌", "🚦", "🦟", "
|
|
491
|
+
for group in ["🗣️", "❌", "🚦", "🦟", "🚫", "○"]:
|
|
492
492
|
if line.startswith(group):
|
|
493
493
|
text = line.split(group)[-1]
|
|
494
494
|
color = get_group_color(group)
|
amd_debug/ttm.py
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: amd-debug-tools
|
|
3
|
+
Version: 0.2.8
|
|
4
|
+
Summary: debug tools for AMD systems
|
|
5
|
+
Author-email: Mario Limonciello <superm1@kernel.org>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://web.git.kernel.org/pub/scm/linux/kernel/git/superm1/amd-debug-tools.git/
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
10
|
+
Requires-Python: >=3.7
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: dbus-fast
|
|
14
|
+
Requires-Dist: pyudev
|
|
15
|
+
Requires-Dist: packaging
|
|
16
|
+
Requires-Dist: pandas
|
|
17
|
+
Requires-Dist: jinja2
|
|
18
|
+
Requires-Dist: tabulate
|
|
19
|
+
Requires-Dist: seaborn
|
|
20
|
+
Requires-Dist: cysystemd
|
|
21
|
+
Requires-Dist: Jinja2
|
|
22
|
+
Requires-Dist: matplotlib
|
|
23
|
+
Requires-Dist: seaborn
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Helpful tools for debugging AMD Zen systems
|
|
27
|
+
[](https://codecov.io/github/superm1/amd-debug-tools)
|
|
28
|
+
[](https://pypi.org/project/amd-debug-tools/)
|
|
29
|
+
|
|
30
|
+
This repository hosts open tools that are useful for debugging issues on AMD systems.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
### Distro (Arch)
|
|
34
|
+
`amd-debug-tools` has been [packaged for Arch Linux](https://archlinux.org/packages/extra/any/amd-debug-tools/) (and derivatives). You can install it using:
|
|
35
|
+
|
|
36
|
+
pacman -Sy amd-debug-tools
|
|
37
|
+
|
|
38
|
+
### Using a python wheel (Generic)
|
|
39
|
+
It is suggested to install tools in a virtual environment either using
|
|
40
|
+
`pipx` or `python3 -m venv`.
|
|
41
|
+
|
|
42
|
+
#### From PyPI
|
|
43
|
+
`amd-debug-tools` is distributed as a python wheel, which is a
|
|
44
|
+
binary package format for Python. To install from PyPI, run the following
|
|
45
|
+
command:
|
|
46
|
+
|
|
47
|
+
pipx install amd-debug-tools
|
|
48
|
+
|
|
49
|
+
### From source
|
|
50
|
+
To build the package from source, you will need to the `python3-build`
|
|
51
|
+
package natively installed by your distribution package manager. Then you
|
|
52
|
+
can generate and install a wheel by running the following commands:
|
|
53
|
+
|
|
54
|
+
python3 -m build
|
|
55
|
+
pipx install dist/amd-debug-tools-*.whl
|
|
56
|
+
|
|
57
|
+
### Ensuring path
|
|
58
|
+
If you have not used a `pipx` environment before, you may need to run the following command
|
|
59
|
+
to set up the environment:
|
|
60
|
+
|
|
61
|
+
pipx ensurepath
|
|
62
|
+
|
|
63
|
+
This will add the `pipx` environment to your path.
|
|
64
|
+
|
|
65
|
+
## Running in-tree
|
|
66
|
+
Documentation about running directly from a git checkout is available [here](https://github.com/superm1/amd-debug-tools/blob/master/docs/amd-s2idle.md).
|
|
67
|
+
|
|
68
|
+
## Tools
|
|
69
|
+
|
|
70
|
+
Each tool has its own individual documentation page:
|
|
71
|
+
* [amd-s2idle](https://github.com/superm1/amd-debug-tools/blob/master/docs/amd-s2idle.md)
|
|
72
|
+
* [amd-bios](https://github.com/superm1/amd-debug-tools/blob/master/docs/amd-bios.md)
|
|
73
|
+
* [amd-pstate](https://github.com/superm1/amd-debug-tools/blob/master/docs/amd-pstate.md)
|
|
74
|
+
* [amd-ttm](https://github.com/superm1/amd-debug-tools/blob/master/docs/amd-ttm.md)
|
|
75
|
+
|
|
@@ -2,14 +2,14 @@ launcher.py,sha256=M8kT9DtyZoQgZaKWDbSBu4jsS6tZF1gWko3sovNVyag,947
|
|
|
2
2
|
test_acpi.py,sha256=wtS43Rz95h7YEEJBeFa6Mswaeo4syBZrw4hY8i0YbJY,3117
|
|
3
3
|
test_batteries.py,sha256=nN5pfP5El7Whypq3HHEpW8bufdf5EWSTVGbayfNQYP4,3360
|
|
4
4
|
test_bios.py,sha256=x_KLmQqGEbQhTugyWCHGXjGp2H1dCdhRz0kgw2Big8w,9276
|
|
5
|
-
test_common.py,sha256=
|
|
5
|
+
test_common.py,sha256=qOCouXyO-dhY_x_L8kyNyuP_c0bhHhlqPoc084_F6Xg,20757
|
|
6
6
|
test_database.py,sha256=q5ZjI5u20f7ki6iCY5o1iPi0YOvPz1_W0LTDraU8mN4,10040
|
|
7
7
|
test_display.py,sha256=hHggv-zBthF1BlwWWSjzAm7BBw1DWcElwil5xAuz87g,5822
|
|
8
8
|
test_failures.py,sha256=H1UxXeVjhJs9-j9yas4vwAha676GX1Es7Kz8RN2B590,6845
|
|
9
9
|
test_installer.py,sha256=oDMCvaKqqAWjTggltacnasQ-s1gyUvXPDcNrCUGnux4,10216
|
|
10
10
|
test_kernel.py,sha256=2EXrLht5ZWdT4N5pb_F3zqZl9NEghjnDpcMGCMw3obI,7917
|
|
11
11
|
test_launcher.py,sha256=8g8CBTvLX64Us4RmHtRPSdpV5E2kQFaudBl7VIsxLhE,1733
|
|
12
|
-
test_prerequisites.py,sha256
|
|
12
|
+
test_prerequisites.py,sha256=ig0ENpnR-wRMNlxLQ1FghivQsOu4yx5XP4HsJlptyQA,91791
|
|
13
13
|
test_pstate.py,sha256=a9oAJ9-LANX32XNQhplz6Y75VNYc__QqoSBKIrwvANg,6058
|
|
14
14
|
test_s2idle.py,sha256=FxsyujgX9Px3m56VzHNeA8yMTHmJiRLWxYt-fh1m5gw,33585
|
|
15
15
|
test_sleep_report.py,sha256=ANuxYi_C1oSKAi4xUU2wBu4SwJtcZA7VPpazBe3_WUQ,6922
|
|
@@ -20,18 +20,18 @@ amd_debug/__init__.py,sha256=66Ya61av8RCws6bEY_vdujGmjBIZ6_UqfuWHgMNNOJY,1271
|
|
|
20
20
|
amd_debug/acpi.py,sha256=fkD3Sov8cRT5ryPlakRlT7Z9jiCLT9x_MPWxt3xU_tc,3161
|
|
21
21
|
amd_debug/battery.py,sha256=WN-6ys9PHCZIwg7PdwyBOa62GjBp8WKG0v1YZt5_W5s,3122
|
|
22
22
|
amd_debug/bios.py,sha256=y1iwDqX-mXCkoUtHSi-XO9pN-oLfaqbAMzANGI12zHs,4041
|
|
23
|
-
amd_debug/common.py,sha256=
|
|
23
|
+
amd_debug/common.py,sha256=JclB-X3xwz2Ovvm0nsejE3W9FirP-inTgvHYx7MPNZo,12635
|
|
24
24
|
amd_debug/database.py,sha256=GkRg3cmaNceyQ2_hy0MBAlMbnTDPHo2co2o4ObWpnQg,10621
|
|
25
25
|
amd_debug/display.py,sha256=5L9x9tI_UoulHpIvuxuVASRtdXta7UCW_JjTb5StEB0,953
|
|
26
26
|
amd_debug/failures.py,sha256=z4O4Q-akv3xYGssSZFCqE0cDE4P9F_aw1hxil3McoD4,22910
|
|
27
27
|
amd_debug/installer.py,sha256=6_Y0oHypW-oh_P8N9JW7fzbqidpsi5jphw9_8s5Qvso,14288
|
|
28
28
|
amd_debug/kernel.py,sha256=HpX-QRh8tgkvqKnExfo2JrYqfcbMY8GNgDrC2VVV0Oc,11638
|
|
29
|
-
amd_debug/prerequisites.py,sha256
|
|
29
|
+
amd_debug/prerequisites.py,sha256=-awyFaG2KCX3GSvVyzH1-o3UPhkMNm7Wf3F5AwYWH5U,52340
|
|
30
30
|
amd_debug/pstate.py,sha256=AOKCvUb0ngwHU2C59uSKrFwdLzEyn8r1w2DgWhZAMKM,9583
|
|
31
31
|
amd_debug/s2idle-hook,sha256=LLiaqPtGd0qetu9n6EYxKHZaIdHpVQDONdOuSc0pfFg,1695
|
|
32
32
|
amd_debug/s2idle.py,sha256=4cxHNfmvq11BE-AtkEthIqR-xrWmxW2LTa6oVDtGztY,13284
|
|
33
|
-
amd_debug/sleep_report.py,sha256=
|
|
34
|
-
amd_debug/ttm.py,sha256=
|
|
33
|
+
amd_debug/sleep_report.py,sha256=EzzvOHe0XxZcr8ZurxQXFjKs21gJPBj-qksj4a2WLuM,17317
|
|
34
|
+
amd_debug/ttm.py,sha256=p9zV7OAOrcbI8apOkRMnsI8g1GDvOJfNEAklFuFGdRQ,4679
|
|
35
35
|
amd_debug/validator.py,sha256=X-cNFVvHWKzFgT4aR0Td3I2kwQRBOK4vQUk6L276VCQ,34153
|
|
36
36
|
amd_debug/wake.py,sha256=xT8WrFrN6voCmXWo5dsn4mQ7iR2QJxHrrYBd3EREG-Q,3936
|
|
37
37
|
amd_debug/bash/amd-s2idle,sha256=g_cle1ElCJpwE4wcLezL6y-BdasDKTnNMhrtzKLE9ks,1142
|
|
@@ -39,9 +39,9 @@ amd_debug/templates/html,sha256=JfGhpmHIB2C2GItdGI1kuC8uayqEVgrpQvAWAj35eZ4,1458
|
|
|
39
39
|
amd_debug/templates/md,sha256=r8X2aehnH2gzj0WHYTZ5K9wAqC5y39i_3nkDORSC0uM,787
|
|
40
40
|
amd_debug/templates/stdout,sha256=hyoOJ96K2dJfnWRWhyCuariLKbEHXvs9mstV_g5aMdI,469
|
|
41
41
|
amd_debug/templates/txt,sha256=nNdsvbPFOhGdL7VA-_4k5aN3nB-6ouGQt6AsWst7T3w,649
|
|
42
|
-
amd_debug_tools-0.2.
|
|
43
|
-
amd_debug_tools-0.2.
|
|
44
|
-
amd_debug_tools-0.2.
|
|
45
|
-
amd_debug_tools-0.2.
|
|
46
|
-
amd_debug_tools-0.2.
|
|
47
|
-
amd_debug_tools-0.2.
|
|
42
|
+
amd_debug_tools-0.2.8.dist-info/licenses/LICENSE,sha256=RBlZI6r3MRGzymI2VDX2iW__D2APDbMhu_Xg5t6BWeo,1066
|
|
43
|
+
amd_debug_tools-0.2.8.dist-info/METADATA,sha256=rd1U-CfX7hGfsubYk0mvuR3Ec92dAZrquTKGKRdOFEk,2775
|
|
44
|
+
amd_debug_tools-0.2.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
45
|
+
amd_debug_tools-0.2.8.dist-info/entry_points.txt,sha256=hIskDz6k0_6q1qpqWCpVFsca_djxAqkLrUAwzAyEGuE,144
|
|
46
|
+
amd_debug_tools-0.2.8.dist-info/top_level.txt,sha256=VvGkkY5I7O3HoLNrc2VfgjCA-to3PUjnnKd7juONaFw,243
|
|
47
|
+
amd_debug_tools-0.2.8.dist-info/RECORD,,
|
test_common.py
CHANGED
|
@@ -4,13 +4,16 @@
|
|
|
4
4
|
"""
|
|
5
5
|
This module contains unit tests for the common functions in the amd-debug-tools package.
|
|
6
6
|
"""
|
|
7
|
-
from unittest.mock import patch, mock_open, call
|
|
7
|
+
from unittest.mock import patch, mock_open, call, Mock
|
|
8
8
|
|
|
9
|
+
import asyncio
|
|
10
|
+
import builtins
|
|
9
11
|
import logging
|
|
10
12
|
import tempfile
|
|
11
13
|
import unittest
|
|
12
14
|
import os
|
|
13
15
|
from platform import uname_result
|
|
16
|
+
import sys
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
from amd_debug.common import (
|
|
@@ -49,6 +52,7 @@ color_dict = {
|
|
|
49
52
|
"○": Colors.OK,
|
|
50
53
|
"💤": Colors.OK,
|
|
51
54
|
"💯": Colors.UNDERLINE,
|
|
55
|
+
"🚫": Colors.UNDERLINE,
|
|
52
56
|
"🗣️": Colors.HEADER,
|
|
53
57
|
}
|
|
54
58
|
|
|
@@ -473,63 +477,84 @@ class TestCommon(unittest.TestCase):
|
|
|
473
477
|
self.assertAlmostEqual(get_system_mem(), expected_gb)
|
|
474
478
|
mock_file.assert_called_once_with("/proc/meminfo", "r", encoding="utf-8")
|
|
475
479
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
def test_get_system_mem_missing(self, _mock_join, _mock_file):
|
|
479
|
-
"""Test get_system_mem raises ValueError if MemTotal is missing"""
|
|
480
|
-
with self.assertRaises(ValueError):
|
|
481
|
-
get_system_mem()
|
|
482
|
-
|
|
483
|
-
@patch("amd_debug.common.fatal_error")
|
|
484
|
-
def test_reboot_importerror(self, mock_fatal_error):
|
|
485
|
-
"""Test reboot handles ImportError"""
|
|
486
|
-
with patch.dict("sys.modules", {"dbus": None}):
|
|
487
|
-
reboot()
|
|
488
|
-
mock_fatal_error.assert_called_once_with("Missing dbus")
|
|
489
|
-
|
|
490
|
-
@patch("amd_debug.common.fatal_error")
|
|
491
|
-
def test_reboot_dbus_exception(self, mock_fatal_error):
|
|
492
|
-
"""Test reboot handles dbus.exceptions.DBusException"""
|
|
493
|
-
|
|
494
|
-
class DummyDBusException(Exception):
|
|
495
|
-
"""Dummy exception"""
|
|
496
|
-
|
|
497
|
-
class DummyIntf:
|
|
498
|
-
"""Dummy interface"""
|
|
480
|
+
def test_reboot_dbus_fast_success(self):
|
|
481
|
+
"""Test reboot returns True when reboot_dbus_fast succeeds"""
|
|
499
482
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
def __init__(self):
|
|
483
|
+
# Create a mock loop that properly handles coroutines
|
|
484
|
+
def mock_run_until_complete(coro):
|
|
485
|
+
# Consume the coroutine to prevent the warning
|
|
486
|
+
try:
|
|
487
|
+
# Close the coroutine to prevent the warning
|
|
488
|
+
coro.close()
|
|
489
|
+
except (AttributeError, RuntimeError):
|
|
508
490
|
pass
|
|
491
|
+
return True
|
|
509
492
|
|
|
510
|
-
|
|
511
|
-
|
|
493
|
+
mock_loop = Mock()
|
|
494
|
+
mock_loop.run_until_complete.side_effect = mock_run_until_complete
|
|
512
495
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
496
|
+
with patch("amd_debug.common.asyncio.get_event_loop", return_value=mock_loop):
|
|
497
|
+
result = reboot()
|
|
498
|
+
self.assertTrue(result)
|
|
499
|
+
mock_loop.run_until_complete.assert_called_once()
|
|
516
500
|
|
|
517
|
-
|
|
518
|
-
|
|
501
|
+
@patch("asyncio.get_event_loop")
|
|
502
|
+
def test_reboot_dbus_fast_failure_and_dbus_success(self, mock_get_event_loop):
|
|
503
|
+
"""Test reboot falls back to reboot_dbus when reboot_dbus_fast fails"""
|
|
519
504
|
|
|
520
|
-
|
|
521
|
-
|
|
505
|
+
# Create a mock loop that properly handles coroutines
|
|
506
|
+
def mock_run_until_complete(coro):
|
|
507
|
+
# Consume the coroutine to prevent the warning
|
|
508
|
+
try:
|
|
509
|
+
coro.close()
|
|
510
|
+
except (AttributeError, RuntimeError):
|
|
511
|
+
pass
|
|
512
|
+
return False
|
|
513
|
+
|
|
514
|
+
mock_loop = Mock()
|
|
515
|
+
mock_loop.run_until_complete.side_effect = mock_run_until_complete
|
|
516
|
+
mock_get_event_loop.return_value = mock_loop
|
|
517
|
+
|
|
518
|
+
# Mock the dbus module to avoid ImportError in CI
|
|
519
|
+
mock_dbus = Mock()
|
|
520
|
+
mock_bus = Mock()
|
|
521
|
+
mock_obj = Mock()
|
|
522
|
+
mock_intf = Mock()
|
|
523
|
+
|
|
524
|
+
mock_dbus.SystemBus.return_value = mock_bus
|
|
525
|
+
mock_bus.get_object.return_value = mock_obj
|
|
526
|
+
mock_obj.get_interface.return_value = mock_intf
|
|
527
|
+
mock_dbus.Interface = Mock(return_value=mock_intf)
|
|
528
|
+
|
|
529
|
+
with patch.dict("sys.modules", {"dbus": mock_dbus}):
|
|
530
|
+
result = reboot()
|
|
531
|
+
self.assertTrue(result)
|
|
532
|
+
|
|
533
|
+
@patch("asyncio.get_event_loop")
|
|
534
|
+
def test_reboot_dbus_fast_failure_and_dbus_failure(self, mock_get_event_loop):
|
|
535
|
+
"""Test reboot returns False when both reboot_dbus_fast and reboot_dbus fail"""
|
|
536
|
+
|
|
537
|
+
# Create a mock loop that properly handles coroutines
|
|
538
|
+
def mock_run_until_complete(coro):
|
|
539
|
+
# Consume the coroutine to prevent the warning
|
|
540
|
+
try:
|
|
541
|
+
coro.close()
|
|
542
|
+
except (AttributeError, RuntimeError):
|
|
543
|
+
pass
|
|
544
|
+
return False
|
|
522
545
|
|
|
523
|
-
|
|
546
|
+
mock_loop = Mock()
|
|
547
|
+
mock_loop.run_until_complete.side_effect = mock_run_until_complete
|
|
548
|
+
mock_get_event_loop.return_value = mock_loop
|
|
524
549
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
return DummyBus()
|
|
550
|
+
# Mock the import to raise ImportError when dbus is imported
|
|
551
|
+
original_import = builtins.__import__
|
|
528
552
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
553
|
+
def mock_import(name, *args, **kwargs):
|
|
554
|
+
if name == "dbus":
|
|
555
|
+
raise ImportError("No module named 'dbus'")
|
|
556
|
+
return original_import(name, *args, **kwargs)
|
|
532
557
|
|
|
533
|
-
with patch
|
|
534
|
-
reboot()
|
|
535
|
-
self.
|
|
558
|
+
with patch("builtins.__import__", side_effect=mock_import):
|
|
559
|
+
result = reboot()
|
|
560
|
+
self.assertFalse(result)
|
test_prerequisites.py
CHANGED
|
@@ -2035,3 +2035,76 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
2035
2035
|
self.mock_db.record_prereq.assert_called_with(
|
|
2036
2036
|
"NVIDIA GPU {f} not readable", "👀"
|
|
2037
2037
|
)
|
|
2038
|
+
|
|
2039
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2040
|
+
@patch(
|
|
2041
|
+
"builtins.open",
|
|
2042
|
+
new_callable=unittest.mock.mock_open,
|
|
2043
|
+
read_data=b"C1 state info",
|
|
2044
|
+
)
|
|
2045
|
+
def test_capture_cstates_single_file(self, mock_open, mock_walk):
|
|
2046
|
+
"""Test capture_cstates with a single cpuidle file"""
|
|
2047
|
+
mock_walk.return_value = [
|
|
2048
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1"]),
|
|
2049
|
+
]
|
|
2050
|
+
self.validator.capture_cstates()
|
|
2051
|
+
self.mock_db.record_debug.assert_called_with(
|
|
2052
|
+
"ACPI C-state information\n└─/sys/bus/cpu/devices/cpu0/cpuidle/state1: C1 state info"
|
|
2053
|
+
)
|
|
2054
|
+
|
|
2055
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2056
|
+
@patch("builtins.open", new_callable=mock_open)
|
|
2057
|
+
def test_capture_cstates_multiple_files(self, mock_open_func, mock_walk):
|
|
2058
|
+
"""Test capture_cstates with multiple cpuidle files"""
|
|
2059
|
+
# Setup mock file reads for two files
|
|
2060
|
+
file_contents = {
|
|
2061
|
+
"/sys/bus/cpu/devices/cpu0/cpuidle/state1": b"C1 info",
|
|
2062
|
+
"/sys/bus/cpu/devices/cpu0/cpuidle/state2": b"C2 info",
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
def side_effect(path, mode="rb"):
|
|
2066
|
+
mock_file = mock_open(read_data=file_contents[path])()
|
|
2067
|
+
return mock_file
|
|
2068
|
+
|
|
2069
|
+
mock_open_func.side_effect = side_effect
|
|
2070
|
+
mock_walk.return_value = [
|
|
2071
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1", "state2"]),
|
|
2072
|
+
]
|
|
2073
|
+
self.validator.capture_cstates()
|
|
2074
|
+
# The prefix logic is based on order, so check for both lines
|
|
2075
|
+
debug_call = self.mock_db.record_debug.call_args[0][0]
|
|
2076
|
+
self.assertIn("/sys/bus/cpu/devices/cpu0/cpuidle/state1: C1 info", debug_call)
|
|
2077
|
+
self.assertIn("/sys/bus/cpu/devices/cpu0/cpuidle/state2: C2 info", debug_call)
|
|
2078
|
+
self.assertTrue(debug_call.startswith("ACPI C-state information\n"))
|
|
2079
|
+
|
|
2080
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2081
|
+
@patch("builtins.open", new_callable=mock_open, read_data=b"")
|
|
2082
|
+
def test_capture_cstates_empty_files(self, _mock_open, mock_walk):
|
|
2083
|
+
"""Test capture_cstates with empty cpuidle files"""
|
|
2084
|
+
mock_walk.return_value = [
|
|
2085
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1"]),
|
|
2086
|
+
]
|
|
2087
|
+
self.validator.capture_cstates()
|
|
2088
|
+
self.mock_db.record_debug.assert_called_with(
|
|
2089
|
+
"ACPI C-state information\n└─/sys/bus/cpu/devices/cpu0/cpuidle/state1: "
|
|
2090
|
+
)
|
|
2091
|
+
|
|
2092
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2093
|
+
@patch("builtins.open", side_effect=PermissionError)
|
|
2094
|
+
def test_capture_cstates_permission_error(self, _mock_open, mock_walk):
|
|
2095
|
+
"""Test capture_cstates when reading cpuidle files raises PermissionError"""
|
|
2096
|
+
mock_walk.return_value = [
|
|
2097
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1"]),
|
|
2098
|
+
]
|
|
2099
|
+
with self.assertRaises(PermissionError):
|
|
2100
|
+
self.validator.capture_cstates()
|
|
2101
|
+
self.mock_db.record_debug.assert_not_called()
|
|
2102
|
+
|
|
2103
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2104
|
+
def test_capture_cstates_no_files(self, mock_walk):
|
|
2105
|
+
"""Test capture_cstates when no cpuidle files are present"""
|
|
2106
|
+
mock_walk.return_value = [
|
|
2107
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], []),
|
|
2108
|
+
]
|
|
2109
|
+
self.validator.capture_cstates()
|
|
2110
|
+
self.mock_db.record_debug.assert_called_with("ACPI C-state information\n")
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: amd-debug-tools
|
|
3
|
-
Version: 0.2.7
|
|
4
|
-
Summary: debug tools for AMD systems
|
|
5
|
-
Author-email: Mario Limonciello <superm1@kernel.org>
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://web.git.kernel.org/pub/scm/linux/kernel/git/superm1/amd-debug-tools.git/
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
10
|
-
Requires-Python: >=3.7
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
License-File: LICENSE
|
|
13
|
-
Requires-Dist: pyudev
|
|
14
|
-
Requires-Dist: packaging
|
|
15
|
-
Requires-Dist: pandas
|
|
16
|
-
Requires-Dist: jinja2
|
|
17
|
-
Requires-Dist: tabulate
|
|
18
|
-
Requires-Dist: seaborn
|
|
19
|
-
Requires-Dist: cysystemd
|
|
20
|
-
Requires-Dist: Jinja2
|
|
21
|
-
Requires-Dist: matplotlib
|
|
22
|
-
Requires-Dist: seaborn
|
|
23
|
-
Dynamic: license-file
|
|
24
|
-
|
|
25
|
-
# Helpful tools for debugging AMD Zen systems
|
|
26
|
-
[](https://codecov.io/github/superm1/amd-debug-tools)
|
|
27
|
-
[](https://pypi.org/project/amd-debug-tools/)
|
|
28
|
-
|
|
29
|
-
This repository hosts open tools that are useful for debugging issues on AMD systems.
|
|
30
|
-
|
|
31
|
-
## Installation
|
|
32
|
-
It is suggested to install tools in a virtual environment either using
|
|
33
|
-
`pipx` or `python3 -m venv`.
|
|
34
|
-
|
|
35
|
-
### From PyPI
|
|
36
|
-
`amd-debug-tools` is distributed as a python wheel, which is a
|
|
37
|
-
binary package format for Python. To install from PyPI, run the following
|
|
38
|
-
command:
|
|
39
|
-
|
|
40
|
-
pipx install amd-debug-tools
|
|
41
|
-
|
|
42
|
-
### From source
|
|
43
|
-
To build the package from source, you will need to the `python3-build`
|
|
44
|
-
package natively installed by your distribution package manager. Then you
|
|
45
|
-
can generate and install a wheel by running the following commands:
|
|
46
|
-
|
|
47
|
-
python3 -m build
|
|
48
|
-
pipx install dist/amd-debug-tools-*.whl
|
|
49
|
-
|
|
50
|
-
### Ensuring path
|
|
51
|
-
If you have not used a `pipx` environment before, you may need to run the following command
|
|
52
|
-
to set up the environment:
|
|
53
|
-
|
|
54
|
-
pipx ensurepath
|
|
55
|
-
|
|
56
|
-
This will add the `pipx` environment to your path.
|
|
57
|
-
|
|
58
|
-
### Running in tree
|
|
59
|
-
If you want to run the tools in tree, you need to make sure that distro dependencies
|
|
60
|
-
that would normally install into a venv are installed. This can be done by running:
|
|
61
|
-
|
|
62
|
-
./install_deps.py
|
|
63
|
-
|
|
64
|
-
After dependencies are installed, you can run the tools by running:
|
|
65
|
-
|
|
66
|
-
./amd_s2idle.py
|
|
67
|
-
./amd_bios.py
|
|
68
|
-
./amd_pstate.py
|
|
69
|
-
|
|
70
|
-
## amd-s2idle
|
|
71
|
-
`amd-s2idle` is a tool used for analyzing the entry and exit of the s2idle
|
|
72
|
-
state of a Linux system.
|
|
73
|
-
|
|
74
|
-
It is intended to use with Linux kernel 6.1 or later and works by hooking
|
|
75
|
-
into dynamic debugging messages and events that are generated by the kernel.
|
|
76
|
-
|
|
77
|
-
For analysis of power consumption issues it can be hooked into `systemd` to
|
|
78
|
-
run a command to capture data right before and after the system enters and
|
|
79
|
-
exits the s2idle state.
|
|
80
|
-
|
|
81
|
-
4 high level commands are supported.
|
|
82
|
-
|
|
83
|
-
### `amd-s2idle install`
|
|
84
|
-
This will install the systemd hook so that data will be captured before and
|
|
85
|
-
after the system enters and exits the s2idle state.
|
|
86
|
-
|
|
87
|
-
This will also install a bash completion script that can be used for other
|
|
88
|
-
commands.
|
|
89
|
-
|
|
90
|
-
**NOTE:** This command is only supported when run from a venv.
|
|
91
|
-
|
|
92
|
-
### `amd-s2idle uninstall`
|
|
93
|
-
This will uninstall the systemd hook and remove the bash completion script.
|
|
94
|
-
|
|
95
|
-
**NOTE:** This command is only supported when run from a venv.
|
|
96
|
-
|
|
97
|
-
### `amd-s2idle test`
|
|
98
|
-
This will run a suspend cycle with a timer based wakeup and capture relevant
|
|
99
|
-
data into a database and produce a report. This can also be used to run multiple cycles.
|
|
100
|
-
|
|
101
|
-
The following optional arguments are supported for this command:
|
|
102
|
-
|
|
103
|
-
--count COUNT Number of cycles to run
|
|
104
|
-
--duration DURATION Duration of the cycle in seconds
|
|
105
|
-
--wait WAIT Time to wait before starting the cycle in seconds
|
|
106
|
-
--format FORMAT Format of the report to produce (html, txt or md)
|
|
107
|
-
--report-file File to write the report to
|
|
108
|
-
--force Run a test cycle even if the system fails to pass prerequisite checks
|
|
109
|
-
--random Run sleep cycles for random durations and waits, using the --duration and --wait arguments as an upper bound
|
|
110
|
-
--logind Use logind to suspend the system
|
|
111
|
-
--tool-debug Enable debug logging
|
|
112
|
-
--bios-debug Enable BIOS debug logging instead of notify logging
|
|
113
|
-
|
|
114
|
-
If the tool is launched with an environment that can call `xdg-open`, the report
|
|
115
|
-
will be opened in a browser.
|
|
116
|
-
|
|
117
|
-
### `amd-s2idle report`
|
|
118
|
-
This will produce a report from the data captured by the `test` command
|
|
119
|
-
and/or from the systemd hook. The report will default to 60 days of data.
|
|
120
|
-
|
|
121
|
-
The following optional arguments are supported for this command:
|
|
122
|
-
|
|
123
|
-
--since SINCE Date to start the report from
|
|
124
|
-
--until UNTIL Date to end the report at
|
|
125
|
-
--format FORMAT Format of the report to produce (html, txt or md)
|
|
126
|
-
--report-file File to write the report to
|
|
127
|
-
--tool-debug Enable tool debug logging
|
|
128
|
-
--report-debug
|
|
129
|
-
--no-report-debug
|
|
130
|
-
Include debug messages in report (WARNING: can significantly increase report size)
|
|
131
|
-
If the tool is launched with an environment that can call `xdg-open`, the report
|
|
132
|
-
will be opened in a browser.
|
|
133
|
-
|
|
134
|
-
### `amd-s2idle --version`
|
|
135
|
-
This will print the version of the tool and exit.
|
|
136
|
-
|
|
137
|
-
### Debug output
|
|
138
|
-
All commands support the `--tool-debug` argument which will enable extra debug output. This is often needed for debugging issues with a particular cycle.
|
|
139
|
-
|
|
140
|
-
**NOTE:** enabling debug output significantly increases the size of the report.
|
|
141
|
-
It's suggested that you use `--since` and `--until` to focus on the cycles that you are interested in.
|
|
142
|
-
|
|
143
|
-
## amd-bios
|
|
144
|
-
`amd-bios` is a a tool that can be used to enable or disable BIOS AML debug logging
|
|
145
|
-
-and to parse a kernel log that contains BIOS logs.
|
|
146
|
-
|
|
147
|
-
### `amd-bios trace`
|
|
148
|
-
Modify BIOS AML trace debug logging.
|
|
149
|
-
|
|
150
|
-
One of the following arguments must be set for this command:
|
|
151
|
-
|
|
152
|
-
--enable Enable BIOS AML tracing
|
|
153
|
-
--disable Disable BIOS AML tracing
|
|
154
|
-
|
|
155
|
-
The following optional arguments are supported for this command:
|
|
156
|
-
|
|
157
|
-
--tool-debug Enable tool debug logging
|
|
158
|
-
|
|
159
|
-
### `amd-bios parse`
|
|
160
|
-
Parses a kernel log that contains BIOS AML debug logging and produces a report.
|
|
161
|
-
|
|
162
|
-
The following optional arguments are supported for this command:
|
|
163
|
-
|
|
164
|
-
--input INPUT Optional input file to parse
|
|
165
|
-
--tool-debug Enable tool debug logging
|
|
166
|
-
|
|
167
|
-
### `amd-bios --version`
|
|
168
|
-
This will print the version of the tool and exit.
|
|
169
|
-
|
|
170
|
-
## amd-pstate
|
|
171
|
-
`amd-pstate` is a tool used for identification of issues with amd-pstate.
|
|
172
|
-
It will capture some state from the system as well as from the machine specific registers that
|
|
173
|
-
amd-pstate uses.
|
|
174
|
-
|
|
175
|
-
## amd-ttm
|
|
176
|
-
`amd-ttm` is a tool used for managing the TTM memory settings on AMD systems.
|
|
177
|
-
|
|
178
|
-
## Compatibility scripts
|
|
179
|
-
|
|
180
|
-
Compatibility scripts are provided for the previous names the tools went by:
|
|
181
|
-
`amd_s2idle.py`, `amd_bios.py` and `amd_pstate.py`.
|
|
182
|
-
These allow cloning the repository and running the scripts without installing
|
|
183
|
-
the package.
|
|
184
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|