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.
amd_debug/__init__.py CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/python3
2
1
  # SPDX-License-Identifier: MIT
3
2
 
4
3
 
@@ -23,8 +22,15 @@ def amd_pstate():
23
22
  return pstate.main()
24
23
 
25
24
 
25
+ def amd_ttm():
26
+ """Launch the amd-ttm tool."""
27
+ from . import ttm # pylint: disable=import-outside-toplevel
28
+
29
+ return ttm.main()
30
+
31
+
26
32
  def install_dep_superset():
27
- """Install all supserset dependencies."""
33
+ """Install all superset dependencies."""
28
34
  from . import installer # pylint: disable=import-outside-toplevel
29
35
 
30
36
  return installer.install_dep_superset()
@@ -36,6 +42,7 @@ def launch_tool(tool_name):
36
42
  "amd_s2idle.py": amd_s2idle,
37
43
  "amd_bios.py": amd_bios,
38
44
  "amd_pstate.py": amd_pstate,
45
+ "amd_ttm.py": amd_ttm,
39
46
  "install_deps.py": install_dep_superset,
40
47
  }
41
48
  if tool_name in tools:
amd_debug/acpi.py CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/python3
2
1
  # SPDX-License-Identifier: MIT
3
2
 
4
3
  import os
amd_debug/battery.py CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/python3
2
1
  # SPDX-License-Identifier: MIT
3
2
  from pyudev import Context
4
3
 
amd_debug/bios.py CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/python3
2
1
  # SPDX-License-Identifier: MIT
3
2
  """s2idle analysis tool"""
4
3
  import argparse
@@ -105,7 +104,9 @@ def parse_args():
105
104
  action="store_true",
106
105
  help="Enable tool debug logging",
107
106
  )
108
- subparsers.add_parser("version", help="Show version information")
107
+ parser.add_argument(
108
+ "--version", action="store_true", help="Show version information"
109
+ )
109
110
 
110
111
  if len(sys.argv) == 1:
111
112
  parser.print_help(sys.stderr)
@@ -122,7 +123,7 @@ def parse_args():
122
123
  return args
123
124
 
124
125
 
125
- def main() -> None|int:
126
+ def main() -> None | int:
126
127
  """Main function"""
127
128
  args = parse_args()
128
129
  ret = False
@@ -132,7 +133,7 @@ def main() -> None|int:
132
133
  elif args.command == "parse":
133
134
  app = AmdBios(args.input, args.tool_debug)
134
135
  ret = app.run()
135
- elif args.command == "version":
136
+ elif args.version:
136
137
  print(version())
137
138
  show_log_info()
138
139
  if ret is False:
amd_debug/common.py CHANGED
@@ -1,10 +1,10 @@
1
- #!/usr/bin/python3
2
1
  # SPDX-License-Identifier: MIT
3
2
 
4
3
  """
5
4
  This module contains common utility functions and classes for various amd-debug-tools.
6
5
  """
7
6
 
7
+ import asyncio
8
8
  import importlib.metadata
9
9
  import logging
10
10
  import os
@@ -12,7 +12,6 @@ import platform
12
12
  import time
13
13
  import struct
14
14
  import subprocess
15
- import re
16
15
  import sys
17
16
  from ast import literal_eval
18
17
  from datetime import date, timedelta
@@ -47,7 +46,7 @@ def get_group_color(group) -> str:
47
46
  color = Colors.WARNING
48
47
  elif group == "🗣️":
49
48
  color = Colors.HEADER
50
- elif group == "💯":
49
+ elif any(mk in group for mk in ["💯", "🚫"]):
51
50
  color = Colors.UNDERLINE
52
51
  elif any(mk in group for mk in ["🦟", "🖴"]):
53
52
  color = Colors.DEBUG
@@ -226,6 +225,74 @@ def get_pretty_distro() -> str:
226
225
  return distro
227
226
 
228
227
 
