amd-debug-tools 0.2.5__py3-none-any.whl → 0.2.12__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.
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.assertEqual(args.action, "version")
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="version")
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(action=None)
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")
@@ -358,8 +360,10 @@ class TestTestFunction(unittest.TestCase):
358
360
  @patch("amd_debug.s2idle.prompt_test_arguments")
359
361
  @patch("amd_debug.s2idle.prompt_report_arguments")
360
362
  @patch("amd_debug.s2idle.display_report_file")
363
+ @patch("amd_debug.s2idle.datetime")
361
364
  def test_test_success(
362
365
  self,
366
+ mock_datetime,
363
367
  mock_display_report_file,
364
368
  mock_prompt_report_arguments,
365
369
  mock_prompt_test_arguments,
@@ -369,6 +373,11 @@ class TestTestFunction(unittest.TestCase):
369
373
  mock_installer,
370
374
  ):
371
375
  """Test the test function when everything succeeds"""
376
+ from datetime import datetime
377
+
378
+ mock_datetime.now.return_value = datetime(2023, 2, 1, 0, 0, 0)
379
+ mock_datetime.side_effect = lambda *args, **kwargs: datetime(*args, **kwargs)
380
+
372
381
  mock_installer_instance = mock_installer.return_value
373
382
  mock_installer_instance.install_dependencies.return_value = True
374
383
 
@@ -414,9 +423,11 @@ class TestTestFunction(unittest.TestCase):
414
423
  mock_sleep_validator_instance.run.assert_called_once_with(
415
424
  duration=10, wait=5, count=3, rand=False, logind=False
416
425
  )
426
+ from datetime import datetime
427
+
417
428
  mock_sleep_report.assert_called_once_with(
418
429
  since="2023-01-01",
419
- until="2023-02-01",
430
+ until=datetime(2023, 2, 1, 0, 0, 0),
420
431
  fname="report.html",
421
432
  fmt="html",
422
433
  tool_debug=True,
@@ -600,12 +611,18 @@ class TestDisplayReportFile(unittest.TestCase):
600
611
  self, mock_subprocess_call, mock_env_get, mock_is_root
601
612
  ):
602
613
  """Test display_report_file when format is html, user is root, and SUDO_USER is set"""
603
- display_report_file("report.html", "html")
614
+ with self.assertRaises(SystemExit):
615
+ display_report_file("/tmp/report.html", "html")
604
616
  mock_is_root.assert_called_once()
605
617
  mock_env_get.assert_any_call("SUDO_USER")
606
- mock_env_get.assert_any_call("XDG_SESSION_TYPE")
607
618
  mock_subprocess_call.assert_called_once_with(
608
- ["sudo", "-E", "-u", "testuser", "xdg-open", "report.html"]
619
+ [
620
+ "systemd-run",
621
+ "--user",
622
+ "--machine=testuser@.host",
623
+ "xdg-open",
624
+ "/tmp/report.html",
625
+ ]
609
626
  )
610
627
 
611
628
  @patch("amd_debug.s2idle.is_root", return_value=True)
