CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
2027 lines
69 KiB
Python
2027 lines
69 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
AMD SMI Complete API Test Script
|
|
================================
|
|
|
|
PURPOSE
|
|
-------
|
|
Run a capability inventory of the AMD SMI (`amdsmi`) Python module against a
|
|
host. Each known API is invoked and classified into one of five buckets, then a
|
|
markdown report is emitted.
|
|
|
|
REQUIREMENTS
|
|
------------
|
|
* Linux host with ROCm/AMDSMI installed and the `amdgpu` kernel driver loaded
|
|
(`/dev/kfd` must exist).
|
|
* Python 3.8+ with the `amdsmi` Python bindings importable. If multiple
|
|
Python interpreters are installed (e.g. `python3` and `python3.9`), this
|
|
script will auto-detect one that has `amdsmi` and re-execute itself with
|
|
that interpreter, so you normally do NOT need to pick the right one
|
|
yourself. Override by invoking the script directly with the interpreter
|
|
you want.
|
|
* For full coverage of write/setter APIs, run the script as root:
|
|
sudo python3 test_amdsmi_all_apis.py
|
|
Without root, every API tagged as a "write" call is reported as
|
|
`Skipped (requires root)` rather than failing as `Unsupported`.
|
|
|
|
STATUS CATEGORIES
|
|
-----------------
|
|
* Supported The API exists, was called, and returned successfully.
|
|
* Unsupported The API exists but the underlying driver/hardware rejected
|
|
the call, OR the installed amdsmi build expects a different
|
|
call signature than this script provides
|
|
(`signature mismatch:` prefix in the note).
|
|
* Skipped The API was intentionally not executed. Reasons include:
|
|
risky write (e.g. GPU reset, partition change), requires
|
|
root and the script was run unprivileged, a required enum
|
|
member is not present in this amdsmi build, or no usable
|
|
prerequisite (e.g. no active GPU process) was available.
|
|
* Script Error A genuine bug in this test script (any non-signature
|
|
AttributeError/TypeError). Should be 0 on a clean run.
|
|
* API Not Found The symbol is not present in the installed amdsmi module.
|
|
|
|
USAGE
|
|
-----
|
|
python3 test_amdsmi_all_apis.py # primary GPU only
|
|
sudo python3 test_amdsmi_all_apis.py # include write APIs
|
|
python3 test_amdsmi_all_apis.py --gpu-index 1 # target a specific GPU
|
|
python3 test_amdsmi_all_apis.py --all-gpus # primary sweep + per-GPU sanity sweep
|
|
python3 test_amdsmi_all_apis.py --output ./report.md # custom markdown report path
|
|
python3 test_amdsmi_all_apis.py --skip-writes # never run setters
|
|
python3 test_amdsmi_all_apis.py --no-revert # do not restore mutated state
|
|
|
|
OUTPUT
|
|
------
|
|
A timestamped markdown report is written next to this script
|
|
(`test_amdsmi_all_apis_output_YYYYMMDD_HHMMSS.md`) unless `--output` is
|
|
supplied. The report contains a summary table, a per-API status row, and the
|
|
script version that produced it.
|
|
"""
|
|
|
|
__version__ = "1.1.0"
|
|
|
|
import argparse
|
|
import datetime
|
|
import importlib.util
|
|
import os
|
|
import platform
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Auto-detect a Python interpreter that has the amdsmi module installed.
|
|
# This must run BEFORE `import amdsmi`. If the current interpreter doesn't
|
|
# have amdsmi but another candidate does, re-execute this script with that
|
|
# interpreter so the user doesn't have to know which one to pick.
|
|
# ----------------------------------------------------------------------------
|
|
def _maybe_reexec_with_amdsmi():
|
|
if importlib.util.find_spec("amdsmi") is not None:
|
|
return # current interpreter is fine
|
|
# Allow callers to opt out (avoids accidental infinite re-exec loops).
|
|
if os.environ.get("AMDSMI_NO_REEXEC") == "1":
|
|
return
|
|
|
|
candidates = [
|
|
"python3",
|
|
"python3.13",
|
|
"python3.12",
|
|
"python3.11",
|
|
"python3.10",
|
|
"python3.9",
|
|
"python3.8",
|
|
"/opt/rocm/bin/python3",
|
|
"/usr/local/bin/python3",
|
|
]
|
|
seen = {os.path.realpath(sys.executable)}
|
|
for cand in candidates:
|
|
path = cand if os.path.isabs(cand) else shutil.which(cand)
|
|
if not path or not os.path.exists(path):
|
|
continue
|
|
real = os.path.realpath(path)
|
|
if real in seen:
|
|
continue
|
|
seen.add(real)
|
|
try:
|
|
r = subprocess.run([path, "-c", "import amdsmi"], capture_output=True, timeout=5)
|
|
except Exception:
|
|
continue
|
|
if r.returncode == 0:
|
|
print(
|
|
f"[INFO] amdsmi not importable in {sys.executable}; re-executing with {path}",
|
|
flush=True,
|
|
)
|
|
env = dict(os.environ)
|
|
env["AMDSMI_NO_REEXEC"] = "1"
|
|
try:
|
|
os.execve(path, [path] + sys.argv, env)
|
|
except OSError:
|
|
# Fall back to subprocess on platforms where execve is restricted.
|
|
sys.exit(subprocess.call([path] + sys.argv, env=env))
|
|
|
|
|
|
_maybe_reexec_with_amdsmi()
|
|
|
|
try:
|
|
import amdsmi
|
|
except ImportError as e:
|
|
print(f"ERROR: Cannot import amdsmi: {e}")
|
|
print("Tried this interpreter and any candidates found on PATH.")
|
|
print("Install the amdsmi Python bindings (e.g. via the ROCm/amd-smi packages),")
|
|
print("or invoke this script with the interpreter that already has them.")
|
|
sys.exit(1)
|
|
|
|
|
|
def is_root():
|
|
"""Cross-platform root/admin check."""
|
|
if hasattr(os, "geteuid"):
|
|
try:
|
|
return os.geteuid() == 0
|
|
except Exception:
|
|
return False
|
|
# Windows: best-effort admin check
|
|
try:
|
|
import ctypes
|
|
|
|
return bool(ctypes.windll.shell32.IsUserAnAdmin())
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def get_hostname():
|
|
try:
|
|
return platform.node() or "unknown"
|
|
except Exception:
|
|
return "unknown"
|
|
|
|
|
|
class _EnumMissing(Exception):
|
|
"""Raised inside a test lambda when a required amdsmi enum is not present."""
|
|
|
|
pass
|
|
|
|
|
|
def need(attr):
|
|
"""Return the amdsmi attribute or raise _EnumMissing. Used inside test lambdas."""
|
|
if not hasattr(amdsmi, attr):
|
|
raise _EnumMissing(f"amdsmi.{attr} not present in this build")
|
|
return getattr(amdsmi, attr)
|
|
|
|
|
|
# ============================================================================
|
|
# GLOBALS
|
|
# ============================================================================
|
|
results = []
|
|
gpu_handle = None
|
|
gpu_handles = []
|
|
socket_handles = []
|
|
cpu_socket_handles = []
|
|
cpu_core_handles = []
|
|
|
|
# Saved values for reverting
|
|
saved_values = {}
|
|
|
|
# Runtime configuration (set in main()).
|
|
CONFIG = {
|
|
"gpu_index": 0,
|
|
"output": None,
|
|
"skip_writes": False,
|
|
"no_revert": False,
|
|
"all_gpus": False,
|
|
}
|
|
|
|
# ============================================================================
|
|
# HELPER FUNCTIONS
|
|
# ============================================================================
|
|
|
|
|
|
def add_result(api_name, status, output="", note=""):
|
|
"""Add a test result to the results list."""
|
|
results.append(
|
|
{
|
|
"api": api_name,
|
|
"status": status,
|
|
"output": str(output)[:120] if output else "",
|
|
"note": note,
|
|
}
|
|
)
|
|
|
|
|
|
def test_api(api_name, test_func, skip_reason=None, requires_root=False):
|
|
"""
|
|
Test a single API and record the result.
|
|
|
|
Parameters:
|
|
api_name: Name of the API to test
|
|
test_func: Lambda/function to call the API (None if skipped)
|
|
skip_reason: If provided, API is not executed. Existence is still reported.
|
|
requires_root: If True and not running as root, the API is skipped instead of
|
|
executed (to avoid mislabeling permission errors as Unsupported).
|
|
"""
|
|
print(f"Testing: {api_name}... ", end="", flush=True)
|
|
# Allow tagged names like "[gpu1] amdsmi_get_gpu_device_bdf" — strip the
|
|
# tag for the existence check while keeping the tag in the report row.
|
|
lookup_name = (
|
|
api_name.split("] ", 1)[1] if api_name.startswith("[") and "] " in api_name else api_name
|
|
)
|
|
exists = hasattr(amdsmi, lookup_name)
|
|
|
|
# Explicit skip path: preserve existence information separately from skip.
|
|
if skip_reason:
|
|
if exists:
|
|
print(f"Skipped ({skip_reason})")
|
|
add_result(api_name, "Skipped", note=f"API exists, skipped: {skip_reason}")
|
|
else:
|
|
print("API Not Found")
|
|
add_result(api_name, "API Not Found", note=f"module amdsmi has no attribute {api_name}")
|
|
return
|
|
|
|
if not exists:
|
|
print("API Not Found")
|
|
add_result(api_name, "API Not Found", note=f"module amdsmi has no attribute {api_name}")
|
|
return
|
|
|
|
if requires_root:
|
|
if CONFIG.get("skip_writes"):
|
|
print("Skipped (--skip-writes)")
|
|
add_result(api_name, "Skipped", note="write API skipped via --skip-writes")
|
|
return
|
|
if not is_root():
|
|
print("Skipped (requires root)")
|
|
add_result(api_name, "Skipped", note="write/privileged API; skipped when not root")
|
|
return
|
|
|
|
# Try to call the API
|
|
try:
|
|
result = test_func()
|
|
print("Supported")
|
|
add_result(api_name, "Supported", output=result)
|
|
except _EnumMissing as e:
|
|
print("Skipped (enum missing)")
|
|
add_result(api_name, "Skipped", note=str(e)[:100])
|
|
except (AttributeError, TypeError) as e:
|
|
msg = str(e)
|
|
# Signature mismatches indicate the installed amdsmi build expects
|
|
# a different argument list than this script uses. That's a build
|
|
# difference, not a script bug, so report as Unsupported.
|
|
sig_markers = (
|
|
"missing",
|
|
"required positional",
|
|
"takes",
|
|
"arguments",
|
|
"positional argument",
|
|
)
|
|
if any(m in msg for m in sig_markers):
|
|
print("Unsupported")
|
|
add_result(api_name, "Unsupported", note=f"signature mismatch: {msg[:90]}")
|
|
else:
|
|
# Genuine script bug.
|
|
print("Script Error")
|
|
add_result(api_name, "Script Error", note=msg[:100])
|
|
except getattr(amdsmi, "AmdSmiLibraryException", Exception) as e:
|
|
print("Unsupported")
|
|
add_result(api_name, "Unsupported", note=str(e)[:100])
|
|
except Exception as e:
|
|
print("Unsupported")
|
|
add_result(api_name, "Unsupported", note=str(e)[:100])
|
|
|
|
|
|
def safe_get(func, default=None):
|
|
"""Safely get a value, returning default on any error."""
|
|
try:
|
|
return func()
|
|
except Exception:
|
|
return default
|
|
|
|
|
|
# ============================================================================
|
|
# MAIN TEST RUNNER
|
|
# ============================================================================
|
|
|
|
|
|
def run_tests():
|
|
global \
|
|
gpu_handle, \
|
|
gpu_handles, \
|
|
socket_handles, \
|
|
cpu_socket_handles, \
|
|
cpu_core_handles, \
|
|
saved_values
|
|
|
|
# ========================================================================
|
|
# INITIALIZATION
|
|
# ========================================================================
|
|
print("=" * 70)
|
|
print(f"AMD SMI COMPLETE API TEST (script v{__version__})")
|
|
print("=" * 70)
|
|
print()
|
|
print("Initializing AMD SMI...")
|
|
|
|
amdsmi.amdsmi_init()
|
|
|
|
# Get all handles
|
|
gpu_handles = amdsmi.amdsmi_get_processor_handles()
|
|
if not gpu_handles:
|
|
print("ERROR: No GPU handles found!")
|
|
amdsmi.amdsmi_shut_down()
|
|
sys.exit(1)
|
|
|
|
gpu_index = CONFIG.get("gpu_index", 0)
|
|
if gpu_index < 0 or gpu_index >= len(gpu_handles):
|
|
print(f"ERROR: --gpu-index {gpu_index} out of range (found {len(gpu_handles)} GPUs).")
|
|
amdsmi.amdsmi_shut_down()
|
|
sys.exit(2)
|
|
gpu_handle = gpu_handles[gpu_index]
|
|
|
|
# Get socket handles
|
|
try:
|
|
socket_handles = amdsmi.amdsmi_get_socket_handles()
|
|
except:
|
|
socket_handles = []
|
|
|
|
# Get CPU socket handles (for CPU APIs)
|
|
try:
|
|
cpu_socket_handles = amdsmi.amdsmi_get_cpu_handles()
|
|
except:
|
|
cpu_socket_handles = []
|
|
|
|
# Get CPU core handles
|
|
try:
|
|
cpu_core_handles = amdsmi.amdsmi_get_cpucore_handles()
|
|
except:
|
|
cpu_core_handles = []
|
|
|
|
# Get GPU info for display
|
|
asic = amdsmi.amdsmi_get_gpu_asic_info(gpu_handle)
|
|
gpu_name = asic.get("market_name", "Unknown")
|
|
bdf = amdsmi.amdsmi_get_gpu_device_bdf(gpu_handle)
|
|
|
|
print(f"GPU: {gpu_name}")
|
|
print(f"BDF: {bdf}")
|
|
print(f"GPUs found: {len(gpu_handles)}")
|
|
print(f"Sockets found: {len(socket_handles)}")
|
|
print(f"CPU Sockets found: {len(cpu_socket_handles)}")
|
|
print(f"CPU Cores found: {len(cpu_core_handles)}")
|
|
print(f"Running as: {'ROOT' if is_root() else 'USER'}")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
# ========================================================================
|
|
# SAVE CURRENT VALUES FOR REVERTING
|
|
# ========================================================================
|
|
print("Saving current values for revert...")
|
|
|
|
saved_values["power_cap"] = safe_get(
|
|
lambda: amdsmi.amdsmi_get_power_cap_info(gpu_handle).get("power_cap")
|
|
)
|
|
saved_values["perf_level"] = safe_get(lambda: amdsmi.amdsmi_get_gpu_perf_level(gpu_handle))
|
|
saved_values["fan_speed"] = safe_get(lambda: amdsmi.amdsmi_get_gpu_fan_speed(gpu_handle, 0))
|
|
saved_values["process_isolation"] = safe_get(
|
|
lambda: amdsmi.amdsmi_get_gpu_process_isolation(gpu_handle)
|
|
)
|
|
|
|
print(f" Power Cap: {saved_values['power_cap']}")
|
|
print(f" Perf Level: {saved_values['perf_level']}")
|
|
print(f" Fan Speed: {saved_values['fan_speed']}")
|
|
print(f" Process Isolation: {saved_values['process_isolation']}")
|
|
print()
|
|
|
|
# ========================================================================
|
|
# SECTION 1: INITIALIZATION & SHUTDOWN (2 APIs)
|
|
# ========================================================================
|
|
print("=" * 70)
|
|
print("SECTION 1: INITIALIZATION & SHUTDOWN (2 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
# amdsmi_init - already called, just verify it exists
|
|
test_api("amdsmi_init", None, skip_reason="Already called during setup")
|
|
|
|
# amdsmi_shut_down - will call at end
|
|
test_api("amdsmi_shut_down", None, skip_reason="Will call at end of tests")
|
|
|
|
# ========================================================================
|
|
# SECTION 2: PROCESSOR/DEVICE DISCOVERY (14 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 2: PROCESSOR/DEVICE DISCOVERY (14 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api("amdsmi_get_processor_type", lambda: amdsmi.amdsmi_get_processor_type(gpu_handle))
|
|
|
|
test_api("amdsmi_get_processor_handles", lambda: amdsmi.amdsmi_get_processor_handles())
|
|
|
|
test_api(
|
|
"amdsmi_get_processor_handles_by_type",
|
|
lambda: (
|
|
amdsmi.amdsmi_get_processor_handles_by_type(
|
|
socket_handles[0],
|
|
getattr(
|
|
need("AmdSmiProcessorType"),
|
|
"AMD_GPU",
|
|
getattr(need("AmdSmiProcessorType"), "AMDSMI_PROCESSOR_TYPE_AMD_GPU", 0),
|
|
),
|
|
)
|
|
if socket_handles
|
|
else (_ for _ in ()).throw(_EnumMissing("no socket handles available"))
|
|
),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_processor_count_from_handles",
|
|
lambda: amdsmi.amdsmi_get_processor_count_from_handles(gpu_handles),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_processor_handle_from_bdf",
|
|
lambda: amdsmi.amdsmi_get_processor_handle_from_bdf(bdf),
|
|
)
|
|
|
|
test_api("amdsmi_get_processor_info", lambda: amdsmi.amdsmi_get_processor_info(gpu_handle))
|
|
|
|
test_api("amdsmi_get_socket_handles", lambda: amdsmi.amdsmi_get_socket_handles())
|
|
|
|
test_api(
|
|
"amdsmi_get_socket_info",
|
|
lambda: amdsmi.amdsmi_get_socket_info(socket_handles[0]) if socket_handles else None,
|
|
)
|
|
|
|
test_api("amdsmi_get_node_handle", lambda: amdsmi.amdsmi_get_node_handle(gpu_handle))
|
|
|
|
test_api("amdsmi_get_gpu_device_bdf", lambda: amdsmi.amdsmi_get_gpu_device_bdf(gpu_handle))
|
|
|
|
test_api("amdsmi_get_gpu_device_uuid", lambda: amdsmi.amdsmi_get_gpu_device_uuid(gpu_handle))
|
|
|
|
test_api("amdsmi_get_gpu_bdf_id", lambda: amdsmi.amdsmi_get_gpu_bdf_id(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_enumeration_info",
|
|
lambda: amdsmi.amdsmi_get_gpu_enumeration_info(gpu_handle),
|
|
)
|
|
|
|
test_api("amdsmi_get_gpu_kfd_info", lambda: amdsmi.amdsmi_get_gpu_kfd_info(gpu_handle))
|
|
|
|
# ========================================================================
|
|
# SECTION 3: DRIVER & VERSION INFO (8 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 3: DRIVER & VERSION INFO (8 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_driver_version", lambda: amdsmi.amdsmi_get_gpu_driver_version(gpu_handle)
|
|
)
|
|
|
|
test_api("amdsmi_get_gpu_driver_info", lambda: amdsmi.amdsmi_get_gpu_driver_info(gpu_handle))
|
|
|
|
test_api("amdsmi_get_version", lambda: amdsmi.amdsmi_get_version())
|
|
|
|
test_api("amdsmi_get_version_str", lambda: amdsmi.amdsmi_get_version_str())
|
|
|
|
test_api("amdsmi_get_lib_version", lambda: amdsmi.amdsmi_get_lib_version())
|
|
|
|
test_api("amdsmi_get_rocm_version", lambda: amdsmi.amdsmi_get_rocm_version())
|
|
|
|
test_api("amdsmi_status_string", lambda: amdsmi.amdsmi_status_string(0))
|
|
|
|
test_api("amdsmi_status_code_to_string", lambda: amdsmi.amdsmi_status_code_to_string(0))
|
|
|
|
# ========================================================================
|
|
# SECTION 4: GPU HARDWARE INFO (12 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 4: GPU HARDWARE INFO (12 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api("amdsmi_get_gpu_asic_info", lambda: amdsmi.amdsmi_get_gpu_asic_info(gpu_handle))
|
|
|
|
test_api("amdsmi_get_gpu_vram_info", lambda: amdsmi.amdsmi_get_gpu_vram_info(gpu_handle))
|
|
|
|
test_api("amdsmi_get_gpu_board_info", lambda: amdsmi.amdsmi_get_gpu_board_info(gpu_handle))
|
|
|
|
test_api("amdsmi_get_gpu_vbios_info", lambda: amdsmi.amdsmi_get_gpu_vbios_info(gpu_handle))
|
|
|
|
test_api("amdsmi_get_gpu_cache_info", lambda: amdsmi.amdsmi_get_gpu_cache_info(gpu_handle))
|
|
|
|
test_api("amdsmi_get_gpu_revision", lambda: amdsmi.amdsmi_get_gpu_revision(gpu_handle))
|
|
|
|
test_api("amdsmi_get_gpu_vendor_name", lambda: amdsmi.amdsmi_get_gpu_vendor_name(gpu_handle))
|
|
|
|
test_api("amdsmi_get_gpu_id", lambda: amdsmi.amdsmi_get_gpu_id(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_drm_render_minor",
|
|
lambda: amdsmi.amdsmi_get_gpu_drm_render_minor(gpu_handle),
|
|
)
|
|
|
|
test_api("amdsmi_get_gpu_subsystem_id", lambda: amdsmi.amdsmi_get_gpu_subsystem_id(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_subsystem_name", lambda: amdsmi.amdsmi_get_gpu_subsystem_name(gpu_handle)
|
|
)
|
|
|
|
# ========================================================================
|
|
# SECTION 5: FIRMWARE INFO (2 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 5: FIRMWARE INFO (2 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api("amdsmi_get_fw_info", lambda: amdsmi.amdsmi_get_fw_info(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_get_npm_info",
|
|
lambda: (
|
|
amdsmi.amdsmi_get_npm_info(amdsmi.amdsmi_get_node_handle(gpu_handle))
|
|
if hasattr(amdsmi, "amdsmi_get_node_handle")
|
|
else None
|
|
),
|
|
)
|
|
|
|
# ========================================================================
|
|
# SECTION 6: POWER MANAGEMENT (10 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 6: POWER MANAGEMENT (10 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api("amdsmi_get_power_cap_info", lambda: amdsmi.amdsmi_get_power_cap_info(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_get_supported_power_cap", lambda: amdsmi.amdsmi_get_supported_power_cap(gpu_handle)
|
|
)
|
|
|
|
test_api("amdsmi_get_power_info", lambda: amdsmi.amdsmi_get_power_info(gpu_handle))
|
|
|
|
# Set power cap - use current value (safe)
|
|
if saved_values["power_cap"]:
|
|
test_api(
|
|
"amdsmi_set_power_cap",
|
|
lambda: amdsmi.amdsmi_set_power_cap(gpu_handle, 0, saved_values["power_cap"]),
|
|
requires_root=True,
|
|
)
|
|
else:
|
|
test_api("amdsmi_set_power_cap", None, skip_reason="Could not get current power cap")
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_power_profile_presets",
|
|
lambda: amdsmi.amdsmi_get_gpu_power_profile_presets(gpu_handle, 0),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_power_profile",
|
|
lambda: amdsmi.amdsmi_set_gpu_power_profile(
|
|
gpu_handle, 0, need("AmdSmiPowerProfilePresetMasks").BOOTUP_DEFAULT
|
|
),
|
|
requires_root=True,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_is_gpu_power_management_enabled",
|
|
lambda: amdsmi.amdsmi_is_gpu_power_management_enabled(gpu_handle),
|
|
)
|
|
|
|
test_api("amdsmi_get_energy_count", lambda: amdsmi.amdsmi_get_energy_count(gpu_handle))
|
|
|
|
test_api("amdsmi_get_violation_status", lambda: amdsmi.amdsmi_get_violation_status(gpu_handle))
|
|
|
|
# ========================================================================
|
|
# SECTION 7: PCIe INFO (12 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 7: PCIe INFO (12 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api("amdsmi_get_pcie_info", lambda: amdsmi.amdsmi_get_pcie_info(gpu_handle))
|
|
|
|
test_api("amdsmi_get_pcie_link_status", lambda: amdsmi.amdsmi_get_pcie_link_status(gpu_handle))
|
|
|
|
test_api("amdsmi_get_pcie_link_caps", lambda: amdsmi.amdsmi_get_pcie_link_caps(gpu_handle))
|
|
|
|
test_api("amdsmi_get_gpu_pci_id", lambda: amdsmi.amdsmi_get_gpu_pci_id(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_pci_bandwidth", lambda: amdsmi.amdsmi_get_gpu_pci_bandwidth(gpu_handle)
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_pci_bandwidth",
|
|
None,
|
|
skip_reason="Risky - writing arbitrary PCIe bandwidth mask is hardware-dependent",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_pci_throughput", lambda: amdsmi.amdsmi_get_gpu_pci_throughput(gpu_handle)
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_pci_replay_counter",
|
|
lambda: amdsmi.amdsmi_get_gpu_pci_replay_counter(gpu_handle),
|
|
)
|
|
|
|
test_api("amdsmi_get_gpu_ptl_formats", lambda: amdsmi.amdsmi_get_gpu_ptl_formats(gpu_handle))
|
|
|
|
test_api("amdsmi_get_gpu_ptl_state", lambda: amdsmi.amdsmi_get_gpu_ptl_state(gpu_handle))
|
|
|
|
test_api("amdsmi_set_gpu_ptl_formats", None, skip_reason="Risky - modifies PCIe PTL formats")
|
|
|
|
test_api("amdsmi_set_gpu_ptl_state", None, skip_reason="Risky - modifies PCIe PTL state")
|
|
|
|
# ========================================================================
|
|
# SECTION 8: ACTIVITY & UTILIZATION (6 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 8: ACTIVITY & UTILIZATION (6 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api("amdsmi_get_gpu_activity", lambda: amdsmi.amdsmi_get_gpu_activity(gpu_handle))
|
|
|
|
test_api("amdsmi_get_busy_percent", lambda: amdsmi.amdsmi_get_busy_percent(gpu_handle))
|
|
|
|
test_api("amdsmi_get_gpu_busy_percent", lambda: amdsmi.amdsmi_get_gpu_busy_percent(gpu_handle))
|
|
|
|
# Build a portable counter_types list from whatever this build exposes.
|
|
def _utilization_counter_types():
|
|
ut = need("AmdSmiUtilizationCounterType")
|
|
candidates = [
|
|
"COARSE_GRAIN_GFX_ACTIVITY",
|
|
"COARSE_GRAIN_MEM_ACTIVITY",
|
|
"FINE_GRAIN_GFX_ACTIVITY",
|
|
"FINE_GRAIN_MEM_ACTIVITY",
|
|
]
|
|
picked = [getattr(ut, name) for name in candidates if hasattr(ut, name)]
|
|
if not picked:
|
|
raise _EnumMissing("no AmdSmiUtilizationCounterType members available")
|
|
return picked
|
|
|
|
test_api(
|
|
"amdsmi_get_utilization_count",
|
|
lambda: amdsmi.amdsmi_get_utilization_count(gpu_handle, _utilization_counter_types()),
|
|
)
|
|
|
|
test_api("amdsmi_get_gpu_xcd_counter", lambda: amdsmi.amdsmi_get_gpu_xcd_counter(gpu_handle))
|
|
|
|
test_api("amdsmi_clean_gpu_local_data", None, skip_reason="Risky - could delete GPU local data")
|
|
|
|
# ========================================================================
|
|
# SECTION 9: CLOCK & FREQUENCY (15 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 9: CLOCK & FREQUENCY (15 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api(
|
|
"amdsmi_get_clock_info",
|
|
lambda: amdsmi.amdsmi_get_clock_info(gpu_handle, need("AmdSmiClkType").GFX),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_clk_freq",
|
|
lambda: amdsmi.amdsmi_get_clk_freq(gpu_handle, need("AmdSmiClkType").GFX),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_clk_freq",
|
|
None,
|
|
skip_reason="Risky - writing arbitrary clk freq mask is hardware-dependent",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_target_frequency_range",
|
|
lambda: amdsmi.amdsmi_get_gpu_target_frequency_range(gpu_handle),
|
|
)
|
|
|
|
# set_gpu_clk_limit takes (handle, clk_type, limit_type, value).
|
|
# Read the current target range and replay max as a no-op-ish write.
|
|
def _set_clk_limit_noop():
|
|
clk_type = need("AmdSmiClkType").GFX
|
|
limit_type_enum = need("AmdSmiClkLimitType")
|
|
# Prefer SOFT_MAX, fall back to MAX or first member.
|
|
limit_type = getattr(
|
|
limit_type_enum, "SOFT_MAX", getattr(limit_type_enum, "MAX", list(limit_type_enum)[0])
|
|
)
|
|
cur = (
|
|
safe_get(lambda: amdsmi.amdsmi_get_gpu_target_frequency_range(gpu_handle), default={})
|
|
or {}
|
|
)
|
|
# target_frequency_range usually returns dict with min/max per clk
|
|
max_clk = None
|
|
if isinstance(cur, dict):
|
|
for k in ("max_clk", "max", "sclk_max", "gfx_max"):
|
|
if k in cur:
|
|
max_clk = cur[k]
|
|
break
|
|
if not max_clk:
|
|
max_clk = 1500 # sane fallback in MHz
|
|
return amdsmi.amdsmi_set_gpu_clk_limit(gpu_handle, clk_type, limit_type, int(max_clk))
|
|
|
|
test_api("amdsmi_set_gpu_clk_limit", _set_clk_limit_noop, requires_root=True)
|
|
|
|
test_api("amdsmi_get_gpu_od_clk_info", lambda: amdsmi.amdsmi_get_gpu_od_clk_info(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_od_clk_info",
|
|
lambda: amdsmi.amdsmi_set_gpu_od_clk_info(
|
|
gpu_handle, need("AmdSmiFreqInd").MIN, 500, need("AmdSmiClkType").GFX
|
|
),
|
|
requires_root=True,
|
|
)
|
|
|
|
test_api("amdsmi_get_gpu_od_volt_info", lambda: amdsmi.amdsmi_get_gpu_od_volt_info(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_od_volt_info",
|
|
lambda: amdsmi.amdsmi_set_gpu_od_volt_info(gpu_handle, 0, 500, 700),
|
|
requires_root=True,
|
|
)
|
|
|
|
# od_volt_curve_regions needs num_regions; pull it from od_volt_info.
|
|
def _od_volt_curve_regions():
|
|
info = safe_get(lambda: amdsmi.amdsmi_get_gpu_od_volt_info(gpu_handle), default={}) or {}
|
|
n = 0
|
|
if isinstance(info, dict):
|
|
n = int(info.get("num_regions") or info.get("num_curve_regions") or 0)
|
|
if n <= 0:
|
|
n = 1 # try a single region; API will error cleanly if unsupported
|
|
return amdsmi.amdsmi_get_gpu_od_volt_curve_regions(gpu_handle, n)
|
|
|
|
test_api("amdsmi_get_gpu_od_volt_curve_regions", _od_volt_curve_regions)
|
|
|
|
test_api("amdsmi_get_dfc_ctrl", lambda: amdsmi.amdsmi_get_dfc_ctrl(gpu_handle))
|
|
|
|
test_api("amdsmi_set_dfc_ctrl", None, skip_reason="Risky - modifies Data Fabric Clock control")
|
|
|
|
test_api("amdsmi_get_soc_pstate", lambda: amdsmi.amdsmi_get_soc_pstate(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_set_soc_pstate",
|
|
lambda: amdsmi.amdsmi_set_soc_pstate(gpu_handle, 0),
|
|
requires_root=True,
|
|
)
|
|
|
|
# ========================================================================
|
|
# SECTION 10: PERFORMANCE LEVEL & OVERDRIVE (8 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 10: PERFORMANCE LEVEL & OVERDRIVE (8 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api("amdsmi_get_gpu_perf_level", lambda: amdsmi.amdsmi_get_gpu_perf_level(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_perf_level",
|
|
lambda: amdsmi.amdsmi_set_gpu_perf_level(gpu_handle, need("AmdSmiDevPerfLevel").AUTO),
|
|
requires_root=True,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_perf_level_v1",
|
|
lambda: amdsmi.amdsmi_set_gpu_perf_level_v1(gpu_handle, need("AmdSmiDevPerfLevel").AUTO),
|
|
requires_root=True,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_perf_determinism_mode",
|
|
lambda: amdsmi.amdsmi_set_gpu_perf_determinism_mode(gpu_handle, 1000),
|
|
requires_root=True,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_overdrive_level", lambda: amdsmi.amdsmi_get_gpu_overdrive_level(gpu_handle)
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_overdrive_level",
|
|
lambda: amdsmi.amdsmi_set_gpu_overdrive_level(gpu_handle, 0),
|
|
requires_root=True,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_overdrive_level_v1",
|
|
lambda: amdsmi.amdsmi_set_gpu_overdrive_level_v1(gpu_handle, 0),
|
|
requires_root=True,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_mem_overdrive_level",
|
|
lambda: amdsmi.amdsmi_get_gpu_mem_overdrive_level(gpu_handle),
|
|
)
|
|
|
|
# ========================================================================
|
|
# SECTION 11: TEMPERATURE & VOLTAGE & METRICS (7 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 11: TEMPERATURE & VOLTAGE & METRICS (7 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api(
|
|
"amdsmi_get_temp_metric",
|
|
lambda: amdsmi.amdsmi_get_temp_metric(
|
|
gpu_handle, need("AmdSmiTemperatureType").EDGE, need("AmdSmiTemperatureMetric").CURRENT
|
|
),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_volt_metric",
|
|
lambda: amdsmi.amdsmi_get_gpu_volt_metric(
|
|
gpu_handle, need("AmdSmiVoltageType").VDDGFX, need("AmdSmiVoltageMetric").CURRENT
|
|
),
|
|
)
|
|
|
|
test_api("amdsmi_get_gpu_metrics_info", lambda: amdsmi.amdsmi_get_gpu_metrics_info(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_metrics_header_info",
|
|
lambda: amdsmi.amdsmi_get_gpu_metrics_header_info(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_pm_metrics_info", lambda: amdsmi.amdsmi_get_gpu_pm_metrics_info(gpu_handle)
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_partition_metrics_info",
|
|
lambda: amdsmi.amdsmi_get_gpu_partition_metrics_info(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_reg_table_info", lambda: amdsmi.amdsmi_get_gpu_reg_table_info(gpu_handle, 0)
|
|
)
|
|
|
|
# ========================================================================
|
|
# SECTION 12: FAN CONTROL (5 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 12: FAN CONTROL (5 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api("amdsmi_get_gpu_fan_rpms", lambda: amdsmi.amdsmi_get_gpu_fan_rpms(gpu_handle, 0))
|
|
|
|
test_api("amdsmi_get_gpu_fan_speed", lambda: amdsmi.amdsmi_get_gpu_fan_speed(gpu_handle, 0))
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_fan_speed_max", lambda: amdsmi.amdsmi_get_gpu_fan_speed_max(gpu_handle, 0)
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_fan_speed",
|
|
lambda: amdsmi.amdsmi_set_gpu_fan_speed(gpu_handle, 0, 128),
|
|
requires_root=True,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_reset_gpu_fan",
|
|
lambda: amdsmi.amdsmi_reset_gpu_fan(gpu_handle, 0),
|
|
requires_root=True,
|
|
)
|
|
|
|
# ========================================================================
|
|
# SECTION 13: MEMORY (8 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 13: MEMORY (8 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api("amdsmi_get_gpu_vram_usage", lambda: amdsmi.amdsmi_get_gpu_vram_usage(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_memory_total",
|
|
lambda: amdsmi.amdsmi_get_gpu_memory_total(gpu_handle, need("AmdSmiMemoryType").VRAM),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_memory_usage",
|
|
lambda: amdsmi.amdsmi_get_gpu_memory_usage(gpu_handle, need("AmdSmiMemoryType").VRAM),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_memory_busy_percent",
|
|
lambda: amdsmi.amdsmi_get_gpu_memory_busy_percent(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_memory_reserved_pages",
|
|
lambda: amdsmi.amdsmi_get_gpu_memory_reserved_pages(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_bad_page_info", lambda: amdsmi.amdsmi_get_gpu_bad_page_info(gpu_handle)
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_bad_page_threshold",
|
|
lambda: amdsmi.amdsmi_get_gpu_bad_page_threshold(gpu_handle),
|
|
)
|
|
|
|
# ========================================================================
|
|
# SECTION 14: RAS & ECC (10 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 14: RAS & ECC (10 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_ras_block_features_enabled",
|
|
lambda: amdsmi.amdsmi_get_gpu_ras_block_features_enabled(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_ras_feature_info",
|
|
lambda: amdsmi.amdsmi_get_gpu_ras_feature_info(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_ecc_error_count", lambda: amdsmi.amdsmi_get_gpu_ecc_error_count(gpu_handle)
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_total_ecc_count", lambda: amdsmi.amdsmi_get_gpu_total_ecc_count(gpu_handle)
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_ecc_count",
|
|
lambda: amdsmi.amdsmi_get_gpu_ecc_count(gpu_handle, need("AmdSmiGpuBlock").UMC),
|
|
)
|
|
|
|
test_api("amdsmi_get_gpu_ecc_enabled", lambda: amdsmi.amdsmi_get_gpu_ecc_enabled(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_ecc_status",
|
|
lambda: amdsmi.amdsmi_get_gpu_ecc_status(gpu_handle, need("AmdSmiGpuBlock").UMC),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_cper_entries", lambda: amdsmi.amdsmi_get_gpu_cper_entries(gpu_handle, 0)
|
|
)
|
|
|
|
test_api("amdsmi_get_afids_from_cper", lambda: amdsmi.amdsmi_get_afids_from_cper(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_gpu_validate_ras_eeprom", lambda: amdsmi.amdsmi_gpu_validate_ras_eeprom(gpu_handle)
|
|
)
|
|
|
|
# ========================================================================
|
|
# SECTION 15: PROCESS MANAGEMENT (7 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 15: PROCESS MANAGEMENT (7 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api("amdsmi_get_gpu_process_list", lambda: amdsmi.amdsmi_get_gpu_process_list(gpu_handle))
|
|
|
|
# Pick a real PID from the process list if possible; otherwise skip.
|
|
_proc_list = safe_get(lambda: amdsmi.amdsmi_get_gpu_process_list(gpu_handle), default=[])
|
|
_first_pid = None
|
|
if _proc_list:
|
|
_p0 = _proc_list[0]
|
|
_first_pid = _p0.get("pid") if isinstance(_p0, dict) else _p0
|
|
if _first_pid:
|
|
test_api(
|
|
"amdsmi_get_gpu_process_info",
|
|
lambda: amdsmi.amdsmi_get_gpu_process_info(gpu_handle, _first_pid),
|
|
)
|
|
else:
|
|
test_api(
|
|
"amdsmi_get_gpu_process_info", None, skip_reason="No active GPU processes to query"
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_compute_process_info", lambda: amdsmi.amdsmi_get_gpu_compute_process_info()
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_compute_process_info_by_pid",
|
|
lambda: amdsmi.amdsmi_get_gpu_compute_process_info_by_pid(os.getpid()),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_compute_process_gpus",
|
|
lambda: amdsmi.amdsmi_get_gpu_compute_process_gpus(os.getpid()),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_process_isolation",
|
|
lambda: amdsmi.amdsmi_get_gpu_process_isolation(gpu_handle),
|
|
)
|
|
|
|
# Set process isolation - use saved value
|
|
if saved_values["process_isolation"] is not None:
|
|
test_api(
|
|
"amdsmi_set_gpu_process_isolation",
|
|
lambda: amdsmi.amdsmi_set_gpu_process_isolation(
|
|
gpu_handle, saved_values["process_isolation"]
|
|
),
|
|
requires_root=True,
|
|
)
|
|
else:
|
|
test_api(
|
|
"amdsmi_set_gpu_process_isolation",
|
|
lambda: amdsmi.amdsmi_set_gpu_process_isolation(gpu_handle, 0),
|
|
requires_root=True,
|
|
)
|
|
|
|
# ========================================================================
|
|
# SECTION 16: TOPOLOGY & NUMA (11 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 16: TOPOLOGY & NUMA (11 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_topo_numa_affinity",
|
|
lambda: amdsmi.amdsmi_get_gpu_topo_numa_affinity(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_topo_get_numa_node_number",
|
|
lambda: amdsmi.amdsmi_topo_get_numa_node_number(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_topo_get_link_weight",
|
|
lambda: (
|
|
amdsmi.amdsmi_topo_get_link_weight(gpu_handles[0], gpu_handles[1])
|
|
if len(gpu_handles) >= 2
|
|
else amdsmi.amdsmi_topo_get_link_weight(gpu_handle, gpu_handle)
|
|
),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_topo_get_link_type",
|
|
lambda: (
|
|
amdsmi.amdsmi_topo_get_link_type(gpu_handles[0], gpu_handles[1])
|
|
if len(gpu_handles) >= 2
|
|
else amdsmi.amdsmi_topo_get_link_type(gpu_handle, gpu_handle)
|
|
),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_topo_get_p2p_status",
|
|
lambda: (
|
|
amdsmi.amdsmi_topo_get_p2p_status(gpu_handles[0], gpu_handles[1])
|
|
if len(gpu_handles) >= 2
|
|
else amdsmi.amdsmi_topo_get_p2p_status(gpu_handle, gpu_handle)
|
|
),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_P2P_status",
|
|
lambda: (
|
|
amdsmi.amdsmi_get_P2P_status(gpu_handles[0], gpu_handles[1])
|
|
if len(gpu_handles) >= 2
|
|
else amdsmi.amdsmi_get_P2P_status(gpu_handle, gpu_handle)
|
|
),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_is_P2P_accessible",
|
|
lambda: (
|
|
amdsmi.amdsmi_is_P2P_accessible(gpu_handles[0], gpu_handles[1])
|
|
if len(gpu_handles) >= 2
|
|
else amdsmi.amdsmi_is_P2P_accessible(gpu_handle, gpu_handle)
|
|
),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_minmax_bandwidth",
|
|
lambda: (
|
|
amdsmi.amdsmi_get_minmax_bandwidth(gpu_handles[0], gpu_handles[1])
|
|
if len(gpu_handles) >= 2
|
|
else amdsmi.amdsmi_get_minmax_bandwidth(gpu_handle, gpu_handle)
|
|
),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_minmax_bandwidth_between_processors",
|
|
lambda: (
|
|
amdsmi.amdsmi_get_minmax_bandwidth_between_processors(gpu_handles[0], gpu_handles[1])
|
|
if len(gpu_handles) >= 2
|
|
else amdsmi.amdsmi_get_minmax_bandwidth_between_processors(gpu_handle, gpu_handle)
|
|
),
|
|
)
|
|
|
|
# Some amdsmi builds raise AttributeError internally (e.g. missing
|
|
# 'domain_number' on union_amdsmi_bdf_t). Re-raise as a runtime error
|
|
# so it reports as Unsupported rather than a Script Error.
|
|
def _get_link_metrics():
|
|
try:
|
|
return amdsmi.amdsmi_get_link_metrics(gpu_handle)
|
|
except AttributeError as e:
|
|
raise RuntimeError(f"amdsmi internal: {e}")
|
|
|
|
test_api("amdsmi_get_link_metrics", _get_link_metrics)
|
|
|
|
# link_topology_nearest needs a link_type; try common members across builds.
|
|
def _link_topology_nearest():
|
|
lt = need("AmdSmiLinkType")
|
|
for name in ("XGMI", "PCIE", "AMDSMI_LINK_TYPE_XGMI", "AMDSMI_LINK_TYPE_PCIE"):
|
|
if hasattr(lt, name):
|
|
return amdsmi.amdsmi_get_link_topology_nearest(gpu_handle, getattr(lt, name))
|
|
# Fall back to first enum value
|
|
first = list(lt)[0]
|
|
return amdsmi.amdsmi_get_link_topology_nearest(gpu_handle, first)
|
|
|
|
test_api("amdsmi_get_link_topology_nearest", _link_topology_nearest)
|
|
|
|
# ========================================================================
|
|
# SECTION 17: XGMI (6 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 17: XGMI (6 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api("amdsmi_get_xgmi_info", lambda: amdsmi.amdsmi_get_xgmi_info(gpu_handle))
|
|
|
|
test_api("amdsmi_get_xgmi_plpd", lambda: amdsmi.amdsmi_get_xgmi_plpd(gpu_handle))
|
|
|
|
test_api(
|
|
"amdsmi_set_xgmi_plpd",
|
|
lambda: amdsmi.amdsmi_set_xgmi_plpd(gpu_handle, 0),
|
|
requires_root=True,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_xgmi_link_status",
|
|
lambda: amdsmi.amdsmi_get_gpu_xgmi_link_status(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_gpu_xgmi_error_status", lambda: amdsmi.amdsmi_gpu_xgmi_error_status(gpu_handle)
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_reset_gpu_xgmi_error",
|
|
lambda: amdsmi.amdsmi_reset_gpu_xgmi_error(gpu_handle),
|
|
requires_root=True,
|
|
)
|
|
|
|
# ========================================================================
|
|
# SECTION 18: PARTITIONING (15 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 18: PARTITIONING (15 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_compute_partition",
|
|
lambda: amdsmi.amdsmi_get_gpu_compute_partition(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_compute_partition",
|
|
None,
|
|
skip_reason="Risky - changes compute partition, may need reboot",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_dev_compute_partition_get",
|
|
lambda: amdsmi.amdsmi_dev_compute_partition_get(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_dev_compute_partition_set",
|
|
None,
|
|
skip_reason="Risky - changes compute partition, may need reboot",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_dev_compute_partition_reset", None, skip_reason="Risky - resets compute partition"
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_memory_partition",
|
|
lambda: amdsmi.amdsmi_get_gpu_memory_partition(gpu_handle),
|
|
)
|
|
|
|
# amdsmi_set_gpu_memory_partition deprecated, use amdsmi_set_gpu_memory_partition_mode instead
|
|
test_api(
|
|
"amdsmi_set_gpu_memory_partition",
|
|
None,
|
|
skip_reason="Risky - changes memory partition, may need reboot",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_memory_partition_mode",
|
|
None,
|
|
skip_reason="Risky - changes memory partition mode",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_memory_partition_config",
|
|
lambda: amdsmi.amdsmi_get_gpu_memory_partition_config(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_accelerator_partition_profile",
|
|
lambda: amdsmi.amdsmi_get_gpu_accelerator_partition_profile(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_accelerator_partition_profile",
|
|
None,
|
|
skip_reason="Risky - changes accelerator partition profile",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_accelerator_partition_profile_config",
|
|
lambda: amdsmi.amdsmi_get_gpu_accelerator_partition_profile_config(gpu_handle),
|
|
)
|
|
|
|
test_api("amdsmi_dev_nps_mode_get", lambda: amdsmi.amdsmi_dev_nps_mode_get(gpu_handle))
|
|
|
|
test_api("amdsmi_dev_nps_mode_set", None, skip_reason="Risky - changes NPS mode")
|
|
|
|
test_api("amdsmi_dev_nps_mode_reset", None, skip_reason="Risky - resets NPS mode")
|
|
|
|
# ========================================================================
|
|
# SECTION 19: VIRTUALIZATION (2 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 19: VIRTUALIZATION (2 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_virtualization_mode",
|
|
lambda: amdsmi.amdsmi_get_gpu_virtualization_mode(gpu_handle),
|
|
)
|
|
|
|
# cpu_affinity_with_scope needs (gpu_handle, scope).
|
|
def _cpu_affinity_with_scope():
|
|
scope_enum = need("AmdSmiCpuAffinityScope")
|
|
scope = getattr(scope_enum, "NODE", getattr(scope_enum, "SOCKET", list(scope_enum)[0]))
|
|
return amdsmi.amdsmi_get_cpu_affinity_with_scope(gpu_handle, scope)
|
|
|
|
test_api("amdsmi_get_cpu_affinity_with_scope", _cpu_affinity_with_scope)
|
|
|
|
# ========================================================================
|
|
# SECTION 20: EVENT NOTIFICATION (5 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 20: EVENT NOTIFICATION (5 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
# AmdSmiEventReader is a class; just verify it is importable.
|
|
if hasattr(amdsmi, "AmdSmiEventReader"):
|
|
add_result("AmdSmiEventReader", "Supported", output="class present")
|
|
print("Testing: AmdSmiEventReader... Supported")
|
|
else:
|
|
add_result(
|
|
"AmdSmiEventReader",
|
|
"API Not Found",
|
|
note="module amdsmi has no attribute AmdSmiEventReader",
|
|
)
|
|
print("Testing: AmdSmiEventReader... API Not Found")
|
|
|
|
test_api(
|
|
"amdsmi_init_gpu_event_notification",
|
|
lambda: amdsmi.amdsmi_init_gpu_event_notification(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_gpu_event_notification_mask",
|
|
None,
|
|
skip_reason="Risky - modifies event notification state",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_event_notification",
|
|
None,
|
|
skip_reason="Requires an active event session with a configured mask",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_stop_gpu_event_notification",
|
|
lambda: amdsmi.amdsmi_stop_gpu_event_notification(gpu_handle),
|
|
)
|
|
|
|
# ========================================================================
|
|
# SECTION 21: PERFORMANCE COUNTERS (6 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 21: PERFORMANCE COUNTERS (6 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api(
|
|
"amdsmi_gpu_counter_group_supported",
|
|
lambda: amdsmi.amdsmi_gpu_counter_group_supported(
|
|
gpu_handle, need("AmdSmiEventGroup").XGMI
|
|
),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_gpu_create_counter",
|
|
lambda: amdsmi.amdsmi_gpu_create_counter(gpu_handle, need("AmdSmiEventType").XGMI_0_NOP_TX),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_gpu_destroy_counter", None, skip_reason="Requires valid counter handle from create"
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_gpu_control_counter", None, skip_reason="Requires valid counter handle from create"
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_gpu_read_counter", None, skip_reason="Requires valid counter handle from create"
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_gpu_available_counters",
|
|
lambda: amdsmi.amdsmi_get_gpu_available_counters(gpu_handle, need("AmdSmiEventGroup").XGMI),
|
|
)
|
|
|
|
# ========================================================================
|
|
# SECTION 22: ITERATOR APIs (5 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 22: ITERATOR APIs (5 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api(
|
|
"amdsmi_open_supported_func_iterator",
|
|
lambda: amdsmi.amdsmi_open_supported_func_iterator(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_open_supported_variant_iterator",
|
|
lambda: amdsmi.amdsmi_open_supported_variant_iterator(gpu_handle),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_close_supported_func_iterator", None, skip_reason="Requires valid iterator handle"
|
|
)
|
|
|
|
test_api("amdsmi_next_func_iter", None, skip_reason="Requires valid iterator handle")
|
|
|
|
test_api("amdsmi_get_func_iter_value", None, skip_reason="Requires valid iterator handle")
|
|
|
|
# ========================================================================
|
|
# SECTION 23: RESET APIs (1 API)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 23: RESET APIs (1 API)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
test_api("amdsmi_reset_gpu", None, skip_reason="Risky - would reset GPU, could crash system")
|
|
|
|
# ========================================================================
|
|
# SECTION 24: CPU/HSMP APIs (74 APIs)
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("SECTION 24: CPU/HSMP APIs (74 APIs)")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
# Get CPU socket handle for CPU APIs
|
|
cpu_socket = cpu_socket_handles[0] if cpu_socket_handles else None
|
|
cpu_core = cpu_core_handles[0] if cpu_core_handles else None
|
|
|
|
test_api("amdsmi_get_cpu_handles", lambda: amdsmi.amdsmi_get_cpu_handles())
|
|
|
|
test_api("amdsmi_get_cpucore_handles", lambda: amdsmi.amdsmi_get_cpucore_handles())
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_hsmp_proto_ver",
|
|
lambda: amdsmi.amdsmi_get_cpu_hsmp_proto_ver(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_smu_fw_version",
|
|
lambda: amdsmi.amdsmi_get_cpu_smu_fw_version(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_hsmp_driver_version",
|
|
lambda: amdsmi.amdsmi_get_cpu_hsmp_driver_version(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_core_energy",
|
|
lambda: amdsmi.amdsmi_get_cpu_core_energy(cpu_core) if cpu_core else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_core_ccd_power",
|
|
lambda: amdsmi.amdsmi_get_cpu_core_ccd_power(cpu_core) if cpu_core else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_socket_energy",
|
|
lambda: amdsmi.amdsmi_get_cpu_socket_energy(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api("amdsmi_get_threads_per_core", lambda: amdsmi.amdsmi_get_threads_per_core())
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_prochot_status",
|
|
lambda: amdsmi.amdsmi_get_cpu_prochot_status(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_fclk_mclk",
|
|
lambda: amdsmi.amdsmi_get_cpu_fclk_mclk(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_cclk_limit",
|
|
lambda: amdsmi.amdsmi_get_cpu_cclk_limit(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_socket_current_active_freq_limit",
|
|
lambda: (
|
|
amdsmi.amdsmi_get_cpu_socket_current_active_freq_limit(cpu_socket)
|
|
if cpu_socket
|
|
else None
|
|
),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_socket_freq_range",
|
|
lambda: amdsmi.amdsmi_get_cpu_socket_freq_range(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_core_current_freq_limit",
|
|
lambda: amdsmi.amdsmi_get_cpu_core_current_freq_limit(cpu_core) if cpu_core else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_socket_power",
|
|
lambda: amdsmi.amdsmi_get_cpu_socket_power(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_socket_power_cap",
|
|
lambda: amdsmi.amdsmi_get_cpu_socket_power_cap(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_socket_power_cap_max",
|
|
lambda: amdsmi.amdsmi_get_cpu_socket_power_cap_max(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_pwr_svi_telemetry_all_rails",
|
|
lambda: (
|
|
amdsmi.amdsmi_get_cpu_pwr_svi_telemetry_all_rails(cpu_socket) if cpu_socket else None
|
|
),
|
|
)
|
|
|
|
test_api("amdsmi_set_cpu_socket_power_cap", None, skip_reason="Risky - modifies CPU power cap")
|
|
|
|
test_api(
|
|
"amdsmi_set_cpu_pwr_efficiency_mode",
|
|
None,
|
|
skip_reason="Risky - modifies CPU power efficiency mode",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_pwr_efficiency_mode",
|
|
lambda: amdsmi.amdsmi_get_cpu_pwr_efficiency_mode(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_core_boostlimit",
|
|
lambda: amdsmi.amdsmi_get_cpu_core_boostlimit(cpu_core) if cpu_core else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_socket_c0_residency",
|
|
lambda: amdsmi.amdsmi_get_cpu_socket_c0_residency(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_cpu_core_boostlimit", None, skip_reason="Risky - modifies CPU core boost limit"
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_cpu_socket_boostlimit",
|
|
None,
|
|
skip_reason="Risky - modifies CPU socket boost limit",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_ddr_bw",
|
|
lambda: amdsmi.amdsmi_get_cpu_ddr_bw(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_socket_temperature",
|
|
lambda: amdsmi.amdsmi_get_cpu_socket_temperature(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_dimm_temp_range_and_refresh_rate",
|
|
lambda: (
|
|
amdsmi.amdsmi_get_cpu_dimm_temp_range_and_refresh_rate(cpu_socket, 0)
|
|
if cpu_socket
|
|
else None
|
|
),
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_dimm_power_consumption",
|
|
lambda: amdsmi.amdsmi_get_cpu_dimm_power_consumption(cpu_socket, 0) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_dimm_thermal_sensor",
|
|
lambda: amdsmi.amdsmi_get_cpu_dimm_thermal_sensor(cpu_socket, 0) if cpu_socket else None,
|
|
)
|
|
|
|
test_api("amdsmi_set_cpu_xgmi_width", None, skip_reason="Risky - modifies CPU XGMI width")
|
|
|
|
test_api(
|
|
"amdsmi_set_cpu_gmi3_link_width_range", None, skip_reason="Risky - modifies GMI3 link width"
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_cpu_apb_enable",
|
|
lambda: amdsmi.amdsmi_cpu_apb_enable(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api("amdsmi_cpu_apb_disable", None, skip_reason="Risky - disables CPU APB")
|
|
|
|
test_api(
|
|
"amdsmi_set_cpu_socket_lclk_dpm_level", None, skip_reason="Risky - modifies LCLK DPM level"
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_socket_lclk_dpm_level",
|
|
lambda: amdsmi.amdsmi_get_cpu_socket_lclk_dpm_level(cpu_socket, 0) if cpu_socket else None,
|
|
)
|
|
|
|
test_api("amdsmi_set_cpu_pcie_link_rate", None, skip_reason="Risky - modifies PCIe link rate")
|
|
|
|
test_api(
|
|
"amdsmi_set_cpu_df_pstate_range", None, skip_reason="Risky - modifies DF P-state range"
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_current_io_bandwidth",
|
|
lambda: amdsmi.amdsmi_get_cpu_current_io_bandwidth(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_current_xgmi_bw",
|
|
lambda: amdsmi.amdsmi_get_cpu_current_xgmi_bw(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_hsmp_metrics_table_version",
|
|
lambda: amdsmi.amdsmi_get_hsmp_metrics_table_version(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_cpu_xgmi_pstate_range", None, skip_reason="Risky - modifies XGMI P-state range"
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_xgmi_pstate_range",
|
|
lambda: amdsmi.amdsmi_get_cpu_xgmi_pstate_range(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_cpu_rail_isofreq_policy",
|
|
None,
|
|
skip_reason="Risky - modifies rail isofreq policy",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_rail_isofreq_policy",
|
|
lambda: amdsmi.amdsmi_get_cpu_rail_isofreq_policy(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api("amdsmi_set_cpu_dfc_ctrl", None, skip_reason="Risky - modifies CPU DFC control")
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_dfc_ctrl",
|
|
lambda: amdsmi.amdsmi_get_cpu_dfc_ctrl(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api("amdsmi_set_cpu_pc6_enable", None, skip_reason="Risky - modifies CPU PC6 state")
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_pc6_enable",
|
|
lambda: amdsmi.amdsmi_get_cpu_pc6_enable(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api("amdsmi_set_cpu_cc6_enable", None, skip_reason="Risky - modifies CPU CC6 state")
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_cc6_enable",
|
|
lambda: amdsmi.amdsmi_get_cpu_cc6_enable(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_dimm_sb_reg",
|
|
lambda: amdsmi.amdsmi_get_cpu_dimm_sb_reg(cpu_socket, 0, 0) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_cpu_dimm_sb_reg", None, skip_reason="Risky - modifies DIMM sideband registers"
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_tdelta",
|
|
lambda: amdsmi.amdsmi_get_cpu_tdelta(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_svi3_vr_controller_temp",
|
|
lambda: amdsmi.amdsmi_get_cpu_svi3_vr_controller_temp(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_enabled_commands",
|
|
lambda: amdsmi.amdsmi_get_cpu_enabled_commands(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_core_floor_freq_limit",
|
|
lambda: amdsmi.amdsmi_get_cpu_core_floor_freq_limit(cpu_core) if cpu_core else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_floor_freq_limit",
|
|
lambda: amdsmi.amdsmi_get_cpu_floor_freq_limit(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_core_eff_floor_freq_limit",
|
|
lambda: amdsmi.amdsmi_get_cpu_core_eff_floor_freq_limit(cpu_core) if cpu_core else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_eff_floor_freq_limit",
|
|
lambda: amdsmi.amdsmi_get_cpu_eff_floor_freq_limit(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_cpu_core_floor_freq_limit",
|
|
None,
|
|
skip_reason="Risky - modifies CPU core floor frequency",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_cpu_floor_freq_limit", None, skip_reason="Risky - modifies CPU floor frequency"
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_cpu_msr_floor_freq_limit",
|
|
None,
|
|
skip_reason="Risky - modifies CPU MSR floor frequency",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_set_cpu_core_msr_floor_freq_limit",
|
|
None,
|
|
skip_reason="Risky - modifies CPU core MSR floor frequency",
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_freq_range",
|
|
lambda: amdsmi.amdsmi_get_cpu_freq_range(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api("amdsmi_set_cpu_sdps_limit", None, skip_reason="Risky - modifies CPU SDPS limit")
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_sdps_limit",
|
|
lambda: amdsmi.amdsmi_get_cpu_sdps_limit(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_get_hsmp_metrics_table",
|
|
lambda: amdsmi.amdsmi_get_hsmp_metrics_table(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api(
|
|
"amdsmi_first_online_core_on_cpu_socket",
|
|
lambda: amdsmi.amdsmi_first_online_core_on_cpu_socket(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api("amdsmi_get_cpu_family", lambda: amdsmi.amdsmi_get_cpu_family())
|
|
|
|
test_api("amdsmi_get_cpu_model", lambda: amdsmi.amdsmi_get_cpu_model())
|
|
|
|
test_api(
|
|
"amdsmi_get_cpu_model_name",
|
|
lambda: amdsmi.amdsmi_get_cpu_model_name(cpu_socket) if cpu_socket else None,
|
|
)
|
|
|
|
test_api("amdsmi_get_esmi_err_msg", lambda: amdsmi.amdsmi_get_esmi_err_msg(0))
|
|
|
|
# ========================================================================
|
|
# MULTI-GPU SANITY SWEEP (only when --all-gpus is used)
|
|
# ========================================================================
|
|
if CONFIG.get("all_gpus") and len(gpu_handles) > 1:
|
|
print()
|
|
print("=" * 70)
|
|
print(f"MULTI-GPU SANITY SWEEP ({len(gpu_handles) - 1} additional GPU(s))")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
sanity_apis = [
|
|
("amdsmi_get_gpu_device_bdf", lambda h: amdsmi.amdsmi_get_gpu_device_bdf(h)),
|
|
("amdsmi_get_gpu_device_uuid", lambda h: amdsmi.amdsmi_get_gpu_device_uuid(h)),
|
|
("amdsmi_get_gpu_asic_info", lambda h: amdsmi.amdsmi_get_gpu_asic_info(h)),
|
|
("amdsmi_get_gpu_board_info", lambda h: amdsmi.amdsmi_get_gpu_board_info(h)),
|
|
("amdsmi_get_gpu_vram_usage", lambda h: amdsmi.amdsmi_get_gpu_vram_usage(h)),
|
|
("amdsmi_get_gpu_activity", lambda h: amdsmi.amdsmi_get_gpu_activity(h)),
|
|
("amdsmi_get_power_info", lambda h: amdsmi.amdsmi_get_power_info(h)),
|
|
(
|
|
"amdsmi_get_clock_info",
|
|
lambda h: amdsmi.amdsmi_get_clock_info(h, need("AmdSmiClkType").GFX),
|
|
),
|
|
(
|
|
"amdsmi_get_temp_metric",
|
|
lambda h: amdsmi.amdsmi_get_temp_metric(
|
|
h, need("AmdSmiTemperatureType").EDGE, need("AmdSmiTemperatureMetric").CURRENT
|
|
),
|
|
),
|
|
]
|
|
|
|
primary_index = CONFIG.get("gpu_index", 0)
|
|
for idx, h in enumerate(gpu_handles):
|
|
if idx == primary_index:
|
|
continue
|
|
for api_name, fn in sanity_apis:
|
|
tagged = f"[gpu{idx}] {api_name}"
|
|
test_api(tagged, lambda fn=fn, h=h: fn(h))
|
|
|
|
# ========================================================================
|
|
# REVERT SECTION
|
|
# ========================================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("REVERTING WRITE OPERATIONS TO SAFE DEFAULTS")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
if CONFIG.get("no_revert"):
|
|
print(" [INFO] --no-revert supplied; leaving any mutated state as-is.")
|
|
elif CONFIG.get("skip_writes"):
|
|
print(" [INFO] --skip-writes supplied; no setters ran, nothing to revert.")
|
|
elif not is_root():
|
|
print(" [INFO] Running as non-root user - write operations were skipped.")
|
|
print(" [INFO] No changes were made, nothing to revert.")
|
|
else:
|
|
# Revert perf level to AUTO. Some builds return a string from the
|
|
# getter that the setter won't accept, so always use the enum here.
|
|
try:
|
|
if hasattr(amdsmi, "AmdSmiDevPerfLevel"):
|
|
amdsmi.amdsmi_set_gpu_perf_level(gpu_handle, amdsmi.AmdSmiDevPerfLevel.AUTO)
|
|
print(" Reverted perf level to AUTO")
|
|
except Exception as e:
|
|
print(f" Could not revert perf level: {str(e).splitlines()[0]}")
|
|
|
|
# Revert power cap to saved value.
|
|
try:
|
|
if saved_values.get("power_cap"):
|
|
amdsmi.amdsmi_set_power_cap(gpu_handle, 0, saved_values["power_cap"])
|
|
print(f" Reverted power_cap to {saved_values['power_cap']}")
|
|
except Exception as e:
|
|
print(f" Could not revert power_cap: {str(e).splitlines()[0]}")
|
|
|
|
# Revert fan to automatic; ignore NOT_SUPPORTED quietly.
|
|
try:
|
|
amdsmi.amdsmi_reset_gpu_fan(gpu_handle, 0)
|
|
print(" Reverted fan to automatic")
|
|
except Exception as e:
|
|
msg = str(e)
|
|
if "NOT_SUPPORTED" in msg:
|
|
print(" Fan revert skipped (no controllable fan)")
|
|
else:
|
|
print(f" Could not revert fan: {msg.splitlines()[0]}")
|
|
|
|
# Revert process isolation
|
|
if saved_values["process_isolation"] is not None:
|
|
try:
|
|
amdsmi.amdsmi_set_gpu_process_isolation(
|
|
gpu_handle, saved_values["process_isolation"]
|
|
)
|
|
print(f" Reverted process_isolation to {saved_values['process_isolation']}")
|
|
except Exception as e:
|
|
print(f" Could not revert process_isolation: {str(e).splitlines()[0]}")
|
|
|
|
# Shutdown
|
|
amdsmi.amdsmi_shut_down()
|
|
print(" AMD SMI shut down successfully")
|
|
|
|
# Generate report
|
|
generate_report(gpu_name, bdf)
|
|
|
|
|
|
# ============================================================================
|
|
# REPORT GENERATION
|
|
# ============================================================================
|
|
|
|
|
|
def generate_report(gpu_name, bdf):
|
|
"""Generate a markdown report of all test results."""
|
|
run_as = "ROOT" if is_root() else "USER"
|
|
supported = [r for r in results if r["status"] == "Supported"]
|
|
unsupported = [r for r in results if r["status"] == "Unsupported"]
|
|
not_found = [r for r in results if r["status"] == "API Not Found"]
|
|
skipped = [r for r in results if r["status"] == "Skipped"]
|
|
script_errors = [r for r in results if r["status"] == "Script Error"]
|
|
|
|
if CONFIG.get("output"):
|
|
outfile = os.path.abspath(CONFIG["output"])
|
|
else:
|
|
ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
outfile = os.path.join(
|
|
os.path.dirname(os.path.abspath(__file__)), f"test_amdsmi_all_apis_output_{ts}.md"
|
|
)
|
|
|
|
try:
|
|
os.makedirs(os.path.dirname(outfile), exist_ok=True)
|
|
except Exception:
|
|
pass
|
|
|
|
try:
|
|
f_out = open(outfile, "w", encoding="utf-8")
|
|
except OSError as e:
|
|
print(f"WARNING: Cannot write report to {outfile}: {e}")
|
|
print("Report not written. Final summary below.")
|
|
print(
|
|
f"FINAL SUMMARY: {len(supported)} Supported / "
|
|
f"{len(unsupported)} Unsupported / {len(skipped)} Skipped / "
|
|
f"{len(script_errors)} Script Errors / {len(not_found)} Not Found / "
|
|
f"{len(results)} Total"
|
|
)
|
|
return
|
|
|
|
with f_out as f:
|
|
f.write(f"# AMD SMI Complete API Test Results\n\n")
|
|
f.write(f"**Script Version:** {__version__} \n")
|
|
f.write(f"**Test Date:** {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} \n")
|
|
f.write(f"**System:** {gpu_name} \n")
|
|
f.write(f"**BDF:** {bdf} \n")
|
|
f.write(f"**Primary GPU Index:** {CONFIG.get('gpu_index', 0)} \n")
|
|
f.write(f"**GPUs:** {len(gpu_handles)} \n")
|
|
f.write(f"**Sockets:** {len(socket_handles)} \n")
|
|
f.write(f"**CPU Sockets:** {len(cpu_socket_handles)} \n")
|
|
f.write(f"**CPU Cores:** {len(cpu_core_handles)} \n")
|
|
f.write(f"**Platform:** Bare Metal \n")
|
|
f.write(f"**Run As:** {run_as} \n")
|
|
f.write(f"**Python:** {sys.executable} ({platform.python_version()}) \n")
|
|
f.write(f"**amdsmi module:** {getattr(amdsmi, '__file__', 'unknown')} \n")
|
|
f.write(f"**Hostname:** {get_hostname()} \n\n")
|
|
f.write("---\n\n")
|
|
|
|
# Summary
|
|
f.write("## Summary\n\n")
|
|
f.write("| Metric | Count |\n")
|
|
f.write("|--------|-------|\n")
|
|
f.write(f"| Total APIs Tested | {len(results)} |\n")
|
|
f.write(f"| Supported | {len(supported)} |\n")
|
|
f.write(f"| Unsupported | {len(unsupported)} |\n")
|
|
f.write(f"| Skipped | {len(skipped)} |\n")
|
|
f.write(f"| Script Error | {len(script_errors)} |\n")
|
|
f.write(f"| API Not Found | {len(not_found)} |\n\n")
|
|
f.write("---\n\n")
|
|
|
|
# Complete results table
|
|
f.write("## Complete Results\n\n")
|
|
f.write("| # | API | Status | Output / Note |\n")
|
|
f.write("|---|-----|--------|---------------|\n")
|
|
for i, r in enumerate(results, 1):
|
|
detail = r["output"] if r["output"] else r["note"]
|
|
detail = detail.replace("|", "/").replace("\n", " ")[:100]
|
|
f.write(f"| {i} | `{r['api']}` | {r['status']} | {detail} |\n")
|
|
|
|
f.write("\n---\n\n")
|
|
|
|
# Unsupported APIs detail
|
|
if unsupported:
|
|
f.write("## ❌ Unsupported APIs Detail\n\n")
|
|
for r in unsupported:
|
|
note = r["note"].replace("\n", " ")
|
|
f.write(f"- **`{r['api']}`**: {note[:120]}\n")
|
|
f.write("\n")
|
|
|
|
# APIs Not Found
|
|
if not_found:
|
|
f.write("## ❓ APIs Not Found\n\n")
|
|
for r in not_found:
|
|
note = r["note"].replace("\n", " ")
|
|
f.write(f"- **`{r['api']}`**: {note[:120]}\n")
|
|
f.write("\n")
|
|
|
|
# Supported APIs list
|
|
f.write("## ✅ Supported APIs\n\n")
|
|
for r in supported:
|
|
f.write(f"- `{r['api']}`\n")
|
|
f.write("\n")
|
|
|
|
print()
|
|
print(f"Report written to: {outfile}")
|
|
print()
|
|
print("=" * 70)
|
|
print(
|
|
f"FINAL SUMMARY: {len(supported)} Supported / "
|
|
f"{len(unsupported)} Unsupported / {len(skipped)} Skipped / "
|
|
f"{len(script_errors)} Script Errors / {len(not_found)} Not Found / "
|
|
f"{len(results)} Total"
|
|
)
|
|
print("=" * 70)
|
|
|
|
|
|
# ============================================================================
|
|
# MAIN ENTRY POINT
|
|
# ============================================================================
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Test AMD SMI Python APIs and emit a markdown report."
|
|
)
|
|
parser.add_argument(
|
|
"--gpu-index", type=int, default=0, help="Index of the GPU to target (default: 0)."
|
|
)
|
|
parser.add_argument(
|
|
"--output",
|
|
default=None,
|
|
help="Path to the markdown report (default: timestamped file next to this script).",
|
|
)
|
|
parser.add_argument(
|
|
"--skip-writes",
|
|
action="store_true",
|
|
help="Skip every API marked requires_root (no setters will run).",
|
|
)
|
|
parser.add_argument(
|
|
"--no-revert",
|
|
action="store_true",
|
|
help="Do not attempt to restore mutated state at the end.",
|
|
)
|
|
parser.add_argument(
|
|
"--all-gpus",
|
|
action="store_true",
|
|
help="After the primary sweep, run a small read-only sanity "
|
|
"sweep on every other GPU so multi-GPU systems are also "
|
|
"validated. The primary sweep still runs only on --gpu-index.",
|
|
)
|
|
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
args = parser.parse_args()
|
|
|
|
CONFIG["gpu_index"] = args.gpu_index
|
|
CONFIG["output"] = args.output
|
|
CONFIG["skip_writes"] = args.skip_writes
|
|
CONFIG["no_revert"] = args.no_revert
|
|
CONFIG["all_gpus"] = args.all_gpus
|
|
|
|
run_tests()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|