228
+ def bytes_to_gb(bytes_value):
229
+ """Convert bytes to GB"""
230
+ return bytes_value * 4096 / (1024 * 1024 * 1024)
231
+
232
+
233
+ def gb_to_pages(gb_value):
234
+ """Convert GB into bytes"""
235
+ return int(gb_value * (1024 * 1024 * 1024) / 4096)
236
+
237
+
238
+ def reboot():
239
+ """Reboot the system"""
240
+
241
+ async def reboot_dbus_fast():
242
+ """Reboot using dbus-fast"""
243
+ try:
244
+ from dbus_fast.aio import ( # pylint: disable=import-outside-toplevel
245
+ MessageBus,
246
+ )
247
+ from dbus_fast import BusType # pylint: disable=import-outside-toplevel
248
+
249
+ bus = await MessageBus(bus_type=BusType.SYSTEM).connect()
250
+ introspection = await bus.introspect(
251
+ "org.freedesktop.login1", "/org/freedesktop/login1"
252
+ )
253
+ proxy_obj = bus.get_proxy_object(
254
+ "org.freedesktop.login1", "/org/freedesktop/login1", introspection
255
+ )
256
+ interface = proxy_obj.get_interface("org.freedesktop.login1.Manager")
257
+ await interface.call_reboot(True)
258
+
259
+ except ImportError:
260
+ return False
261
+ return True
262
+
263
+ def reboot_dbus():
264
+ """Reboot using python-dbus"""
265
+ try:
266
+ import dbus # pylint: disable=import-outside-toplevel
267
+
268
+ bus = dbus.SystemBus()
269
+ obj = bus.get_object("org.freedesktop.login1", "/org/freedesktop/login1")
270
+ intf = dbus.Interface(obj, "org.freedesktop.login1.Manager")
271
+ intf.Reboot(True)
272
+ except ImportError:
273
+ return False
274
+ return True
275
+
276
+ loop = asyncio.get_event_loop()
277
+ result = loop.run_until_complete(reboot_dbus_fast())
278
+ if not result:
279
+ return reboot_dbus()
280
+
281
+ return True
282
+
283
+
284
+ def get_system_mem():
285
+ """Get the total system memory in GB using /proc/meminfo"""
286
+ with open(os.path.join("/", "proc", "meminfo"), "r", encoding="utf-8") as f:
287
+ for line in f:
288
+ if line.startswith("MemTotal:"):
289
+ # MemTotal line format: "MemTotal: 16384516 kB"
290
+ # Extract the number and convert from kB to GB
291
+ mem_kb = int(line.split()[1])
292
+ return mem_kb / (1024 * 1024)
293
+ raise ValueError("Could not find MemTotal in /proc/meminfo")
294
+
295
+
229
296
  def is_root() -> bool:
230
297
  """Check if the user is root"""
231
298
  return os.geteuid() == 0
amd_debug/database.py CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/python3
2
1
  # SPDX-License-Identifier: MIT
3
2
 
4
3
  from datetime import datetime
@@ -99,8 +98,8 @@ class SleepDatabase:
99
98
 
100
99
  def start_cycle(self, timestamp):
101
100
  """Start a new sleep cycle"""
101
+ assert self.db
102
102
  self.last_suspend = timestamp
103
-
104
103
  # increment the counters so that systemd hooks work
105
104
  cur = self.db.cursor()