test_ttm.py ADDED
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/python3
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ """
5
+ This module contains unit tests for the ttm tool in the amd-debug-tools package.
6
+ """
7
+ import unittest
8
+ import sys
9
+ import logging
10
+ from unittest import mock
11
+
12
+ from amd_debug.ttm import main, parse_args, AmdTtmTool, maybe_reboot
13
+
14
+
15
+ class TestParseArgs(unittest.TestCase):
16
+ """Test parse_args function"""
17
+
18
+ @classmethod
19
+ def setUpClass(cls):
20
+ logging.basicConfig(filename="/dev/null", level=logging.DEBUG)
21
+
22
+ def setUp(self):
23
+ self.default_sys_argv = sys.argv
24
+
25
+ def tearDown(self):
26
+ sys.argv = self.default_sys_argv
27
+
28
+ @mock.patch.object(sys, "argv", new=["ttm", "--version"])
29
+ def test_parse_args_version(self):
30
+ """Test version argument"""
31
+ args = parse_args()
32
+ self.assertTrue(args.version)
33
+ self.assertFalse(args.set)
34
+ self.assertFalse(args.clear)
35
+
36
+ @mock.patch.object(sys, "argv", new=["ttm", "--clear"])
37
+ def test_parse_args_clear(self):
38
+ """Test clear argument"""
39
+ args = parse_args()
40
+ self.assertFalse(args.version)
41
+ self.assertFalse(args.set)
42
+ self.assertTrue(args.clear)
43
+
44
+
45
+ class TestMainFunction(unittest.TestCase):
46
+ """Test main() function logic"""
47
+
48
+ @mock.patch("amd_debug.ttm.parse_args")
49
+ @mock.patch("amd_debug.ttm.version", return_value="1.2.3")
50
+ @mock.patch("builtins.print")
51
+ def test_main_version(self, mock_print, _mock_version, mock_parse_args):
52
+ """Test main function with version argument"""
53
+ mock_parse_args.return_value = mock.Mock(
54
+ version=True, set=None, clear=False, tool_debug=False
55
+ )
56
+ ret = main()
57
+ mock_print.assert_called_with("1.2.3")
58
+ self.assertIsNone(ret)
59
+
60
+ @mock.patch("amd_debug.ttm.parse_args")
61
+ @mock.patch("amd_debug.ttm.AmdTtmTool")
62
+ @mock.patch("builtins.print")
63
+ def test_main_set_invalid(self, mock_print, _mock_tool, mock_parse_args):
64
+ """Test main function with invalid set argument"""
65
+ mock_parse_args.return_value = mock.Mock(
66
+ version=False, set=0, clear=False, tool_debug=False
67
+ )
68
+ ret = main()
69
+ mock_print.assert_called_with("Error: GB value must be greater than 0")
70
+ self.assertEqual(ret, 1)
71
+
72
+ @mock.patch("amd_debug.ttm.parse_args")
73
+ @mock.patch("amd_debug.ttm.AmdTtmTool")
74
+ def test_main_set_valid(self, mock_tool, mock_parse_args):
75
+ """Test main function with set argument"""
76
+ instance = mock_tool.return_value
77
+ instance.set.return_value = True
78
+ mock_parse_args.return_value = mock.Mock(
79
+ version=False, set=2, clear=False, tool_debug=False
80
+ )
81
+ ret = main()
82
+ instance.set.assert_called_with(2)
83
+ self.assertIsNone(ret)
84
+
85
+ @mock.patch("amd_debug.ttm.parse_args")
86
+ @mock.patch("amd_debug.ttm.AmdTtmTool")
87
+ def test_main_set_failed(self, mock_tool, mock_parse_args):
88
+ instance = mock_tool.return_value
89
+ instance.set.return_value = False
90
+ mock_parse_args.return_value = mock.Mock(
91
+ version=False, set=2, clear=False, tool_debug=False
92
+ )
93
+ ret = main()
94
+ instance.set.assert_called_with(2)
95
+ self.assertEqual(ret, 1)
96
+
97
+ @mock.patch("amd_debug.ttm.parse_args")
98
+ @mock.patch("amd_debug.ttm.AmdTtmTool")
99
+ def test_main_clear_success(self, mock_tool, mock_parse_args):
100
+ """Test main function with clear argument"""
101
+ instance = mock_tool.return_value
102
+ instance.clear.return_value = True
103
+ mock_parse_args.return_value = mock.Mock(
104
+ version=False, set=None, clear=True, tool_debug=False
105
+ )
106
+ ret = main()
107
+ instance.clear.assert_called_once()
108
+ self.assertIsNone(ret)
109
+
110
+ @mock.patch("amd_debug.ttm.parse_args")
111
+ @mock.patch("amd_debug.ttm.AmdTtmTool")
112
+ def test_main_clear_failed(self, mock_tool, mock_parse_args):
113
+ """Test main function with clear argument failure"""
114
+ instance = mock_tool.return_value
115
+ instance.clear.return_value = False
116
+ mock_parse_args.return_value = mock.Mock(
117
+ version=False, set=None, clear=True, tool_debug=False
118
+ )
119
+ ret = main()
120
+ instance.clear.assert_called_once()
121
+ self.assertEqual(ret, 1)
122
+
123
+ @mock.patch("amd_debug.ttm.parse_args")
124
+ @mock.patch("amd_debug.ttm.AmdTtmTool")
125
+ def test_main_get_success(self, mock_tool, mock_parse_args):
126
+ """Test main function with get argument"""
127
+ instance = mock_tool.return_value
128
+ instance.get.return_value = True
129
+ mock_parse_args.return_value = mock.Mock(
130
+ version=False, set=None, clear=False, tool_debug=False
131
+ )
132
+ ret = main()
133
+ instance.get.assert_called_once()
134
+ self.assertIsNone(ret)
135
+
136
+ @mock.patch("amd_debug.ttm.parse_args")
137
+ @mock.patch("amd_debug.ttm.AmdTtmTool")
138
+ def test_main_get_failed(self, mock_tool, mock_parse_args):
139
+ """Test main function with get argument failure"""
140
+ instance = mock_tool.return_value
141
+ instance.get.return_value = False
142
+ mock_parse_args.return_value = mock.Mock(
143
+ version=False, set=None, clear=False, tool_debug=False
144
+ )
145
+ ret = main()
146
+ instance.get.assert_called_once()
147
+ self.assertEqual(ret, 1)
148
+
149
+
150
+ class TestMaybeReboot(unittest.TestCase):
151
+ """Test maybe_reboot function"""
152
+
153
+ @mock.patch("builtins.input", return_value="y")
154
+ @mock.patch("amd_debug.ttm.reboot", return_value=True)
155
+ def test_maybe_reboot_yes(self, mock_reboot, _mock_input):
156
+ """Test reboot confirmation and execution"""
157
+ result = maybe_reboot()
158
+ mock_reboot.assert_called_once()
159
+ self.assertTrue(result)
160
+
161
+ @mock.patch("builtins.input", return_value="n")
162
+ @mock.patch("amd_debug.ttm.reboot", return_value=True)
163
+ def test_maybe_reboot_no(self, mock_reboot, _mock_input):
164
+ """Test reboot confirmation without execution"""
165
+ result = maybe_reboot()
166
+ mock_reboot.assert_not_called()
167
+ self.assertTrue(result)
168
+
169
+
170
+ class TestAmdTtmTool(unittest.TestCase):
171
+ """Unit tests for AmdTtmTool class"""
172
+
173
+ def setUp(self):
174
+ self.tool = AmdTtmTool(logging=False)
175
+
176
+ @mock.patch("builtins.open", new_callable=mock.mock_open, read_data="4096")
177
+ @mock.patch("amd_debug.ttm.bytes_to_gb", return_value=4.0)
178
+ @mock.patch("amd_debug.ttm.print_color")
179
+ @mock.patch("amd_debug.ttm.get_system_mem", return_value=16.0)
180
+ def test_get_success(self, _mock_mem, mock_print, _mock_bytes_to_gb, mock_open):
181
+ """Test get() when TTM_PARAM_PATH exists"""
182
+ result = self.tool.get()
183
+ mock_open.assert_called_once_with(
184
+ "/sys/module/ttm/parameters/pages_limit", "r", encoding="utf-8"
185
+ )
186
+ mock_print.assert_any_call(
187
+ "Current TTM pages limit: 4096 pages (4.00 GB)", "💻"
188
+ )
189
+ mock_print.assert_any_call("Total system memory: 16.00 GB", "💻")
190
+ self.assertTrue(result)
191
+
192
+ @mock.patch("builtins.open", side_effect=FileNotFoundError)
193
+ @mock.patch("amd_debug.ttm.print_color")
194
+ def test_get_file_not_found(self, mock_print, _mock_open):
195
+ """Test get() when TTM_PARAM_PATH does not exist"""
196
+ result = self.tool.get()
197
+ mock_print.assert_called_with(
198
+ "Error: Could not find /sys/module/ttm/parameters/pages_limit", "❌"
199
+ )
200
+ self.assertFalse(result)
201
+
202
+ @mock.patch("amd_debug.ttm.relaunch_sudo", return_value=True)
203
+ @mock.patch("amd_debug.ttm.get_system_mem", return_value=8.0)
204
+ @mock.patch("amd_debug.ttm.print_color")
205
+ def test_set_gb_greater_than_total(
206
+ self, mock_print, _mock_mem, _mock_relaunch_sudo
207
+ ):
208
+ """Test set() when gb_value > total system memory"""
209
+ result = self.tool.set(16)
210
+ mock_print.assert_any_call(
211
+ "16.00 GB is greater than total system memory (8.00 GB)", "❌"
212
+ )
213
+ self.assertFalse(result)
214
+
215
+ @mock.patch("amd_debug.ttm.relaunch_sudo", return_value=True)
216
+ @mock.patch("amd_debug.ttm.get_system_mem", return_value=10.0)
217
+ @mock.patch("amd_debug.ttm.print_color")
218
+ @mock.patch("builtins.input", return_value="n")
219
+ def test_set_gb_exceeds_max_percentage_cancel(
220
+ self, _mock_input, mock_print, _mock_mem, mock_relaunch_sudo
221
+ ):
222
+ """Test set() when gb_value exceeds max percentage and user cancels"""
223
+ result = self.tool.set(9.5)
224
+ self.assertFalse(result)
225
+ mock_print.assert_any_call("Operation cancelled.", "🚦")
226
+
227
+ @mock.patch("amd_debug.ttm.relaunch_sudo", return_value=True)
228
+ @mock.patch("amd_debug.ttm.get_system_mem", return_value=10.0)
229
+ @mock.patch("amd_debug.ttm.gb_to_pages", return_value=20480)
230
+ @mock.patch("amd_debug.ttm.print_color")
231
+ @mock.patch("builtins.open", new_callable=mock.mock_open)
232
+ @mock.patch("builtins.input", return_value="y")
233
+ @mock.patch("amd_debug.ttm.maybe_reboot", return_value=True)
234
+ def test_set_success(
235
+ self,
236
+ _mock_reboot,
237
+ _mock_input,
238
+ mock_open,
239
+ mock_print,
240
+ _mock_gb_to_pages,
241
+ _mock_mem,
242
+ _relaunch_sudo,
243
+ ):
244
+ """Test set() success path"""
245
+ result = self.tool.set(5)
246
+ mock_open.assert_called_once_with(
247
+ "/etc/modprobe.d/ttm.conf", "w", encoding="utf-8"
248
+ )
249
+ mock_print.assert_any_call(
250
+ "Successfully set TTM pages limit to 20480 pages (5.00 GB)", "🐧"
251
+ )
252
+ self.assertTrue(result)
253
+
254
+ @mock.patch("os.path.exists", return_value=False)
255
+ @mock.patch("amd_debug.ttm.print_color")
256
+ def test_clear_file_not_exists(self, mock_print, _mock_exists):
257
+ """Test clear() when config file does not exist"""
258
+ result = self.tool.clear()
259
+ mock_print.assert_called_with("/etc/modprobe.d/ttm.conf doesn't exist", "❌")
260
+ self.assertFalse(result)
261
+
262
+ @mock.patch("os.path.exists", return_value=True)
263
+ @mock.patch("amd_debug.ttm.relaunch_sudo", return_value=True)
264
+ @mock.patch("os.remove")
265
+ @mock.patch("amd_debug.ttm.print_color")
266
+ @mock.patch("amd_debug.ttm.maybe_reboot", return_value=True)
267
+ def test_clear_success(
268
+ self, _mock_reboot, mock_print, mock_remove, _mock_relaunch_sudo, _mock_exists
269
+ ):
270
+ """Test clear() success path"""
271
+ result = self.tool.clear()
272
+ mock_remove.assert_called_once_with("/etc/modprobe.d/ttm.conf")
273
+ mock_print.assert_any_call(
274
+ "Configuration /etc/modprobe.d/ttm.conf removed", "🐧"
275
+ )
276
+ self.assertTrue(result)
test_validator.py CHANGED
@@ -44,7 +44,7 @@ class TestValidatorHelpers(unittest.TestCase):
44
44
  return "Test function executed"
