[SWDEV-503520] Add amdsmi_get_rocm_version() in python library (#76)

Changed amdsmi_get_rocm_version() to be an API in the python library only. 
Updated usage and version detection
Updated path detection of librocm-core.so
Updated docs to reflect both amdsmi_get_rocm_version and amdsmi_get_lib_version() do not require initialization.

Signed-off-by: Arif, Maisam <Maisam.Arif@amd.com>
This commit is contained in:
Arif, Maisam
2025-02-26 05:45:58 -06:00
committed by GitHub
parent 83c88f26d1
commit 52b3ee2dc6
9 changed files with 191 additions and 120 deletions
+2
View File
@@ -139,6 +139,8 @@ GPU: 0
### Changed
- **Removed initialization requirements for `amdsmi_get_lib_version()` and added `amdsmi_get_rocm_version()` to the python API & CLI**.
- **Added an additional argument `sensor_ind` to `amdsmi_get_power_info()`**.
This change breaks previous C API calls and will require a change
Python API now accepts `sensor_ind` as an optional argument, does not imapact previous usage
-3
View File
@@ -22,7 +22,6 @@ add_custom_command(
${PY_PACKAGE_DIR}/amdsmi_logger.py
${PY_PACKAGE_DIR}/amdsmi_parser.py
${PY_PACKAGE_DIR}/amdsmi_cli_exceptions.py
${PY_PACKAGE_DIR}/rocm_version.py
${PY_PACKAGE_DIR}/BDF.py
${PY_PACKAGE_DIR}/README.md
${PY_PACKAGE_DIR}/Release_Notes.md
@@ -36,7 +35,6 @@ add_custom_command(
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_logger.py ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_parser.py ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_cli_exceptions.py ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/rocm_version.py ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/BDF.py ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/Release_Notes.md ${PY_PACKAGE_DIR}/)
@@ -54,7 +52,6 @@ add_custom_target(
${PY_PACKAGE_DIR}/amdsmi_logger.py
${PY_PACKAGE_DIR}/amdsmi_parser.py
${PY_PACKAGE_DIR}/amdsmi_cli_exceptions.py
${PY_PACKAGE_DIR}/rocm_version.py
${PY_PACKAGE_DIR}/BDF.py
${PY_PACKAGE_DIR}/README.md
${PY_PACKAGE_DIR}/Release_Notes.md)
+18 -30
View File
@@ -19,6 +19,7 @@
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import argparse
import logging
import sys
import threading
@@ -32,7 +33,6 @@ from _version import __version__
from amdsmi_helpers import AMDSMIHelpers
from amdsmi_logger import AMDSMILogger
from amdsmi_cli_exceptions import AmdSmiRequiredCommandException, AmdSmiInvalidParameterException
from rocm_version import get_rocm_version
from amdsmi import amdsmi_interface
from amdsmi import amdsmi_exception
@@ -95,36 +95,14 @@ class AMDSMICommands():
exit_flag = True
if exit_flag:
try:
amdsmi_lib_version = amdsmi_interface.amdsmi_get_lib_version()
amdsmi_lib_version_str = f"{amdsmi_lib_version['year']}.{amdsmi_lib_version['major']}.{amdsmi_lib_version['minor']}.{amdsmi_lib_version['release']}"
except amdsmi_exception.AmdSmiLibraryException as e:
amdsmi_lib_version_str = e.get_error_info()
self.logger.output['tool'] = 'AMDSMI Tool'
self.logger.output['version'] = f'{__version__}'
self.logger.output['amdsmi_library_version'] = f'{amdsmi_lib_version_str}'
self.logger.output['rocm_version'] = f'{get_rocm_version()}'
if self.logger.is_human_readable_format():
human_readable_output = f"AMDSMI Tool: {__version__} | " \
f"AMDSMI Library version: {amdsmi_lib_version_str} | " \
f"ROCm version: {get_rocm_version()}"
# Custom human readable handling for version
if self.logger.destination == 'stdout':
print(human_readable_output)
else:
with self.logger.destination.open('a', encoding="utf-8") as output_file:
output_file.write(human_readable_output + '\n')
elif self.logger.is_json_format() or self.logger.is_csv_format():
self.logger.print_output()
version_args = argparse.Namespace()
version_args.gpu_version = False
version_args.cpu_version = False
self.version(version_args)
sys.exit(-1)
def version(self, args, gpu_version=None, cpu_version=None):
"""Print Version String
Args:
@@ -136,17 +114,23 @@ class AMDSMICommands():
if cpu_version:
args.cpu_version = cpu_version
# if no args are given, display everything
if not args.gpu_version and not args.cpu_version:
if args.gpu_version is None and args.cpu_version is None:
args.gpu_version = True
args.cpu_version = True
try:
amdsmi_lib_version = amdsmi_interface.amdsmi_get_lib_version()
amdsmi_lib_version_str = f"{amdsmi_lib_version['year']}.{amdsmi_lib_version['major']}.{amdsmi_lib_version['minor']}.{amdsmi_lib_version['release']}"
rocm_version_str = get_rocm_version()
except amdsmi_exception.AmdSmiLibraryException as e:
amdsmi_lib_version_str = e.get_error_info()
try:
rocm_lib_status, rocm_version_str = amdsmi_interface.amdsmi_get_rocm_version()
if rocm_lib_status is not True:
rocm_version_str = "N/A"
except amdsmi_exception.AmdSmiLibraryException as e:
rocm_version_str = e.get_error_info()
self.logger.output['tool'] = 'AMDSMI Tool'
self.logger.output['version'] = f'{__version__}'
self.logger.output['amdsmi_library_version'] = f'{amdsmi_lib_version_str}'
@@ -984,7 +968,7 @@ class AMDSMICommands():
static_dict['clock'] = clk_dict
else:
raise amdsmi_exception.AmdSmiParameterException(args.clock, list[str])
raise amdsmi_exception.AmdSmiParameterException(args.clock, 'list[str]')
# if original_clock_args is a boolean, set it back to the original value
if isinstance(original_clock_args, bool):
args.clock = original_clock_args
@@ -4218,6 +4202,7 @@ class AMDSMICommands():
self.logger.store_output(args.gpu, 'perfdeterminism', f"Successfully enabled performance determinism and set GFX clock frequency to {args.perf_determinism}")
if args.compute_partition:
attempted_to_set = "N/A"
try:
(accelerator_set_choices, accelerator_profiles) = self.helpers.get_accelerator_choices_types_indices()
logging.debug("args.compute_partition: %s; Accelerator_set_choices: %s", str(args.compute_partition), str(json.dumps(accelerator_set_choices, indent=4)))
@@ -4474,6 +4459,9 @@ class AMDSMICommands():
amdsmi_clk_type = amdsmi_interface.AmdSmiClkType.GFX
elif clk_type == "mclk":
amdsmi_clk_type = amdsmi_interface.AmdSmiClkType.MEM
else:
raise ValueError(f"Invalid clock type {clk_type} for {gpu_string}")
clk_tuple = amdsmi_interface.amdsmi_get_clock_info(args.gpu, amdsmi_clk_type)
if lim_type == "min":
+13 -2
View File
@@ -29,9 +29,8 @@ import re
import multiprocessing
import json
from typing import List, Union
from enum import Enum
from typing import Set
from typing import List, Set, Union
from amdsmi_init import *
from BDF import BDF
@@ -98,9 +97,11 @@ class AMDSMIHelpers():
def increment_set_count(self):
self._count_of_sets_called += 1
def get_set_count(self):
return self._count_of_sets_called
def is_virtual_os(self):
return self._is_virtual_os
@@ -173,6 +174,16 @@ class AMDSMIHelpers():
return AMDSMI_INIT_FLAG & amdsmi_interface.amdsmi_wrapper.AMDSMI_INIT_AMD_CPUS
def get_rocm_version(self):
try:
rocm_lib_status, rocm_version = amdsmi_interface.amdsmi_get_rocm_version()
if rocm_lib_status is not True:
return "N/A"
return rocm_version
except amdsmi_interface.AmdSmiLibraryException as e:
return "N/A"
def get_cpu_choices(self):
"""Return dictionary of possible CPU choices and string of the output:
Dictionary will be in format: cpus[ID]: Device Handle)
+1 -2
View File
@@ -33,7 +33,6 @@ from pathlib import Path
from _version import __version__
from amdsmi_helpers import AMDSMIHelpers
from rocm_version import get_rocm_version
import amdsmi_cli_exceptions
@@ -95,7 +94,7 @@ class AMDSMIParser(argparse.ArgumentParser):
version_string = f"Version: {__version__}"
platform_string = f"Platform: {self.helpers.os_info()}"
rocm_version = get_rocm_version()
rocm_version = self.helpers.get_rocm_version()
rocm_version_string = f"ROCm version: {rocm_version}"
program_name = 'amd-smi'
-29
View File
@@ -1,29 +0,0 @@
import os
import ctypes
from pathlib import Path
# Get the ROCm version for rocm-core library
def get_rocm_version():
try:
librocm_core_file = Path(__file__).resolve().parent.parent.parent / "lib" / "librocm-core.so"
if not librocm_core_file.is_file():
return "N/A"
# python binding
librocm_core = ctypes.CDLL(librocm_core_file)
VerErrors = ctypes.c_uint32
get_rocm_core_version = librocm_core.getROCmVersion
get_rocm_core_version.restype = VerErrors
get_rocm_core_version.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32),ctypes.POINTER(ctypes.c_uint32)]
# call the function
major = ctypes.c_uint32()
minor = ctypes.c_uint32()
patch = ctypes.c_uint32()
if get_rocm_core_version(ctypes.byref(major), ctypes.byref(minor),ctypes.byref(patch)) == 0:
return "%d.%d.%d" % (major.value, minor.value, patch.value)
return "N/A"
except:
return "N/A"
+46 -22
View File
@@ -3541,28 +3541,6 @@ except AmdSmiException as e:
print(e)
```
### amdsmi_get_lib_version
Description: Get the build version information for the currently running build of AMDSMI.
Output: amdsmi build version
Exceptions that can be thrown by `amdsmi_get_lib_version` function:
* `AmdSmiLibraryException`
* `AmdSmiRetryException`
* `AmdSmiParameterException`
Example:
```python
try:
version = amdsmi_get_lib_version()
print(version)
except AmdSmiException as e:
print(e)
```
### amdsmi_topo_get_numa_node_number
Description: Retrieve the NUMA CPU node number for a device
@@ -5092,3 +5070,49 @@ try:
except AmdSmiException as e:
print(e)
```
## No amdsmi_init APIs
### amdsmi_get_lib_version
Description: Get the build version information for the currently running build of AMDSMI. This function doesn't require amdsmi library init.
Output: amdsmi build version
Exceptions that can be thrown by `amdsmi_get_lib_version` function:
* `AmdSmiLibraryException`
* `AmdSmiRetryException`
* `AmdSmiParameterException`
Example:
```python
try:
version = amdsmi_get_lib_version()
print(version)
except AmdSmiException as e:
print(e)
```
### amdsmi_get_rocm_version
Description: This function attempts to retrieve the ROCm version by loading the `librocm-core.so` shared library. This function doesn't require amdsmi library init.
Output: Tuple (bool, str) containing rocm_load_status and version_message
Exceptions that can be thrown by `amdsmi_get_rocm_version` function:
* `AmdSmiLibraryException`
Example:
```python
try:
import amdsmi
rocm_load_status, version_message = amdsmi_get_rocm_version()
print(f"ROCm load status: {rocm_load_status}")
print(f"ROCm version msg: {version_message}")
except AmdSmiException as e:
print(e)
```
+6 -4
View File
@@ -9,7 +9,7 @@
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
#``
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
@@ -207,9 +207,6 @@ from .amdsmi_interface import amdsmi_get_gpu_vram_vendor
from .amdsmi_interface import amdsmi_get_gpu_subsystem_id
from .amdsmi_interface import amdsmi_get_gpu_subsystem_name
# # Version information
from .amdsmi_interface import amdsmi_get_lib_version
# # Hardware topology query
from .amdsmi_interface import amdsmi_topo_get_numa_node_number
from .amdsmi_interface import amdsmi_topo_get_link_weight
@@ -239,6 +236,11 @@ from .amdsmi_interface import amdsmi_get_gpu_pm_metrics_info
# # Virtualization Mode Detection
from .amdsmi_interface import amdsmi_get_gpu_virtualization_mode_info
# # Functions where library initialization is not needed
# # Version information
from .amdsmi_interface import amdsmi_get_lib_version
from .amdsmi_interface import amdsmi_get_rocm_version
# # Enums
from .amdsmi_interface import AmdSmiInitFlags
from .amdsmi_interface import AmdSmiContainerTypes
+105 -28
View File
@@ -18,19 +18,33 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import ctypes
import re
import json
import logging
from typing import Union, Any, Dict, List
from enum import IntEnum
import math
import os
import re
import sys
from collections.abc import Iterable
from enum import IntEnum
from pathlib import Path
from time import asctime, localtime, time
from typing import Any, Dict, List, Tuple, Union
from . import amdsmi_wrapper
from .amdsmi_exception import *
import sys
import math
from time import localtime, asctime, time
import json
### Non Library Specific Constants ###
class MaxUIntegerTypes(IntEnum):
UINT8_T = 0xFF
UINT16_T = 0xFFFF
UINT32_T = 0xFFFFFFFF
UINT64_T = 0xFFFFFFFFFFFFFFFF
NO_OF_32BITS = (sys.getsizeof(ctypes.c_uint32) * 8)
NO_OF_64BITS = (sys.getsizeof(ctypes.c_uint64) * 8)
KILO = math.pow(10, 3)
###############################
MAX_NUM_PROCESSES = 1024
@@ -72,6 +86,7 @@ AMDSMI_MAX_NUM_XGMI_PHYSICAL_LINK = 64
AMDSMI_GPU_UUID_SIZE = 38
MAX_AMDSMI_NAME_LENGTH = 64
MAX_EVENT_NOTIFICATION_MSG_SIZE = 96
_AMDSMI_STRING_LENGTH = 80
class AmdSmiInitFlags(IntEnum):
@@ -257,6 +272,7 @@ class AmdSmiEvtNotificationType(IntEnum):
GPU_POST_RESET = amdsmi_wrapper.AMDSMI_EVT_NOTIF_GPU_POST_RESET
RING_HANG = amdsmi_wrapper.AMDSMI_EVT_NOTIF_RING_HANG
class AmdSmiTemperatureMetric(IntEnum):
CURRENT = amdsmi_wrapper.AMDSMI_TEMP_CURRENT
MAX = amdsmi_wrapper.AMDSMI_TEMP_MAX
@@ -511,11 +527,6 @@ class AmdSmiEventReader:
self.stop()
_AMDSMI_MAX_DRIVER_VERSION_LENGTH = 80
_AMDSMI_GPU_UUID_SIZE = 38
_AMDSMI_STRING_LENGTH = 80
def _format_bad_page_info(bad_page_info, bad_page_count: ctypes.c_uint32) -> List[Dict]:
"""
Format bad page info data retrieved.
@@ -618,6 +629,7 @@ def _make_amdsmi_bdf_from_list(bdf):
amdsmi_bdf.struct_amdsmi_bdf_t.domain_number = bdf[0]
return amdsmi_bdf
def _pad_hex_value(value, length):
""" Pad a hexadecimal value with a given length of zeros
@@ -633,11 +645,6 @@ def _pad_hex_value(value, length):
return '0x' + value[2:].zfill(length)
return value
class MaxUIntegerTypes(IntEnum):
UINT8_T = 0xFF
UINT16_T = 0xFFFF
UINT32_T = 0xFFFFFFFF
UINT64_T = 0xFFFFFFFFFFFFFFFF
def _validate_if_max_uint(value, uint_type: MaxUIntegerTypes, isActivity=False, isBool=False):
return_val = "N/A"
@@ -661,7 +668,6 @@ def _validate_if_max_uint(value, uint_type: MaxUIntegerTypes, isActivity=False,
else:
return return_val
def amdsmi_get_socket_handles() -> List[amdsmi_wrapper.amdsmi_socket_handle]:
"""
Function that gets socket handles. Wraps the same named function call.
@@ -719,7 +725,6 @@ def amdsmi_get_cpusocket_handles() -> List[amdsmi_wrapper.amdsmi_socket_handle]:
]
return cpu_handles
def amdsmi_get_socket_info(socket_handle):
if not isinstance(socket_handle, amdsmi_wrapper.amdsmi_socket_handle):
raise AmdSmiParameterException(
@@ -747,7 +752,6 @@ def amdsmi_get_processor_info(processor_handle):
return processor_info.value.decode()
def amdsmi_get_processor_handles() -> List[amdsmi_wrapper.amdsmi_processor_handle]:
socket_handles = amdsmi_get_socket_handles()
devices = []
@@ -799,7 +803,6 @@ def amdsmi_get_cpucore_handles() -> List[amdsmi_wrapper.amdsmi_processor_handle]
return core_handles
def amdsmi_get_cpu_hsmp_proto_ver(
processor_handle: amdsmi_wrapper.amdsmi_processor_handle,
) -> int:
@@ -1502,10 +1505,6 @@ def amdsmi_get_hsmp_metrics_table_version(
return metric_tbl_version.value
NO_OF_32BITS = (sys.getsizeof(ctypes.c_uint32) * 8)
NO_OF_64BITS = (sys.getsizeof(ctypes.c_uint64) * 8)
KILO = math.pow(10, 3)
# Get 2's complement of 32 bit unsigned integer
def check_msb_32(num):
msb = 1 << (NO_OF_32BITS - 1)
@@ -2265,10 +2264,10 @@ def amdsmi_get_gpu_device_uuid(processor_handle: amdsmi_wrapper.amdsmi_processor
processor_handle, amdsmi_wrapper.amdsmi_processor_handle
)
uuid = ctypes.create_string_buffer(_AMDSMI_GPU_UUID_SIZE)
uuid = ctypes.create_string_buffer(AMDSMI_GPU_UUID_SIZE)
uuid_length = ctypes.c_uint32()
uuid_length.value = _AMDSMI_GPU_UUID_SIZE
uuid_length.value = AMDSMI_GPU_UUID_SIZE
_check_res(
amdsmi_wrapper.amdsmi_get_gpu_device_uuid(
@@ -2288,7 +2287,7 @@ def amdsmi_get_gpu_driver_info(
)
length = ctypes.c_int()
length.value = _AMDSMI_MAX_DRIVER_VERSION_LENGTH
length.value = AMDSMI_MAX_DRIVER_VERSION_LENGTH
info = amdsmi_wrapper.amdsmi_driver_info_t()
_check_res(
@@ -4451,6 +4450,7 @@ def amdsmi_get_gpu_metrics_header_info(
"content_revision": header_info.content_revision
}
def amdsmi_get_link_topology_nearest(
processor_handle: amdsmi_wrapper.amdsmi_processor_handle,
link_type: AmdSmiLinkType,
@@ -4491,3 +4491,80 @@ def amdsmi_get_gpu_virtualization_mode_info(
return {
"mode": AmdSmiVirtualizationMode(mode.value)
}
### Non C-Lib APIs ###
def amdsmi_get_rocm_version()-> Tuple[bool, str]:
"""
Get the ROCm version for the rocm-core library.
This function attempts to retrieve the ROCm version by loading the `librocm-core.so` shared library
and calling its `getROCmVersion` function. The version is returned as a string in the format "major.minor.patch".
Returns:
Tuple[bool, str]: A tuple containing a boolean and a string.
- The boolean indicates whether the operation was successful.
- The string contains the ROCm version if successful, or an error message if not.
Raises:
Exception: If there is an error loading the shared library or calling the function.
Example:
rocm_lib_status, version_message = amdsmi_get_rocm_version()
if rocm_lib_status:
print(f"ROCm version: {version_message}")
else:
print(f"Error: {version_message}")
"""
# librocm-core.so can be located in found using several different methods.
# Look for it with below priority:
# 1. ROCM_HOME/ROCM_PATH environment variables
# - ROCM_HOME/lib
# - ROCM_PATH/lib (usually set to /opt/rocm/)
# 2. Decided by the linker
# - LD_LIBRARY_PATH env var
# - defined path in /etc/ld.so.conf.d/
# 3. Relative to amdsmi_wrapper.py in /opt/rocm/share/amd_smi
# - parent directory
try:
possible_locations = list()
# 1.
rocm_path = os.getenv("ROCM_HOME", os.getenv("ROCM_PATH"))
if rocm_path:
possible_locations.append(os.path.join(rocm_path, "lib/librocm-core.so"))
# Check if /opt/rocm/lib/librocm-core.so exists and add it to the list
if os.path.exists("/opt/rocm/lib/librocm-core.so"):
possible_locations.append("/opt/rocm/lib/librocm-core.so")
# 2.
possible_locations.append("librocm-core.so")
# 3.
librocm_core_parent_dir = Path(__file__).resolve().parent.parent.parent / "lib" / "librocm-core.so"
possible_locations.append(librocm_core_parent_dir)
for librocm_core_file_path in possible_locations:
try:
librocm_core = ctypes.CDLL(librocm_core_file_path)
VerErrors = ctypes.c_uint32
get_rocm_core_version = librocm_core.getROCmVersion
get_rocm_core_version.restype = VerErrors
get_rocm_core_version.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32),ctypes.POINTER(ctypes.c_uint32)]
# call the function
major = ctypes.c_uint32()
minor = ctypes.c_uint32()
patch = ctypes.c_uint32()
if get_rocm_core_version(ctypes.byref(major), ctypes.byref(minor),ctypes.byref(patch)) == 0:
return True, f"{major.value}.{minor.value}.{patch.value}"
else:
return False, "Failed to unpack ROCm version"
except OSError as e:
err = e
continue
# If we hit here, we were unable to find the librocm-core.so file
return False, "Could not find librocm-core.so"
except Exception as e:
return False, f"Unable to detect ROCm installation, Unknown Error: {e}"