106
105
  cur.execute(
@@ -124,11 +123,13 @@ class SleepDatabase:
124
123
 
125
124
  def sync(self) -> None:
126
125
  """Sync the database to disk"""
126
+ assert self.db
127
127
  self.db.commit()
128
128
 
129
129
  def record_debug(self, message, level=6) -> None:
130
130
  """Helper function to record a message to debug database"""
131
131
  assert self.last_suspend
132
+ assert self.db
132
133
  cur = self.db.cursor()
133
134
  cur.execute(
134
135
  "INSERT into debug (t0, id, message, priority) VALUES (?, ?, ?, ?)",
@@ -151,6 +152,8 @@ class SleepDatabase:
151
152
 
152
153
  def record_battery_energy(self, name, energy, full, unit):
153
154
  """Helper function to record battery energy"""
155
+ assert self.db
156
+ assert self.last_suspend
154
157
  cur = self.db.cursor()
155
158
  cur.execute(
156
159
  "SELECT * FROM battery WHERE t0=?",
@@ -180,6 +183,7 @@ class SleepDatabase:
180
183
  def record_cycle_data(self, message, symbol) -> None:
181
184
  """Helper function to record a message to cycle_data database"""
182
185
  assert self.last_suspend
186
+ assert self.db
183
187
  cur = self.db.cursor()
184
188
  cur.execute(
185
189
  """
@@ -207,6 +211,7 @@ class SleepDatabase:
207
211
  ) -> None:
208
212
  """Helper function to record a sleep cycle into the cycle database"""
209
213
  assert self.last_suspend
214
+ assert self.db
210
215
  cur = self.db.cursor()
211
216
  cur.execute(
212
217
  """
@@ -227,6 +232,7 @@ class SleepDatabase:
227
232
  def record_prereq(self, message, symbol) -> None:
228
233
  """Helper function to record a message to prereq_data database"""
229
234
  assert self.last_suspend
235
+ assert self.db
230
236
  cur = self.db.cursor()
231
237
  cur.execute(
232
238
  """
@@ -246,6 +252,7 @@ class SleepDatabase:
246
252
 
247
253
  def report_prereq(self, t0) -> list:
248
254
  """Helper function to report the prereq_data database"""
255
+ assert self.db
249
256
  if t0 is None:
250
257
  return []
251
258
  cur = self.db.cursor()
@@ -255,10 +262,11 @@ class SleepDatabase:
255
262
  )
256
263
  return cur.fetchall()
257
264
 
258
- def report_debug(self, t0) -> str:
265
+ def report_debug(self, t0) -> list:
259
266
  """Helper function to report the debug database"""
267
+ assert self.db
260
268
  if t0 is None:
261
- return ""
269
+ return []
262
270
  cur = self.db.cursor()
263
271
  cur.execute(
264
272
  "SELECT message, priority FROM debug WHERE t0=?",
@@ -268,6 +276,7 @@ class SleepDatabase:
268
276
 
269
277
  def report_cycle(self, t0=None) -> list:
270
278
  """Helper function to report a cycle from database"""
279
+ assert self.db
271
280
  if t0 is None:
272
281
  assert self.last_suspend
273
282
  t0 = self.last_suspend
@@ -280,7 +289,9 @@ class SleepDatabase:
280
289
 
281
290
  def report_cycle_data(self, t0=None) -> str:
282
291
  """Helper function to report a table matching a timestamp from cycle_data database"""
292
+ assert self.db
283
293
  if t0 is None:
294
+ assert self.last_suspend
284
295
  t0 = self.last_suspend
285
296
  cur = self.db.cursor()
286
297
  cur.execute(
@@ -294,7 +305,9 @@ class SleepDatabase:
294
305
 
295
306
  def report_battery(self, t0=None) -> list:
296
307
  """Helper function to report a line from battery database"""
308
+ assert self.db
297
309
  if t0 is None:
310
+ assert self.last_suspend
298
311
  t0 = self.last_suspend
299
312
  cur = self.db.cursor()
300
313
  cur.execute(
@@ -305,19 +318,23 @@ class SleepDatabase:
305
318
 
306
319
  def get_last_prereq_ts(self) -> int:
307
320
  """Helper function to report the last line from prereq database"""
321
+ assert self.db
308
322
  cur = self.db.cursor()
309
323
  cur.execute("SELECT * FROM prereq_data ORDER BY t0 DESC LIMIT 1")
310
324
  result = cur.fetchone()
311
- return result[0] if result else None
325
+ return result[0] if result else 0
312
326
 
313
327
  def get_last_cycle(self) -> list:
314
328
  """Helper function to report the last line from battery database"""
329
+ assert self.db
315
330
  cur = self.db.cursor()
316
331
  cur.execute("SELECT t0 FROM cycle ORDER BY t0 DESC LIMIT 1")
317
332
  return cur.fetchone()
318
333
 
319
334
  def report_summary_dataframe(self, since, until) -> object:
320
335
  """Helper function to report a dataframe from the database"""
336
+ assert self.db
337
+
321
338
  import pandas as pd # pylint: disable=import-outside-toplevel
322
339
 
323
340
  pd.set_option("display.precision", 2)
amd_debug/display.py CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env python3
2
1
  # SPDX-License-Identifier: MIT
3
2
  """Display analysis"""
4
3
  import os
@@ -12,7 +11,7 @@ class Display:
12
11
 
13
12
  def __init__(self):
14
13
  self.pyudev = Context()
15
- self.edid = {}
14
+ self.edid = []
16
15
 
17
16
  for dev in self.pyudev.list_devices(subsystem="drm"):
18
17
  if not "card" in dev.device_path:
@@ -27,7 +26,7 @@ class Display:
27
26
  f = read_file(p)
28
27
  if f != "enabled":
29
28
  continue
30
- self.edid[dev.sys_name] = os.path.join(dev.sys_path, "edid")
29
+ self.edid.append(os.path.join(dev.sys_path, "edid"))
31
30
 
32
31
  def get_edid(self) -> list:
33
32
  """Get the path for EDID data for all connected displays"""
amd_debug/failures.py CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/python3
2
1
  # SPDX-License-Identifier: MIT
3
2
 
4
3
  from datetime import timedelta
@@ -48,6 +47,19 @@ class RtcAlarmWrong(S0i3Failure):
48
47
  self.url = "https://github.com/systemd/systemd/issues/24279"
49
48
 
50
49
 
50
+ class MissingGpu(S0i3Failure):
51
+ """GPU device is missing"""
52
+
53
+ def __init__(self):
54
+ super().__init__()
55
+ self.description = "GPU device is missing"
56
+ self.explanation = (
57
+ "Running the s2idle sequence without an integrated GPU is likely "
58
+ "to cause problems. If you have a mux in BIOS, enable the integrated "
59
+ "GPU."
60
+ )
61
+
62
+
51
63
  class MissingAmdgpu(S0i3Failure):
52
64
  """AMDGPU driver is missing"""
53
65
 
@@ -467,7 +479,7 @@ class MissingIommuACPI(S0i3Failure):
467
479
  super().__init__()
468
480
  self.description = f"Device {device} missing from ACPI tables"
469
481
  self.explanation = (
470
- "The ACPI device {device} is required for suspend to work when the IOMMU is enabled. "
482
+ f"The ACPI device {device} is required for suspend to work when the IOMMU is enabled. "
471
483
  "Please check your BIOS settings and if configured correctly, report a bug to your system vendor."
472
484
  )
473
485
  self.url = "https://gitlab.freedesktop.org/drm/amd/-/issues/3738#note_2667140"
@@ -598,3 +610,32 @@ class DmcubTooOld(S0i3Failure):
598
610
  f"The DMCUB microcode version {hex(current)} is older than the"
599
611
  f"minimum suggested version {hex(expected)}."
600
612
  )
613
+
614
+
615
+ class MissingIsp4PlatformDriver(S0i3Failure):
616
+ """ISP4 platform driver is missing"""
617
+
618
+ def __init__(self):
619
+ super().__init__()
620
+ self.description = "ISP4 platform driver is missing"
621
+ self.explanation = (
622
+ "The ISP4 platform driver is required for the camera interface included "
623
+ "with the SOC to enter the proper power states. "
624
+ "Be sure that you have enabled CONFIG_AMD_ISP_PLATFORM in your kernel."
625
+ )
626
+
627
+
628
+ class MissingAmdCaptureModule(S0i3Failure):
629
+ """AMD Capture module is missing"""
630
+
631
+ def __init__(self):
632
+ super().__init__()
633
+ self.description = "AMD Capture module is missing"
634
+ self.explanation = (
635
+ "The amd_capture module is required for the camera interface included "
636
+ "with the SOC to enter the proper power states. "
637
+ "Be sure that the amd_capture module is loaded."
638
+ ""
639
+ "If the module is not available, disable the camera in the BIOS to prevent issues."
640
+ )
641
+ self.url = "https://gitlab.freedesktop.org/drm/amd/-/issues/4869"
amd_debug/installer.py CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/python3
2
1
  # SPDX-License-Identifier: MIT
3
2
 
4
3
  """
@@ -65,7 +64,7 @@ class DistroPackage:
65
64
  for line in release.split("\n"):
66
65
  if line.startswith("VARIANT_ID"):
67
66
  variant = line.split("=")[-1]
68
- if variant != "workstation":
67
+ if variant not in ("workstation", "kde"):
69
68
  return False
70
69
  installer = ["dnf", "install", "-y", self.rpm]
71
70
  elif dist == "arch" or os.path.exists("/etc/arch-release"):
@@ -197,7 +196,7 @@ class DisplayInfoPackage(DistroPackage):
197
196
  def __init__(self):
198
197
  super().__init__(
199
198
  deb="libdisplay-info-bin",
200
- rpm="libdisplay-info",
199
+ rpm="libdisplay-info-tools",
201
200
  arch="libdisplay-info",
202
201
  message=Headers.MissingDiEdidDecode,
203
202
  )
@@ -446,8 +445,8 @@ def parse_args():
446
445
  return parser.parse_args()
447
446
 
448
447
 
449
- def install_dep_superset() -> None|int:
450
- """Install all python supserset dependencies"""
448
+ def install_dep_superset() -> None | int:
449
+ """Install all python superset dependencies"""
451
450
  args = parse_args()
452
451
  tool = Installer(tool_debug=args.tool_debug)
453
452
  tool.set_requirements(
amd_debug/kernel.py CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env python3
2
1
  # SPDX-License-Identifier: MIT
3
2
  """Kernel log analysis"""
4
3
 
@@ -158,7 +157,7 @@ class KernelLogger:
158
157
  def seek_tail(self, tim=None):
159
158
  """Seek to the end of the log"""
160
159
 
161
- def process_callback(self, callback, priority):
160
+ def process_callback(self, callback, priority=None):
162
161
  """Process the log"""
163
162
 
164
163
  def match_line(self, _matches) -> str:
@@ -175,7 +174,7 @@ class InputFile(KernelLogger):
175
174
 
176
175
  def __init__(self, fname):
177
176
  self.since_support = False
178
- self.buffer = None
177
+ self.buffer = ""
179
178
  self.seeked = False
180
179
  self.buffer = read_file(fname)
181
180
 
@@ -190,7 +189,7 @@ class DmesgLogger(KernelLogger):
190
189
 
191
190
  def __init__(self):
192
191
  self.since_support = False
193
- self.buffer = None
192
+ self.buffer = ""
194
193
  self.seeked = False
195
194
 
196
195
  cmd = ["dmesg", "-h"]
@@ -204,7 +203,7 @@ class DmesgLogger(KernelLogger):
204
203
  self._refresh_head()
205
204
 
206
205
  def _refresh_head(self):
207
- self.buffer = []
206
+ self.buffer = ""
208
207
  self.seeked = False
209
208
  result = subprocess.run(self.command, check=True, capture_output=True)
210
209
  if result.returncode == 0:
@@ -262,7 +261,11 @@ class CySystemdLogger(KernelLogger):
262
261
  """Class for logging using systemd journal using cython"""
263
262
 
264
263
  def __init__(self):
265
- from cysystemd.reader import JournalReader, JournalOpenMode, Rule
264
+ from cysystemd.reader import (
265
+ JournalReader,
266
+ JournalOpenMode,
267
+ Rule,
268
+ ) # pylint: disable=import-outside-toplevel
266
269
 
267
270
  boot_reader = JournalReader()
268
271
  boot_reader.open(JournalOpenMode.SYSTEM)
@@ -384,6 +387,6 @@ def get_kernel_log(input_file=None) -> KernelLogger:
384
387
  kernel_log = DmesgLogger()
385
388
  except subprocess.CalledProcessError as e:
386
389
  fatal_error(f"{e}")
387
- kernel_log = None
390
+ kernel_log = KernelLogger()
388
391
  logging.debug("Kernel log provider: %s", kernel_log.__class__.__name__)
389
392
  return kernel_log