amd-debug-tools 0.2.4__py3-none-any.whl → 0.2.6__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 +9 -1
- amd_debug/bios.py +5 -3
- amd_debug/common.py +39 -0
- amd_debug/installer.py +2 -2
- amd_debug/kernel.py +2 -10
- amd_debug/prerequisites.py +57 -20
- amd_debug/pstate.py +5 -3
- amd_debug/s2idle.py +5 -3
- amd_debug/test_ttm.py +291 -0
- amd_debug/ttm.py +161 -0
- amd_debug/validator.py +20 -0
- {amd_debug_tools-0.2.4.dist-info → amd_debug_tools-0.2.6.dist-info}/METADATA +6 -3
- {amd_debug_tools-0.2.4.dist-info → amd_debug_tools-0.2.6.dist-info}/RECORD +24 -22
- {amd_debug_tools-0.2.4.dist-info → amd_debug_tools-0.2.6.dist-info}/entry_points.txt +1 -0
- test_bios.py +26 -8
- test_common.py +91 -0
- test_kernel.py +3 -2
- test_launcher.py +7 -0
- test_prerequisites.py +115 -5
- test_s2idle.py +6 -4
- test_validator.py +63 -1
- {amd_debug_tools-0.2.4.dist-info → amd_debug_tools-0.2.6.dist-info}/WHEEL +0 -0
- {amd_debug_tools-0.2.4.dist-info → amd_debug_tools-0.2.6.dist-info}/licenses/LICENSE +0 -0
- {amd_debug_tools-0.2.4.dist-info → amd_debug_tools-0.2.6.dist-info}/top_level.txt +0 -0
test_prerequisites.py
CHANGED
|
@@ -143,6 +143,16 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
143
143
|
result = self.validator.check_port_pm_override()
|
|
144
144
|
self.assertTrue(result)
|
|
145
145
|
|
|
146
|
+
@patch("amd_debug.prerequisites.version.parse")
|
|
147
|
+
def test_check_port_pm_override_smu_version_missing(self, mock_version_parse):
|
|
148
|
+
"""Test check_port_pm_override with SMU version undefined"""
|
|
149
|
+
self.validator.cpu_family = 0x19
|
|
150
|
+
self.validator.cpu_model = 0x74
|
|
151
|
+
mock_version_parse.side_effect = lambda v: v if isinstance(v, str) else None
|
|
152
|
+
self.validator.smu_version = ""
|
|
153
|
+
result = self.validator.check_port_pm_override()
|
|
154
|
+
self.assertTrue(result)
|
|
155
|
+
|
|
146
156
|
@patch("amd_debug.prerequisites.version.parse")
|
|
147
157
|
def test_check_port_pm_override_smu_version_too_low(self, mock_version_parse):
|
|
148
158
|
"""Test check_port_pm_override with SMU version < 76.18.0"""
|
|
@@ -185,15 +195,18 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
185
195
|
new_callable=unittest.mock.mock_open,
|
|
186
196
|
read_data=b"\x00" * 45,
|
|
187
197
|
)
|
|
188
|
-
|
|
189
|
-
|
|
198
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
199
|
+
def test_check_iommu_no_dma_protection_no_msft0201(self, _mock_open, _mock_exists):
|
|
200
|
+
"""Test check_iommu when DMA protection is not enabled and no MSFT0201 in IVRS"""
|
|
190
201
|
self.validator.cpu_family = 0x1A
|
|
191
202
|
self.validator.cpu_model = 0x20
|
|
192
203
|
iommu_device = MagicMock(sys_path="/sys/devices/iommu")
|
|
204
|
+
acpi_device = MagicMock(sys_path="/sys/devices/acpi/MSFT0201")
|
|
205
|
+
platform_device = MagicMock(sys_path="/sys/devices/platform/MSFT0201")
|
|
193
206
|
self.mock_pyudev.list_devices.side_effect = [
|
|
194
207
|
[iommu_device],
|
|
195
|
-
[],
|
|
196
|
-
[],
|
|
208
|
+
[acpi_device],
|
|
209
|
+
[platform_device],
|
|
197
210
|
]
|
|
198
211
|
result = self.validator.check_iommu()
|
|
199
212
|
self.assertFalse(result)
|
|
@@ -204,6 +217,27 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
204
217
|
"IOMMU is misconfigured: Pre-boot DMA protection not enabled", "❌"
|
|
205
218
|
)
|
|
206
219
|
|
|
220
|
+
@patch(
|
|
221
|
+
"amd_debug.prerequisites.open",
|
|
222
|
+
new_callable=unittest.mock.mock_open,
|
|
223
|
+
read_data=b"\x00" * 45 + "MSFT0201".encode("utf-8"),
|
|
224
|
+
)
|
|
225
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
226
|
+
def test_check_iommu_no_dma_protection_BUT_msft0201(self, _mock_open, _mock_exists):
|
|
227
|
+
"""Test check_iommu when DMA protection is not enabled BUT MSFT0201 is in IVRS"""
|
|
228
|
+
self.validator.cpu_family = 0x1A
|
|
229
|
+
self.validator.cpu_model = 0x20
|
|
230
|
+
iommu_device = MagicMock(sys_path="/sys/devices/iommu")
|
|
231
|
+
acpi_device = MagicMock(sys_path="/sys/devices/acpi/MSFT0201")
|
|
232
|
+
platform_device = MagicMock(sys_path="/sys/devices/platform/MSFT0201")
|
|
233
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
234
|
+
[iommu_device],
|
|
235
|
+
[acpi_device],
|
|
236
|
+
[platform_device],
|
|
237
|
+
]
|
|
238
|
+
result = self.validator.check_iommu()
|
|
239
|
+
self.assertTrue(result)
|
|
240
|
+
|
|
207
241
|
@patch(
|
|
208
242
|
"amd_debug.prerequisites.open",
|
|
209
243
|
new_callable=unittest.mock.mock_open,
|
|
@@ -292,7 +326,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
292
326
|
BIT(9) | 1
|
|
293
327
|
) # Kernel warnings ignored, other taint present
|
|
294
328
|
result = self.validator.check_taint()
|
|
295
|
-
self.
|
|
329
|
+
self.assertTrue(result)
|
|
296
330
|
self.assertTrue(
|
|
297
331
|
any(isinstance(f, TaintedKernel) for f in self.validator.failures)
|
|
298
332
|
)
|
|
@@ -1925,3 +1959,79 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
1925
1959
|
result = self.validator.check_dpia_pg_dmcub()
|
|
1926
1960
|
self.assertTrue(result)
|
|
1927
1961
|
self.mock_db.record_prereq.assert_not_called()
|
|
1962
|
+
|
|
1963
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1964
|
+
def test_capture_nvidia_version_file_missing(self, mock_exists):
|
|
1965
|
+
"""Test capture_nvidia when /proc/driver/nvidia/version does not exist"""
|
|
1966
|
+
mock_exists.side_effect = lambda p: False if "version" in p else True
|
|
1967
|
+
result = self.validator.capture_nvidia()
|
|
1968
|
+
self.assertTrue(result)
|
|
1969
|
+
self.mock_db.record_debug_file.assert_not_called()
|
|
1970
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1971
|
+
|
|
1972
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1973
|
+
def test_capture_nvidia_gpus_dir_missing(self, mock_exists):
|
|
1974
|
+
"""Test capture_nvidia when /proc/driver/nvidia/gpus does not exist"""
|
|
1975
|
+
|
|
1976
|
+
def exists_side_effect(path):
|
|
1977
|
+
if "version" in path:
|
|
1978
|
+
return True
|
|
1979
|
+
if "gpus" in path:
|
|
1980
|
+
return False
|
|
1981
|
+
return True
|
|
1982
|
+
|
|
1983
|
+
mock_exists.side_effect = exists_side_effect
|
|
1984
|
+
result = self.validator.capture_nvidia()
|
|
1985
|
+
self.assertTrue(result)
|
|
1986
|
+
self.mock_db.record_debug_file.assert_called_once_with(
|
|
1987
|
+
"/proc/driver/nvidia/version"
|
|
1988
|
+
)
|
|
1989
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1990
|
+
|
|
1991
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
1992
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1993
|
+
def test_capture_nvidia_success(self, mock_exists, mock_walk):
|
|
1994
|
+
"""Test capture_nvidia when NVIDIA GPU files are present and readable"""
|
|
1995
|
+
mock_exists.side_effect = lambda p: True
|
|
1996
|
+
mock_walk.return_value = [
|
|
1997
|
+
("/proc/driver/nvidia/gpus/0000:01:00.0", [], ["info", "power"])
|
|
1998
|
+
]
|
|
1999
|
+
result = self.validator.capture_nvidia()
|
|
2000
|
+
self.assertTrue(result)
|
|
2001
|
+
self.mock_db.record_debug_file.assert_any_call("/proc/driver/nvidia/version")
|
|
2002
|
+
self.mock_db.record_debug.assert_any_call("NVIDIA info")
|
|
2003
|
+
self.mock_db.record_debug_file.assert_any_call(
|
|
2004
|
+
"/proc/driver/nvidia/gpus/0000:01:00.0/info"
|
|
2005
|
+
)
|
|
2006
|
+
self.mock_db.record_debug.assert_any_call("NVIDIA power")
|
|
2007
|
+
self.mock_db.record_debug_file.assert_any_call(
|
|
2008
|
+
"/proc/driver/nvidia/gpus/0000:01:00.0/power"
|
|
2009
|
+
)
|
|
2010
|
+
|
|
2011
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2012
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2013
|
+
def test_capture_nvidia_permission_error_on_version(self, mock_exists, mock_walk):
|
|
2014
|
+
"""Test capture_nvidia when PermissionError occurs reading version file"""
|
|
2015
|
+
mock_exists.side_effect = lambda p: True if "version" in p else False
|
|
2016
|
+
self.mock_db.record_debug_file.side_effect = PermissionError
|
|
2017
|
+
result = self.validator.capture_nvidia()
|
|
2018
|
+
self.assertTrue(result)
|
|
2019
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
2020
|
+
"NVIDIA GPU version not readable", "👀"
|
|
2021
|
+
)
|
|
2022
|
+
|
|
2023
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2024
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2025
|
+
def test_capture_nvidia_permission_error_on_gpu_file(self, mock_exists, mock_walk):
|
|
2026
|
+
"""Test capture_nvidia when PermissionError occurs reading a GPU file"""
|
|
2027
|
+
mock_exists.side_effect = lambda p: True
|
|
2028
|
+
mock_walk.return_value = [
|
|
2029
|
+
("/proc/driver/nvidia/gpus/0000:01:00.0", [], ["info"])
|
|
2030
|
+
]
|
|
2031
|
+
self.mock_db.record_debug_file.side_effect = [None, PermissionError]
|
|
2032
|
+
result = self.validator.capture_nvidia()
|
|
2033
|
+
self.assertTrue(result)
|
|
2034
|
+
self.mock_db.record_debug.assert_any_call("NVIDIA info")
|
|
2035
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
2036
|
+
"NVIDIA GPU {f} not readable", "👀"
|
|
2037
|
+
)
|
test_s2idle.py
CHANGED
|
@@ -117,9 +117,9 @@ class TestParseArgs(unittest.TestCase):
|
|
|
117
117
|
|
|
118
118
|
def test_version_command(self):
|
|
119
119
|
"""Test parse_args with version command"""
|
|
120
|
-
sys.argv = ["s2idle.py", "version"]
|
|
120
|
+
sys.argv = ["s2idle.py", "--version"]
|
|
121
121
|
args = parse_args()
|
|
122
|
-
self.
|
|
122
|
+
self.assertTrue(args.version)
|
|
123
123
|
|
|
124
124
|
|
|
125
125
|
class TestMainFunction(unittest.TestCase):
|
|
@@ -214,7 +214,7 @@ class TestMainFunction(unittest.TestCase):
|
|
|
214
214
|
"""Test main function with version action"""
|
|
215
215
|
sys.argv = ["s2idle.py", "version"]
|
|
216
216
|
with patch("amd_debug.s2idle.parse_args") as mock_parse_args:
|
|
217
|
-
mock_parse_args.return_value = argparse.Namespace(action=
|
|
217
|
+
mock_parse_args.return_value = argparse.Namespace(version=True, action=None)
|
|
218
218
|
mock_version.return_value = "1.0.0"
|
|
219
219
|
with patch("builtins.print") as mock_print:
|
|
220
220
|
result = main()
|
|
@@ -226,7 +226,9 @@ class TestMainFunction(unittest.TestCase):
|
|
|
226
226
|
"""Test main function with no action specified"""
|
|
227
227
|
sys.argv = ["s2idle.py"]
|
|
228
228
|
with patch("amd_debug.s2idle.parse_args") as mock_parse_args:
|
|
229
|
-
mock_parse_args.return_value = argparse.Namespace(
|
|
229
|
+
mock_parse_args.return_value = argparse.Namespace(
|
|
230
|
+
version=False, action=None
|
|
231
|
+
)
|
|
230
232
|
with self.assertRaises(SystemExit) as cm:
|
|
231
233
|
main()
|
|
232
234
|
self.assertEqual(cm.exception.code, "no action specified")
|
test_validator.py
CHANGED
|
@@ -71,7 +71,8 @@ class TestValidator(unittest.TestCase):
|
|
|
71
71
|
logging.basicConfig(filename="/dev/null", level=logging.DEBUG)
|
|
72
72
|
|
|
73
73
|
@patch("amd_debug.validator.SleepDatabase")
|
|
74
|
-
|
|
74
|
+
@patch("subprocess.run")
|
|
75
|
+
def setUp(self, _db_mock, _mock_run):
|
|
75
76
|
"""Set up a mock context for testing"""
|
|
76
77
|
self.validator = SleepValidator(tool_debug=True, bios_debug=False)
|
|
77
78
|
|
|
@@ -830,3 +831,64 @@ class TestValidator(unittest.TestCase):
|
|
|
830
831
|
)
|
|
831
832
|
mock_os_write.assert_called_once_with(3, b"mem")
|
|
832
833
|
mock_os_close.assert_called_once_with(3)
|
|
834
|
+
|
|
835
|
+
@patch("os.path.exists")
|
|
836
|
+
@patch("os.open")
|
|
837
|
+
@patch("os.write")
|
|
838
|
+
@patch("os.close")
|
|
839
|
+
def test_toggle_nvidia_file_not_exists(
|
|
840
|
+
self, mock_close, mock_write, mock_open, mock_exists
|
|
841
|
+
):
|
|
842
|
+
"""Test toggle_nvidia returns True if NVIDIA suspend file does not exist"""
|
|
843
|
+
mock_exists.return_value = False
|
|
844
|
+
result = self.validator.toggle_nvidia(b"suspend")
|
|
845
|
+
self.assertTrue(result)
|
|
846
|
+
mock_open.assert_not_called()
|
|
847
|
+
mock_write.assert_not_called()
|
|
848
|
+
mock_close.assert_not_called()
|
|
849
|
+
|
|
850
|
+
@patch("os.path.exists")
|
|
851
|
+
@patch("os.open")
|
|
852
|
+
@patch("os.write")
|
|
853
|
+
@patch("os.close")
|
|
854
|
+
def test_toggle_nvidia_success(
|
|
855
|
+
self, mock_close, mock_write, mock_open, mock_exists
|
|
856
|
+
):
|
|
857
|
+
"""Test toggle_nvidia writes value and returns True on success"""
|
|
858
|
+
mock_exists.return_value = True
|
|
859
|
+
mock_open.return_value = 42
|
|
860
|
+
mock_write.return_value = None
|
|
861
|
+
with patch.object(self.validator.db, "record_debug") as mock_record_debug:
|
|
862
|
+
result = self.validator.toggle_nvidia(b"suspend")
|
|
863
|
+
self.assertTrue(result)
|
|
864
|
+
mock_open.assert_called_once_with(
|
|
865
|
+
"/proc/driver/nvidia/suspend", os.O_WRONLY | os.O_SYNC
|
|
866
|
+
)
|
|
867
|
+
mock_write.assert_called_once_with(42, b"suspend")
|
|
868
|
+
mock_close.assert_called_once_with(42)
|
|
869
|
+
mock_record_debug.assert_called_once_with(
|
|
870
|
+
"Wrote b'suspend' to NVIDIA driver"
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
@patch("os.path.exists")
|
|
874
|
+
@patch("os.open")
|
|
875
|
+
@patch("os.write")
|
|
876
|
+
@patch("os.close")
|
|
877
|
+
def test_toggle_nvidia_oserror(
|
|
878
|
+
self, mock_close, mock_write, mock_open, mock_exists
|
|
879
|
+
):
|
|
880
|
+
"""Test toggle_nvidia handles OSError and returns False"""
|
|
881
|
+
mock_exists.return_value = True
|
|
882
|
+
mock_open.return_value = 99
|
|
883
|
+
mock_write.side_effect = OSError("write error")
|
|
884
|
+
with patch.object(
|
|
885
|
+
self.validator.db, "record_cycle_data"
|
|
886
|
+
) as mock_record_cycle_data:
|
|
887
|
+
result = self.validator.toggle_nvidia(b"resume")
|
|
888
|
+
self.assertFalse(result)
|
|
889
|
+
mock_open.assert_called_once_with(
|
|
890
|
+
"/proc/driver/nvidia/suspend", os.O_WRONLY | os.O_SYNC
|
|
891
|
+
)
|
|
892
|
+
mock_write.assert_called_once_with(99, b"resume")
|
|
893
|
+
mock_close.assert_called_once_with(99)
|
|
894
|
+
mock_record_cycle_data.assert_called_once()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|