Files
TheRock/rocm-systems/projects/amdsmi/tools/hw_compatibility_status.py
T
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

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()