45
45
 
46
46
  # Mock /sys/power/pm_debug_messages existing and all ACPI existing
47
- with patch("builtins.open", new_callable=mock_open, read_data="0") as mock_file:
47
+ with patch("amd_debug.validator.open", new_callable=mock_open, read_data="0") as mock_file:
48
48
  handlers = (
49
49
  mock_file.return_value,
50
50
  mock_open(read_data="0").return_value,
@@ -57,7 +57,7 @@ class TestValidatorHelpers(unittest.TestCase):
57
57
 
58
58
  # Mock /sys/power/pm_debug_messages missing
59
59
  with patch(
60
- "builtins.open", side_effect=FileNotFoundError("not found")
60
+ "amd_debug.validator.open", side_effect=FileNotFoundError("not found")
61
61
  ) as mock_file:
62
62
  with self.assertRaises(FileNotFoundError):
63
63
  result = test_function()
@@ -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
- def setUp(self, _db_mock):
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
 
@@ -263,7 +264,7 @@ class TestValidator(unittest.TestCase):
263
264
 
264
265
  # Validate debug messages
265
266
  mock_record_debug.assert_called_once_with(
266
- "Woke up from input source /sys/devices/input0 (3->5)", "💤"
267
+ "Woke up from input source /sys/devices/input0 (3->5)"
267
268
  )
268
269
 
269
270
  # Stop patches
@@ -556,7 +557,7 @@ class TestValidator(unittest.TestCase):
556
557
  # Set attributes for record_cycle
557
558
  self.validator.requested_duration = 60
558
559
  self.validator.active_gpios = ["GPIO1"]
559
- self.validator.wakeup_irqs = [5]
560
+ self.validator.wakeup_irqs = ["5"]
560
561
  self.validator.kernel_duration = 1.5
561
562
  self.validator.hw_sleep_duration = 1.0
562
563
 
@@ -579,10 +580,10 @@ class TestValidator(unittest.TestCase):
579
580
  # Assert record_cycle was called with correct arguments
580
581
  mock_record_cycle.assert_called_once_with(
581
582
  self.validator.requested_duration,
582
- self.validator.active_gpios,
583
- self.validator.wakeup_irqs,
584
- self.validator.kernel_duration,
585
- self.validator.hw_sleep_duration,
583
+ ",".join(str(gpio) for gpio in self.validator.active_gpios),
584
+ ",".join(str(irq) for irq in self.validator.wakeup_irqs),
585
+ int(self.validator.kernel_duration),
586
+ int(self.validator.hw_sleep_duration),
586
587
  )
587
588
 
588
589
  def test_program_wakealarm(self):
@@ -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()
@@ -1,181 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: amd-debug-tools
3
- Version: 0.2.5
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
- [![codecov](https://codecov.io/github/superm1/amd-debug-tools/graph/badge.svg?token=Z9WTBZADGT)](https://codecov.io/github/superm1/amd-debug-tools)
27
- [![PyPI](https://img.shields.io/pypi/v/amd-debug-tools.svg)](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
- ## Compatibility scripts
176
-
177
- Compatibility scripts are provided for the previous names the tools went by:
178
- `amd_s2idle.py`, `amd_bios.py` and `amd_pstate.py`.
179
- These allow cloning the repository and running the scripts without installing
180
- the package.
181
-