Files
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

290 lines
8.2 KiB
Python

# Copyright Advanced Micro Devices, Inc., or its affiliates.
# SPDX-License-Identifier: MIT
"""Get host/gpu specs."""
import json
import os
import re
import shutil
import socket
import subprocess
from dataclasses import dataclass
from pathlib import Path as path
from textwrap import dedent
import yaml
@dataclass
class MachineSpecs(yaml.YAMLObject):
yaml_tag = "!rrperfMachineSpecs"
hostname: str
cpu: str
kernel: str
ram: str
distro: str
rocmversion: str
vbios: str
gpuid: str
deviceinfo: str
vram: str
perflevel: str
mclk: str
sclk: str
def __init__(
self,
hostname="",
cpu="",
kernel="",
ram="",
distro="",
rocmversion="",
vbios="",
gpuid="",
deviceinfo="",
vram="",
perflevel="",
mclk="",
sclk="",
):
self.hostname = hostname
self.cpu = cpu
self.kernel = kernel
self.ram = ram
self.distro = distro
self.rocmversion = rocmversion
self.vbios = vbios
self.gpuid = gpuid
self.deviceinfo = deviceinfo
self.vram = vram
self.perflevel = perflevel
self.mclk = mclk
self.sclk = sclk
def __str__(self):
return yaml.dump(self)
def __hash__(self):
return hash(str(self))
def __lt__(self, other):
return str(self) < str(other)
@classmethod
def from_yaml(cls, loader, node):
values = loader.construct_mapping(node, deep=True)
return cls(
values.get("hostname", ""),
values.get("cpu", ""),
values.get("kernel", ""),
values.get("ram", ""),
values.get("distro", ""),
values.get("rocmversion", ""),
values.get("vbios", ""),
values.get("gpuid", ""),
values.get("deviceinfo", ""),
values.get("vram", ""),
values.get("perflevel", ""),
values.get("mclk", ""),
values.get("sclk", ""),
)
def pretty_string(self):
return dedent(f"""\
Host info:
hostname: {self.hostname}
cpu info: {self.cpu}
ram: {self.ram}
distro: {self.distro}
kernel version: {self.kernel}
rocm version: {self.rocmversion}
Device info:
device: {self.deviceinfo}
vbios version: {self.vbios}
vram: {self.vram}
performance level: {self.perflevel}
system clock: {self.sclk}
memory clock: {self.mclk}
""")
def search(pattern, string):
m = re.search(pattern, string, re.MULTILINE)
if m is not None:
return m.group(1)
return None
def _run_amdsmi_json(cmd: list):
"""Run an amd-smi command that emits JSON and return the parsed object."""
try:
completed = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if completed.returncode != 0:
return None
return json.loads(completed.stdout.decode("utf-8", errors="replace"))
except (FileNotFoundError, json.JSONDecodeError, ValueError):
return None
def _gpu_entry(payload, devicenum: int):
"""Return the gpu_data entry for devicenum, if present."""
if not payload:
return None
gpu_data = payload.get("gpu_data")
if not gpu_data:
return None
for entry in gpu_data:
if entry.get("gpu") == devicenum:
return entry
return gpu_data[0]
def _clock_mhz(clock_entry):
"""Extract a clock string from an amd-smi clock block."""
if not isinstance(clock_entry, dict):
return None
clk = clock_entry.get("clk")
if isinstance(clk, dict):
value = clk.get("value")
if value is not None:
return f"{value}Mhz"
elif isinstance(clk, (int, float)):
return f"{clk}Mhz"
elif isinstance(clk, str) and clk not in ("", "N/A"):
return clk
return None
def get_amdsmi_specs(devicenum: int = 0, amd_smi_path: str = "amd-smi") -> dict:
"""
Collect per-device GPU specs using amd-smi structured JSON output.
Any field that cannot be resolved is returned as None.
"""
g = str(devicenum)
static = _run_amdsmi_json(
[amd_smi_path, "static", "-g", g, "--asic", "--vbios", "--json"]
)
metric = _run_amdsmi_json(
[
amd_smi_path,
"metric",
"-g",
g,
"--mem-usage",
"--clock",
"--perf-level",
"--json",
]
)
result = {
"vbios_version": None,
"gpuid": None,
"market_name": None,
"vram": None,
"performance_level": None,
"memory_clk": None,
"system_clk": None,
}
if static:
d = _gpu_entry(static, devicenum)
if d:
result["gpuid"] = d.get("asic", {}).get("device_id")
result["market_name"] = d.get("asic", {}).get("market_name")
vbios = d.get("ifwi") or d.get("vbios") or {}
result["vbios_version"] = vbios.get("part_number")
if metric:
d = _gpu_entry(metric, devicenum)
if d:
total = d.get("mem_usage", {}).get("total_vram", {}).get("value")
if total is not None:
result["vram"] = int(total) * 1024 * 1024
perf = d.get("perf_level")
if isinstance(perf, str):
result["performance_level"] = perf.split("_")[-1].lower()
clock = d.get("clock", {})
result["system_clk"] = _clock_mhz(clock.get("gfx_0"))
result["memory_clk"] = _clock_mhz(clock.get("mem_0"))
return result
def load_machine_specs(path):
if path.exists():
contents = path.read_text()
if contents.startswith(MachineSpecs.yaml_tag):
return yaml.load(contents, Loader=yaml.Loader)
return MachineSpecs()
def _gpu_spec_text(value, missing="no amd-smi"):
return missing if value is None else value
def get_machine_specs(devicenum, amd_smi_path="amd-smi"):
cpuinfo = path("/proc/cpuinfo").read_text()
meminfo = path("/proc/meminfo").read_text()
version = path("/proc/version").read_text()
os_release = path("/etc/os-release").read_text()
if os.path.isfile("/opt/rocm/.info/version-utils"):
rocm_info = path("/opt/rocm/.info/version-utils").read_text()
elif os.path.isfile("/opt/rocm/.info/version"):
rocm_info = path("/opt/rocm/.info/version").read_text()
else:
rocm_info = "rocm info not available"
amd_smi_available = shutil.which(amd_smi_path) is not None
amdsmi = get_amdsmi_specs(devicenum, amd_smi_path) if amd_smi_available else {}
missing = "no amd-smi"
# Use the NODE_NAME env var in CI.
hostname = os.environ.get("NODE_NAME")
if not hostname:
hostname = socket.gethostname()
cpu = search(r"^model name\s*: (.*?)$", cpuinfo)
kernel = search(r"version (\S*)", version)
ram = search(r"MemTotal:\s*(\S*)", meminfo)
distro = search(r'PRETTY_NAME="(.*?)"', os_release)
rocmversion = rocm_info.strip()
vbios = _gpu_spec_text(amdsmi.get("vbios_version"), missing) if amd_smi_available else missing
gpuid = _gpu_spec_text(amdsmi.get("gpuid"), missing) if amd_smi_available else missing
deviceinfo = _gpu_spec_text(amdsmi.get("market_name"), missing) if amd_smi_available else missing
vram = amdsmi.get("vram") if amd_smi_available else 0
perflevel = _gpu_spec_text(amdsmi.get("performance_level"), missing) if amd_smi_available else missing
mclk = _gpu_spec_text(amdsmi.get("memory_clk"), "") if amd_smi_available else ""
sclk = _gpu_spec_text(amdsmi.get("system_clk"), "") if amd_smi_available else ""
if ram is not None:
ram = "{:.2f} GiB".format(float(ram) / 1024**2)
if vram is not None:
vram = "{:.2f} GiB".format(float(vram) / 1024**3)
return MachineSpecs(
hostname,
cpu,
kernel,
ram,
distro,
rocmversion,
vbios,
gpuid,
deviceinfo,
vram,
perflevel,
mclk,
sclk,
)
if __name__ == "__main__":
print(get_machine_specs(0))