Merge amd-dev into amd-master 20230320

Change-Id: Ia62c1bb990a1e6aa07fd0534c0ed3119e6bed5e3
Signed-off-by: Galantsev, Dmitrii <dmitrii.galantsev@amd.com>
Dieser Commit ist enthalten in:
Galantsev, Dmitrii
2023-03-20 21:30:14 -05:00
Commit 7d8cca660f
21 geänderte Dateien mit 3222 neuen und 311 gelöschten Zeilen
+5
Datei anzeigen
@@ -12,3 +12,8 @@ DEBIAN/postinst
DEBIAN/prerm
RPM/
docs/*.pdf
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*.egg-*
+9 -4
Datei anzeigen
@@ -54,7 +54,8 @@ include(GNUInstallDirs)
option(BUILD_TESTS "Build test suite" OFF)
# TODO: Enable once virtualenv is installed on CI machines
option(BUILD_PACKAGE "Build python package" OFF)
option(BUILD_WRAPPER "Rebuild AMDSMI-wrapper" OFF)
option(BUILD_CLI "Build AMDSMI-CLI and install" ON)
option(ENABLE_LDCONFIG "Set library links and caches using ldconfig." ON)
# Set share path here because project name != amd_smi
@@ -130,15 +131,17 @@ if(BUILD_TESTS)
add_subdirectory("tests/amd_smi_test")
endif()
if(BUILD_PACKAGE)
add_subdirectory("py-interface")
add_subdirectory("py-interface")
if(BUILD_CLI)
add_subdirectory("amdsmi_cli")
endif()
include(CMakePackageConfigHelpers)
configure_package_config_file(
amd_smi-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/amd_smi-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/amd_smi
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${AMD_SMI}
PATH_VARS CMAKE_INSTALL_LIBDIR CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_BINDIR)
write_basic_package_version_file(
@@ -202,6 +205,8 @@ if(CPACK_RPM_PACKAGE_RELEASE)
endif()
set(CPACK_RPM_PACKAGE_PROVIDES "amd-smi")
set(CPACK_RPM_PACKAGE_REQUIRES "python3")
# don't terminate if bytecompile of python files fails
set(CPACK_RPM_SPEC_MORE_DEFINE "%define _python_bytecompile_errors_terminate_build 0")
# Add rocm-core dependency if -DROCM_DEP_ROCMCORE=ON is passed
if(ROCM_DEP_ROCMCORE)
+26 -36
Datei anzeigen
@@ -1,10 +1,9 @@
# AMD System Management Interface (AMD SMI) Library
The AMD System Management Interface Library, or AMD SMI library, is a C library for Linux that provides a user space interface for applications to monitor and control AMD devices.
## Supported platforms
At initial release, the AMD SMI library will support Linux bear metal and Linux virtual machine guest for AMD GPUs. In the future release, the library will be extended to support AMD EPYC™ CPUs.
AMD SMI library can run on AMD ROCm supported platforms, please refer to [List of Supported Operating Systems and GPUs](https://docs.amd.com/bundle/ROCm-Getting-Started-Guide-v5.3/page/Introduction_to_ROCm_Getting_Started_Guide_for_Linux.html)
@@ -12,9 +11,9 @@ AMD SMI library can run on AMD ROCm supported platforms, please refer to [List o
To run the AMD SMI library, the amdgpu driver needs to be installed. Optionally, the libdrm can be
installed to query firmware information and hardware IPs.
# Building AMD SMI
## Building AMD SMI
## Additional Required software for building
### Additional Required software for building
In order to build the AMD SMI library, the following components are required. Note that the software versions listed are what was used in development. Earlier versions are not guaranteed to work:
* CMake (v3.11.0) - `pip3 install cmake`
@@ -26,31 +25,35 @@ In order to build the AMD SMI python package, the following components are requi
* virtualenv - `pip3 install virtualenv`
In order to build the latest documentation, the following are required:
* DOxygen (1.8.11)
* latex (pdfTeX 3.14159265-2.6-1.40.16)
The source code for AMD SMI is available on Github.
After the AMD SMI library git repository has been cloned to a local Linux machine, building the library is achieved by following the typical CMake build sequence. Specifically,
After the AMD SMI library git repository has been cloned to a local Linux machine, the Default location for the library and headers is /opt/rocm. Building the library is achieved by following the typical CMake build sequence, specifically:
```bash
mkdir -p build
cd build
cmake <location of root of AMD SMI library CMakeLists.txt>
cmake ..
make -j $(nproc)
# Install library file and header; default location is /opt/rocm
make install```
make install
```
The built library will appear in the `build` folder.
To build the rpm and deb packages follow the above steps with:
```bash
make package```
## Documentation
```bash
make package
```
### Documentation
The reference manual, `refman.pdf` will be in the `latex` directory upon a successful build.
## Building the Tests
### Building the Tests
In order to verify the build and capability of AMD SMI on your system and to see an example of how AMD SMI can be used, you may build and run the tests that are available in the repo. To build the tests, follow these steps:
@@ -61,21 +64,23 @@ cmake -DBUILD_TESTS=ON <location of root of AMD SMI library CMakeLists.txt>
make -j $(nproc)
```
## Run the Tests
### Run the Tests
To run the test, execute the program `amdsmitst` that is built from the steps above.
# Usage Basics
## Usage Basics
### Device/Socket handles
## Device/Socket handles
Many of the functions in the library take a "socket handle" or "device handle". The socket is an abstraction of hardware physical socket. This will enable amd-smi to provide a better representation of the hardware to user. Although there is always one distinct GPU for a socket, the APU may have both
GPU device and CPU device on the same socket. Moreover, for MI200, it may have multiple GCDs.
To discover the sockets in the system, `amdsmi_get_socket_handles()` is called to get list of sockets
handles, which in turn can be used to query the devices in that socket using `amdsmi_get_device_handles()`. The device handler is used to distinguish the detected devices from one another. It is important to note that a device may end up with a different device handles after restart application, so a device handle should not be relied upon to be constant over process.
# Hello AMD SMI
The only required AMD-SMI call for any program that wants to use AMD-SMI is the `amdsmi_init()` call. This call initializes some internal data structures that will be used by subsequent AMD-SMI calls. In the call, a flag can be passed if the application is only interested in a specific device type.
## Hello AMD SMI
The only required AMD-SMI call for any program that wants to use AMD-SMI is the `amdsmi_init()` call. This call initializes some internal data structures that will be used by subsequent AMD-SMI calls. In the call, a flag can be passed if the application is only interested in a specific device type.
When AMD-SMI is no longer being used, `amdsmi_shut_down()` should be called. This provides a way to do any releasing of resources that AMD-SMI may have held.
@@ -158,30 +163,15 @@ The python wrapper (binding) is an auto-generated file `py-interface/amdsmi_wrap
Wrapper should be re-generated on each C++ API change, by doing:
```bash
make python_wrapper
cmake .. -DBUILD_WRAPPER=on
make python_wrapper # or simply 'make'
```
After this command, the file in `py-interface/amdsmi_wrapper.py` will be automatically updated.
After this command, the file in `py-interface/amdsmi_wrapper.py` will be automatically updated on each compile.
Note: To be able to re-generate python wrapper you need several tools installed on your system: clang-14, clang-format, libclang-dev
Note: python_wrapper is automatically generated with normal `make` and `make python_package` commands.
Warning: If clang version is older than clang-14 - the wrapper generation will fail with a warning.
To mitigate this issue - we include a pre-generated version of the wrapper.
# Python package
`amdsmi` package can be generated by running:
```bash
make python_package
```
It will be placed in following location: `build/py-interface/python_package/amdsmi-0.1-py3-none-any.whl`
The .whl package can be installed using `pip3 install amdsmi-0.1-py3-none-any.whl` command.
Warning: You must have python3.7 or above with virtualenv installed. Otherwise packaging will fail with a warning.
Note: python_wrapper is NOT automatically re-generated. You must run `cmake` with `-DBUILD_WRAPPER=on` argument.
## DISCLAIMER
+130
Datei anzeigen
@@ -0,0 +1,130 @@
#
# Copyright (C) 2023 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# 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
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# 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 logging
import re
class BDF():
""" BDF Class to cast and compare BDF objects using built-in python comparators
Useful for validating a BDF string and converting it to a BDF object
This allows us to handle BDF objects in a pythonic way
Attributes:
__eq__: The equals comparator
__: An integer count of the eggs we have laid.
"""
def __init__(self, bdf):
"""Init a BDF object"""
if isinstance(bdf, BDF):
self.segment, self.bus, self.device, self.function = tuple(bdf)
else:
if bdf.startswith("BDF("):
bdf = bdf.replace('BDF(', '').replace(')', '')
try:
bdf_components = [int(x, 16) for x in re.split('[:.]', bdf)]
except self.BDFError as e:
logging.error(f"Invalid string passed: {bdf}")
raise e
self.segment = bdf_components[0] if len(bdf_components) == 4 else 0
self.bus, self.device, self.function = bdf_components[-3:]
if self.segment > 65535:
raise self.BDFError("Segment can't be greater than 65535")
if self.bus > 255:
raise self.BDFError("Bus can't be greater than 255")
if self.device > 31:
raise self.BDFError("Device can't be greater than 31")
if self.function > 7:
raise self.BDFError("Function can't be greater than 7")
class BDFError(Exception):
"""BDF Class Error"""
def __eq__(self, passed_bdf):
"""Overrides the == operator and allows for BDF objects to be compared to BDF strings"""
# Only accept strings and BDF objects
if isinstance(passed_bdf, str):
if passed_bdf == '':
return False
passed_bdf = BDF(passed_bdf)
elif not isinstance(passed_bdf, BDF):
return False
if self.segment == passed_bdf.segment and \
self.bus == passed_bdf.bus and \
self.device == passed_bdf.device and \
self.function == passed_bdf.function:
return True
else:
return False
def __ne__(self, passed_bdf):
"""Overrides the != operator and allows for BDF objects to be compared to BDF strings"""
# Since we overrided the == operator we can use that to make this simple
return not self == passed_bdf
def __add__(self, passed_bdf):
"""Overrides the + operator and allows for string concatenation"""
return str(self) + passed_bdf
def __radd__(self, passed_bdf):
"""Overrides the + operator and allows for string concatenation"""
return passed_bdf + str(self)
def __str__(self):
"""Cast BDF object to a string"""
return "{:04X}:{:02X}:{:02X}:{}".format(self.segment, self.bus, self.device, self.function)
def __repr__(self):
"""How the BDF object is represented"""
return f"BDF({self})"
def __hash__(self):
"""Allow the BDF object to be hashable"""
return hash(str(self))
def __iter__(self):
"""Make the BDF object iterable over its 4 values"""
yield from (self.segment, self.bus, self.device, self.function)
def __contains__(self, passed_bdf):
"""Overrided the 'in' comparator in python"""
passed_bdf = str(BDF(passed_bdf))
bdf_regex = "(?:[0-6]?[0-9a-fA-F]{1,4}:)?[0-2]?[0-9a-fA-F]{1,2}:[0-9a-fA-F]{1,2}\.[0-7]"
for match in re.findall(bdf_regex, passed_bdf):
if self == match:
return True
return False
+89
Datei anzeigen
@@ -0,0 +1,89 @@
message("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&")
message(" CMake AMDSMI CLI Install ")
message("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&")
# Set CLI Build Directory
set(PY_PACKAGE_DIR "amdsmi_cli")
set(PY_CLI_INSTALL_DIR
"${CMAKE_INSTALL_LIBEXECDIR}" CACHE STRING
"CLI tool installation directory")
# if Python3 is found but the version is below 3.7 - Python3_FOUND is set to FALSE
find_package(Python3 3.7 COMPONENTS Interpreter Development)
# WARN: This is a HACK to pass compile on AMD rhel8 and centos7 CI systems!
# Those still use python3.6 which is too old for this project!
# TODO: Promote to an error when python3.7 or above is installed on CI
if(NOT Python3_FOUND)
message(AUTHOR_WARNING "Python3 DOESN'T EXIST OR VERSION IS TOO OLD!: ${Python3_VERSION}")
message(AUTHOR_WARNING "The CLI tool will not be created and the project will not be packaged!")
# WARN: EXIT CURRENT CMAKE FILE
return()
endif()
# hard-linking instead of copying avoids unnecessarry regeneration of packaged files
add_custom_command(
OUTPUT ${PY_PACKAGE_DIR}/__init__.py
${PY_PACKAGE_DIR}/_version.py
${PY_PACKAGE_DIR}/amdsmi_cli.py
${PY_PACKAGE_DIR}/amdsmi_commands.py
${PY_PACKAGE_DIR}/amdsmi_helpers.py
${PY_PACKAGE_DIR}/amdsmi_init.py
${PY_PACKAGE_DIR}/amdsmi_logger.py
${PY_PACKAGE_DIR}/amdsmi_parser.py
${PY_PACKAGE_DIR}/BDF.py
DEPENDS amdsmi_cli
COMMAND mkdir -p ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/_version.py ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_cli.py ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_commands.py ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_helpers.py ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_init.py ${PY_PACKAGE_DIR}/
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}/BDF.py ${PY_PACKAGE_DIR}/)
# The CLI requires the python amdsmi wrapper to be installed
add_custom_target(
amdsmi_cli ALL
DEPENDS python_package
${PY_PACKAGE_DIR}/__init__.py
${PY_PACKAGE_DIR}/_version.py
${PY_PACKAGE_DIR}/amdsmi_cli.py
${PY_PACKAGE_DIR}/amdsmi_commands.py
${PY_PACKAGE_DIR}/amdsmi_helpers.py
${PY_PACKAGE_DIR}/amdsmi_init.py
${PY_PACKAGE_DIR}/amdsmi_logger.py
${PY_PACKAGE_DIR}/amdsmi_parser.py
${PY_PACKAGE_DIR}/BDF.py)
install(
DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${PY_PACKAGE_DIR}
DESTINATION ${PY_CLI_INSTALL_DIR})
install(
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${PY_PACKAGE_DIR}/amdsmi_cli.py
DESTINATION ${PY_CLI_INSTALL_DIR}/${PY_PACKAGE_DIR})
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
# symlink amdsmi_cli.py to amd-smi
add_custom_target(
link_amdsmi_cli ALL
DEPENDS amdsmi_cli
BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/bin/amd-smi
${CMAKE_CURRENT_BINARY_DIR}/bin/gpuv-smi
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND ${CMAKE_COMMAND} -E create_symlink
../${PY_CLI_INSTALL_DIR}/${PY_PACKAGE_DIR}/amdsmi_cli.py
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}/amd-smi
COMMAND ${CMAKE_COMMAND} -E create_symlink
amd-smi
${CMAKE_CURRENT_BINARY_DIR}/bin/gpuv-smi)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/bin/amd-smi
${CMAKE_CURRENT_BINARY_DIR}/bin/gpuv-smi
DESTINATION ${CMAKE_INSTALL_BINDIR})
Datei anzeigen
+1
Datei anzeigen
@@ -0,0 +1 @@
__version__ = "0.0.1"
Ausführbare Datei
+72
Datei anzeigen
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
#
# Copyright (C) 2023 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# 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
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# 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 logging
import sys
from amdsmi_commands import AMDSMICommands
from amdsmi_parser import AMDSMIParser
from amdsmi_logger import AMDSMILogger
if __name__ == "__main__":
# Set compatability mode based on which cli mapping user selects
if 'gpuv-smi' in sys.argv[0]:
compatibility = AMDSMILogger.LoggerCompatibility.gpuvsmi.value
elif 'rocm-smi' in sys.argv[0]:
compatibility = AMDSMILogger.LoggerCompatibility.rocmsmi.value
else:
compatibility = AMDSMILogger.LoggerCompatibility.amdsmi.value
amd_smi_commands = AMDSMICommands(compatibility=compatibility)
amd_smi_parser = AMDSMIParser(amd_smi_commands.version,
amd_smi_commands.discovery,
amd_smi_commands.static,
amd_smi_commands.firmware,
amd_smi_commands.bad_pages,
amd_smi_commands.metric,
amd_smi_commands.process,
amd_smi_commands.profile,
amd_smi_commands.event,
amd_smi_commands.topology,
amd_smi_commands.set_value,
amd_smi_commands.reset,
amd_smi_commands.rocm_smi)
args = amd_smi_parser.parse_args(args=None if sys.argv[1:] else ['--help'])
# Handle command modifiers before subcommand execution
if args.json:
amd_smi_commands.logger.format = amd_smi_commands.logger.LoggerFormat.json.value
if args.csv:
amd_smi_commands.logger.format = amd_smi_commands.logger.LoggerFormat.csv.value
if args.file:
amd_smi_commands.logger.destination = args.file
if args.loglevel:
logging_dict = {'DEBUG' : logging.DEBUG,
'INFO' : logging.INFO,
'WARNING': logging.WARNING,
'ERROR': logging.ERROR,
'CRITICAL': logging.CRITICAL}
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging_dict[args.loglevel])
# Execute subcommands
args.func(args)
Datei-Diff unterdrückt, da er zu groß ist Diff laden
+280
Datei anzeigen
@@ -0,0 +1,280 @@
#!/usr/bin/env python3
#
# Copyright (C) 2023 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# 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
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# 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 logging
import platform
import time
from pathlib import Path
from amdsmi_init import *
from BDF import BDF
class AMDSMIHelpers():
"""Helper functions that aren't apart of the AMDSMI API
Useful for determining platform and device identifiers
Functions:
os_info: tuple ()
"""
def __init__(self) -> None:
self.operating_system = platform.system()
self._is_hypervisor = False
self._is_virtual_os = False
self._is_baremetal = False
self._is_linux = False
self._is_windows = False
if self.operating_system.startswith("Linux"):
self._is_linux = True
logging.debug(f"AMDSMIHelpers: Platform is linux:{self._is_linux}")
product_name = ""
product_name_path = Path("/sys/class/dmi/id/product_name")
if product_name_path.exists():
product_name = product_name_path.read_text().strip()
if product_name == "":
# Unable to determine product_name default to baremetal
self._is_baremetal = True
# Determine if a system is baremetal by deduction
self._is_baremetal = not self._is_hypervisor and not self._is_virtual_os
def os_info(self, string_format=True):
"""Return operating_system and type information ex. (Linux, Baremetal)
params:
string_format (bool) True to return in string format, False to return Tuple
returns:
str or (str, str)
"""
operating_system = ""
if self._is_linux:
operating_system = "Linux"
elif self._is_windows:
operating_system = "Windows"
else:
operating_system = "Unknown"
operating_system_type = ""
if self._is_baremetal:
operating_system_type = "Baremetal"
elif self._is_virtual_os:
operating_system_type = "Guest"
elif self._is_hypervisor:
operating_system_type = "Hypervisor"
else:
operating_system_type = "Unknown"
if string_format:
return f"{operating_system} {operating_system_type}"
else:
return (operating_system, operating_system_type)
def is_virtual_os(self):
return self._is_virtual_os
def is_hypervisor(self):
# Returns True if hypervisor is enabled on the system
return self._is_hypervisor
def is_baremetal(self):
# Returns True if system is baremetal, if system is hypervisor this should return False
return self._is_baremetal
def is_linux(self):
return self._is_linux
def is_windows(self):
return self._is_windows
def get_gpu_choices(self):
"""Return dictionary of possible GPU choices and string of the output:
Dictionary will be in format: gpus[ID] : (BDF, UUID, Device Handle)
String output will be in format:
"ID:0 | BDF:0000:23:00.0 | UUID:ffffffff-0000-1000-0000-000000000000"
params:
None
return:
(dict, str) : (gpu_choices, gpu_choices_str)
"""
gpu_choices = {}
gpu_choices_str = ""
# amdsmi_get_device_handles returns the device_handles storted by gpu_id
device_handles = amdsmi_interface.amdsmi_get_device_handles()
for gpu_id, device_handle in enumerate(device_handles):
bdf = amdsmi_interface.amdsmi_get_device_bdf(device_handle)
uuid = amdsmi_interface.amdsmi_get_device_uuid(device_handle)
gpu_choices[str(gpu_id)] = {
"BDF": bdf,
"UUID": uuid,
"Device Handle": device_handle,
}
gpu_choices_str += f"ID:{gpu_id:<2} | BDF:{bdf} | UUID:{uuid}"
return (gpu_choices, gpu_choices_str)
def get_device_handles_from_gpu_selections(self, gpu_selections, gpu_choices=None):
"""Convert provided gpu_selections to device_handles
Args:
gpu_selections (list[str]): This will be the GPU ID, BDF, or UUID:
ex: ID:0 | BDF:0000:23:00.0 | UUID:ffffffff-0000-1000-0000-000000000000
gpu_choices (dict{gpu_choices}): This is a dictionary of the possible gpu_choices
Returns:
(True, list[device_handles]): Returns a list of all the gpu_selections converted to
amdsmi device_handles
(False, str): Return False, and the first input that failed to be converted
"""
if isinstance(gpu_selections, str):
gpu_selections = [gpu_selections]
if gpu_choices is None:
gpu_choices = self.get_gpu_choices()[0]
selected_device_handles = []
for gpu_selection in gpu_selections:
valid_gpu_choice = False
for gpu_id, gpu_info in gpu_choices.items():
bdf = gpu_info['BDF']
uuid = gpu_info['UUID']
device_handle = gpu_info['Device Handle']
# Check if passed gpu is a gpu ID or UUID
if gpu_selection == gpu_id or gpu_selection.lower() == uuid:
selected_device_handles.append(device_handle)
valid_gpu_choice = True
break
else: # Check if gpu passed is a BDF object
try:
if BDF(gpu_selection) == BDF(bdf):
selected_device_handles.append(device_handle)
valid_gpu_choice = True
break
except Exception:
# Ignore exception when checking if the gpu_choice is a BDF
pass
if not valid_gpu_choice:
logging.debug(f"AMDSMIHelpers.get_device_handles_from_gpu_selections - Unable to convert {gpu_selection}")
return False, gpu_selection
return True, selected_device_handles
def handle_watch(self, args, subcommand):
"""This function will run the subcommand multiple times based
on the passed watch, watch_time, and iterations passed in.
params:
args - argparser args to pass to subcommand
subcommand (AMDSMICommands) - Function that can handle
watching output (Currently: metric & process)
return:
Nothing
"""
# Set the values for watching as the args will cleared
watch = args.watch
watch_time = args.watch_time
iterations = args.iterations
# Set the args values to None so we don't loop recursively
args.watch = None
args.watch_time = None
args.iterations = None
if watch_time: # Run for set amount of time
iterations_ran = 0
end_time = time.time() + watch_time
while time.time() <= end_time:
subcommand(args, watching_output=True)
# Handle iterations limit
iterations_ran += 1
if iterations:
if iterations >= iterations_ran:
break
time.sleep(watch)
elif iterations: # Run for a set amount of iterations
for iteration in range(iterations):
subcommand(args, watching_output=True)
if iteration == iterations - 1: # Break on iteration completion
break
time.sleep(watch)
else: # Run indefinitely as watch_time and iterations are not set
while True:
subcommand(args, watching_output=True)
time.sleep(watch)
return 1
def get_gpu_id_from_device_handle(self, input_device_handle):
"""Get the gpu index from the device_handle.
amdsmi_get_device_handles() returns the list of device_handles in order of gpu_index
"""
device_handles = amdsmi_interface.amdsmi_get_device_handles()
for gpu_index, device_handle in enumerate(device_handles):
if input_device_handle.value == device_handle.value:
return gpu_index
raise IndexError("Unable to find gpu ID from device_handle")
def get_amd_gpu_bdfs(self):
"""Return a list of GPU BDFs visibile to amdsmi
Returns:
list[BDF]: List of GPU BDFs
"""
gpu_bdfs = []
device_handles = amdsmi_interface.amdsmi_get_device_handles()
for device_handle in device_handles:
bdf = amdsmi_interface.amdsmi_get_device_bdf(device_handle)
gpu_bdfs.append(bdf)
return gpu_bdfs
# def get_amd_cpu_bdfs(self):
# pass
def is_amd_device(self, device_handle):
""" Return whether the specified device is an AMD device or not
param device: DRM device identifier
"""
# Get card vendor id
asic_info = amdsmi_interface.amdsmi_get_asic_info(device_handle)
return asic_info['vendor_id'] == AMD_VENDOR_ID
+96
Datei anzeigen
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
#
# Copyright (C) 2023 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# 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
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# 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.
#
### Handle safe initialization for amdsmi
import atexit
import logging
import signal
import sys
from pathlib import Path
sys.path.append(f'{Path(__file__).resolve().parent}/../../share/amd_smi')
import amdsmi as amdsmi_interface
# Using basic python logging for user errors and development
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.ERROR) # User level logging
# On initial import set initialized variable
AMDSMI_INITIALIZED = False
AMD_VENDOR_ID = 4098
def check_amdgpu_driver():
""" Returns true if amdgpu is found in the list of initialized modules """
amd_gpu_status_file = Path("/sys/module/amdgpu/initstate")
if amd_gpu_status_file.exists():
if amd_gpu_status_file.read_text(encoding='ascii').strip() == 'live':
return True
return False
def init_amdsmi(flag=amdsmi_interface.AmdSmiInitFlags.AMD_GPUS):
""" Initializes AMDSMI
Raises:
err: AmdSmiLibraryException if not successful
"""
# Check if amdgpu driver is up & Handle error gracefully
if check_amdgpu_driver():
# Only init AMD GPUs for now, waiting for future support for AMD CPUs
try:
amdsmi_interface.amdsmi_init(flag)
except (amdsmi_interface.AmdSmiLibraryException, amdsmi_interface.AmdSmiParameterException) as err:
raise err
logging.info('AMDSMI initialized successfully') # without errors really
else:
logging.error('Driver not initialized (amdgpu not found in modules)')
exit(-1)
def shut_down_amdsmi():
"""Shutdown AMDSMI instance
Raises:
err: AmdSmiLibraryException if not successful
"""
try:
amdsmi_interface.amdsmi_shut_down()
except amdsmi_interface.AmdSmiLibraryException as err:
raise err
def signal_handler(sig, frame):
logging.debug(f'Handling signal: {sig}')
sys.exit(0)
if not AMDSMI_INITIALIZED:
init_amdsmi()
AMDSMI_INITIALIZED = True
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
atexit.register(shut_down_amdsmi)
+384
Datei anzeigen
@@ -0,0 +1,384 @@
#!/usr/bin/env python3
#
# Copyright (C) 2023 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# 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
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# 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 json
import time
import yaml
import re
from enum import Enum
from amdsmi_helpers import AMDSMIHelpers
class AMDSMILogger():
def __init__(self, compatibility='amdsmi', format='human_readable',
destination='stdout') -> None:
self.output = {}
self.multiple_device_output = []
self.watch_output = []
self.compatibility = compatibility # amd-smi, gpuv-smi, or rocm-smi
self.format = format # csv, json, or human_readable
self.destination = destination # stdout, path to a file (append)
self.amd_smi_helpers = AMDSMIHelpers()
class LoggerFormat(Enum):
"""Enum for logger formats"""
json = 'json'
csv = 'csv'
human_readable = 'human_readable'
class LoggerCompatibility(Enum):
"""Enum for logger compatibility"""
amdsmi = 'amdsmi'
rocmsmi = 'rocmsmi'
gpuvsmi = 'gpuvsmi'
def is_json_format(self):
return self.format == self.LoggerFormat.json.value
def is_csv_format(self):
return self.format == self.LoggerFormat.csv.value
def is_human_readable_format(self):
return self.format == self.LoggerFormat.human_readable.value
def is_amdsmi_compatibility(self):
return self.compatibility == self.LoggerCompatibility.amdsmi.value
def is_rocmsmi_compatibility(self):
return self.compatibility == self.LoggerCompatibility.rocmsmi.value
def is_gpuvsmi_compatibility(self):
return self.compatibility == self.LoggerCompatibility.gpuvsmi.value
def store_output(self, device_handle, argument, data):
""" Store the argument and device handle according to the compatibility.
Each compatibility function will handle the output format and
populate the output
params:
device_handle - device handle object to the target device output
argument (str) - key to store data
data (dict | list) - Data store against argument
return:
Nothing
"""
gpu_id = self.amd_smi_helpers.get_gpu_id_from_device_handle(device_handle)
if self.is_amdsmi_compatibility():
self._store_output_amdsmi(gpu_id=gpu_id, argument=argument, data=data)
elif self.is_rocmsmi_compatibility():
self._store_output_rocmsmi(gpu_id=gpu_id, argument=argument, data=data)
elif self.is_gpuvsmi_compatibility():
self._store_output_gpuvsmi(gpu_id=gpu_id, argument=argument, data=data)
def _store_output_amdsmi(self, gpu_id, argument, data):
if self.is_json_format() or self.is_human_readable_format():
self.output['gpu'] = int(gpu_id)
if argument == 'values' and isinstance(data, dict):
self.output.update(data)
else:
self.output[argument] = data
elif self.is_csv_format():
# put output into self.csv_output
pass
else:
raise "err"
def _store_output_rocmsmi(self, gpu_id, argument, data):
if self.is_json_format():
# put output into self.json_output
pass
elif self.is_csv_format():
# put output into self.csv_output
pass
elif self.is_human_readable_format():
# put output into self.human_readable_output
pass
else:
raise "err"
def _store_output_gpuvsmi(self, gpu_id, argument, data):
if self.is_json_format() or self.is_human_readable_format():
self.output['gpu'] = int(gpu_id)
self.output[argument] = data
elif self.is_csv_format():
# put output into self.csv_output
pass
else:
raise "err"
def store_multiple_device_output(self):
""" Store the current output into the multiple_device_output
then clear the current output
params:
None
return:
Nothing
"""
if self.is_amdsmi_compatibility():
self._store_multiple_device_output_amdsmi()
elif self.is_rocmsmi_compatibility():
self._store_multiple_device_output_rocmsmi()
elif self.is_gpuvsmi_compatibility():
self._store_multiple_device_output_gpuvsmi()
def _store_multiple_device_output_amdsmi(self):
if self.is_json_format() or self.is_human_readable_format():
self.multiple_device_output.append(self.output)
self.output = {}
elif self.is_csv_format():
# put output into self.csv_output
pass
else:
raise "err"
def _store_multiple_device_output_rocmsmi(self):
if self.is_json_format():
# put output into self.json_output
pass
elif self.is_csv_format():
# put output into self.csv_output
pass
elif self.is_human_readable_format():
# put output into self.human_readable_output
pass
else:
raise "err"
def _store_multiple_device_output_gpuvsmi(self):
if self.is_json_format() or self.is_human_readable_format():
self.multiple_device_output.append(self.output)
self.output = {}
elif self.is_csv_format():
# put output into self.csv_output
pass
else:
raise "err"
def store_watch_output(self, multiple_devices=False):
""" Add the current output or multiple_devices_output
params:
multiple_devices (bool) - True if watching multiple devices
return:
Nothing
"""
if self.is_amdsmi_compatibility():
self._store_watch_output_amdsmi(multiple_devices=multiple_devices)
elif self.is_rocmsmi_compatibility():
self._store_watch_output_rocmsmi(multiple_devices=multiple_devices)
elif self.is_gpuvsmi_compatibility():
self._store_watch_output_gpuvsmi(multiple_devices=multiple_devices)
def _store_watch_output_amdsmi(self, multiple_devices):
if self.is_json_format() or self.is_human_readable_format():
values = self.output
if multiple_devices:
values = self.multiple_device_output
self.watch_output.append({'timestamp': int(time.time()),
'values': values})
elif self.is_csv_format():
# put output into self.csv_output
pass
else:
raise "err"
def _store_watch_output_rocmsmi(self, multiple_devices):
if self.is_json_format():
# put output into self.json_output
pass
elif self.is_csv_format():
# put output into self.csv_output
pass
elif self.is_human_readable_format():
# put output into self.human_readable_output
pass
else:
raise "err"
def _store_watch_output_gpuvsmi(self, multiple_devices):
if self.is_json_format() or self.is_human_readable_format():
values = self.output
if multiple_devices:
values = self.multiple_device_output
self.watch_output.append({'timestamp': int(time.time()),
'values': values})
elif self.is_csv_format():
# put output into self.csv_output
pass
else:
raise "err"
def capitalize_keys(self, input_dict):
output_dict = {}
for key in input_dict.keys():
# Capitalize key if it is a string
if isinstance(key, str):
cap_key = key.upper()
else:
cap_key = key
if isinstance(input_dict[key], dict):
output_dict[cap_key] = self.capitalize_keys(input_dict[key])
elif isinstance(input_dict[key], list):
cap_key_list = []
for data in input_dict[key]:
if isinstance(data, dict):
cap_key_list.append(self.capitalize_keys(data))
else:
cap_key_list.append(data)
output_dict[cap_key] = cap_key_list
else:
output_dict[cap_key] = input_dict[key]
return output_dict
def convert_json_to_human_readable(self, json_object):
# First Capitalize all keys in the json object
capitalized_json = self.capitalize_keys(json_object)
json_string = json.dumps(capitalized_json, indent=4)
yaml_data = yaml.safe_load(json_string)
yaml_output = yaml.dump(yaml_data, sort_keys=False, allow_unicode=True)
if self.is_gpuvsmi_compatibility():
# Convert from GPU: 0 to GPU 0:
yaml_output = re.sub('GPU: ([0-9]+)', 'GPU \\1:', yaml_output)
# Remove a key line if it is a spacer
yaml_output = yaml_output.replace("AMDSMI_SPACING_REMOVAL:\n", "")
yaml_output = yaml_output.replace("'", "") # Remove ''
clean_yaml_output = ''
for line in yaml_output.splitlines():
line = line.split(':')
# Remove dashes and increase tabbing split key
line[0] = line[0].replace("-", " ", 1)
line[0] = line[0].replace(" ", " ")
# Join cleaned output
line = ':'.join(line) + '\n'
clean_yaml_output += line
return clean_yaml_output
def print_output(self, multiple_device_output=False, watch_output=False):
""" Print current output acording to format and then destination
params:
multiple_device_output (bool) - True if printing output from
multiple devices
watch_output (bool) - True if printing watch output
return:
Nothing
"""
if self.is_json_format():
self._print_json_output(multiple_device_output=multiple_device_output,
watch_output=watch_output)
elif self.is_csv_format():
self._print_csv_output(multiple_device_output=multiple_device_output,
watch_output=watch_output)
elif self.is_human_readable_format():
self._print_human_readable_output(multiple_device_output=multiple_device_output,
watch_output=watch_output)
def _print_json_output(self, multiple_device_output=False, watch_output=False):
json_output = json.dumps(self.output, indent = 4)
json_multiple_device_output = json.dumps(self.multiple_device_output, indent = 4)
if self.destination == 'stdout':
if watch_output:
return # We don't need to print to stdout at the end of watch
elif multiple_device_output:
print(json_multiple_device_output)
else:
print(json_output)
else: # Write output to file
if watch_output:
with self.destination.open('w') as output_file:
json.dump(self.watch_output, output_file, indent=4)
elif multiple_device_output:
with self.destination.open('a') as output_file:
json.dump(self.multiple_device_output, output_file, indent=4)
else:
with self.destination.open('a') as output_file:
json.dump(self.output, output_file, indent=4)
def _print_csv_output(self, multiple_device_output=False, watch_output=False):
if self.destination == 'stdout':
if watch_output:
return # We don't need to print to stdout at the end of watch
elif multiple_device_output:
pass
else:
pass
else: # Write output to file
if watch_output:
pass
elif multiple_device_output:
pass
else:
pass
def _print_human_readable_output(self, multiple_device_output=False, watch_output=False):
if multiple_device_output:
human_readable = ''
for output in self.multiple_device_output:
human_readable += (self.convert_json_to_human_readable(output))
else:
human_readable = self.convert_json_to_human_readable(self.output)
if self.destination == 'stdout':
if watch_output:
return
# print_output may need another value: flush_output vs watch_output
print(human_readable)
else:
if watch_output:
return
with self.destination.open('a') as output_file:
output_file.write(human_readable)
return
+719
Datei anzeigen
@@ -0,0 +1,719 @@
#!/usr/bin/env python3
#
# Copyright (C) 2023 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# 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
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# 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 errno
import os
import time
from pathlib import Path
from _version import __version__
from amdsmi_helpers import AMDSMIHelpers
class AMDSMIParser(argparse.ArgumentParser):
def __init__(self, version, discovery, static, firmware, bad_pages, metric,
process, profile, event, topology, set_value, reset, rocmsmi):
# Helper variables
self.amd_smi_helpers = AMDSMIHelpers()
self.gpu_choices, self.gpu_choices_str = self.amd_smi_helpers.get_gpu_choices()
self.vf_choices = ['3', '2', '1']
version_string = f"Version: {__version__}"
platform_string = f"Platform: {self.amd_smi_helpers.os_info()}"
# Adjust argument parser options
super().__init__(
formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog,
max_help_position=80,
width=90),
description=f"AMD System Management Interface | {version_string} | {platform_string}",
add_help=True,
prog="amdsmi_cli")
# Setup subparsers
subparsers = self.add_subparsers(
title="AMD-SMI Commands",
parser_class=argparse.ArgumentParser,
required=True,
help="Descriptions:",
# dest='cmd',
metavar="")
# Add all subparsers
self._add_version_parser(subparsers, version)
self._add_discovery_parser(subparsers, discovery)
self._add_static_parser(subparsers, static)
self._add_firmware_parser(subparsers, firmware)
self._add_bad_pages_parser(subparsers, bad_pages)
self._add_metric_parser(subparsers, metric)
self._add_process_parser(subparsers, process)
self._add_profile_parser(subparsers, profile)
self._add_event_parser(subparsers, event)
self._add_topology_parser(subparsers, topology)
self._add_set_value_parser(subparsers, set_value)
self._add_reset_parser(subparsers, reset)
self._add_rocm_smi_parser(subparsers, rocmsmi)
def _positive_int(self, int_value):
# Argument type validator
if int_value.isdigit(): # Is digit works only on positive numbers
return int(int_value)
else:
raise argparse.ArgumentTypeError(
f"invalid input:{int_value} integer provided must be positive")
def _check_output_file_path(self):
""" Argument action validator:
Returns a path to a file from the output file path provided.
If the path is a directory then create a file within it and return that file path
If the path is a file and it exists return the file path
If the path is a file and it doesn't exist create and return the file path
"""
class CheckOutputFilePath(argparse.Action):
# Checks the values
def __call__(self, parser, args, values, option_string=None):
path = Path(values)
if not path.exists():
if path.parent.is_dir():
path.touch()
else:
raise argparse.ArgumentTypeError(
f"Invalid path:{path} Could not find parent directory of given path")
if path.is_dir():
path = path / f"{int(time.time())}-amdsmi-output.txt"
path.touch()
setattr(args, self.dest, path)
elif path.is_file():
setattr(args, self.dest, path)
else:
raise argparse.ArgumentTypeError(
f"Invalid path:{path} Could not determine if value given is a valid path")
return CheckOutputFilePath
def _check_input_file_path(self):
""" Argument action validator:
Returns a path to a file from the input file path provided.
If the file doesn't exist or is empty raise error
"""
class _CheckInputFilePath(argparse.Action):
# Checks the values
def __call__(self, parser, args, values, option_string=None):
path = Path(values)
if not path.exists():
raise FileNotFoundError(
errno.ENOENT, os.strerror(errno.ENOENT), values)
if path.is_dir():
raise argparse.ArgumentTypeError(
f"Invalid Path: {path} is directory when it needs to be a specific file")
if path.is_file():
if os.stat(values).st_size == 0:
raise argparse.ArgumentTypeError(
f"Invalid Path: {path} Input file is empty")
setattr(args, self.dest, path)
else:
raise argparse.ArgumentTypeError(
f"Invalid path:{path} Could not determine if value given is a valid path")
return _CheckInputFilePath
def _check_watch_selected(self):
""" Argument action validator:
Validate that the -w/--watch argument was selected
This is because -W/--watch_time and -i/--iterations are dependent on watch
"""
class _WatchSelectedAction(argparse.Action):
# Checks the values
def __call__(self, parser, args, values, option_string=None):
if args.watch is None:
raise argparse.ArgumentError(self, f"Invalid argument: '{self.dest}' needs to be paired with -w/--watch")
setattr(args, self.dest, values)
return _WatchSelectedAction
def _gpu_select(self, gpu_choices):
""" Argument action validator:
Custom argparse action to return the device handle(s) for the gpu(s) selected
This will set the destination (args.gpu) to a list of 1 or more device handles
If 1 or more device handles are not found then raise an ArgumentError for the first invalid gpu seen
"""
amd_smi_helpers = self.amd_smi_helpers
class _GPUSelectAction(argparse.Action):
# Checks the values
def __call__(self, parser, args, values, option_string=None):
status, selected_device_handles = amd_smi_helpers.get_device_handles_from_gpu_selections(gpu_selections=values,
gpu_choices=gpu_choices)
if status:
setattr(args, self.dest, selected_device_handles)
else:
invalid_selection = selected_device_handles
raise argparse.ArgumentError(self, f"invalid choice: '{invalid_selection}' (see available choices with -h)")
return _GPUSelectAction
def _add_command_modifiers(self, subcommand_parser, rocm_smi=True, gpuv_smi=True):
json_help = "Displays output in JSON format (human readable by default)."
csv_help = "Displays output in CSV format (human readable by default)."
file_help = "Saves output into a file on the provided path (stdout by default)."
loglevel_help = "Set the logging level for the parser commands"
command_modifier_group = subcommand_parser.add_argument_group('Command Modifiers')
# Output Format options
logging_args = command_modifier_group.add_mutually_exclusive_group()
logging_args.add_argument('--json', action='store_true', required=False, help=json_help)
logging_args.add_argument('--csv', action='store_true', required=False, help=csv_help)
command_modifier_group.add_argument('--file', action=self._check_output_file_path(), type=str, required=False, help=file_help)
# Placing loglevel outside the subcommands so it can be used with any subcommand
command_modifier_group.add_argument('--loglevel', action='store', required=False, help=loglevel_help, default='ERROR',
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"])
def _add_device_arguments(self, subcommand_parser, required=False):
# Device arguments help text
gpu_help = f"Select a GPU ID, BDF, or UUID from the possible choices:\n{self.gpu_choices_str}"
vf_help = "Gets general information about the specified VF (timeslice, fb info, …).\
\nAvailable only on virtualization OSs"
# Mutually Exclusive Args within the subparser
device_args = subcommand_parser.add_mutually_exclusive_group(required=required)
device_args.add_argument('-g', '--gpu', action=self._gpu_select(self.gpu_choices),
nargs='+', help=gpu_help)
if self.amd_smi_helpers.is_hypervisor():
device_args.add_argument('-v', '--vf', action='store', nargs='+',
help=vf_help, choices=self.vf_choices)
def _add_version_parser(self, subparsers, func):
# Subparser help text
version_help = "Display version information"
# Create version subparser
version_parser = subparsers.add_parser('version', help=version_help, description=None)
version_parser._optionals.title = None
version_parser.formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=80, width=90)
version_parser.set_defaults(func=func)
self._add_command_modifiers(version_parser)
def _add_discovery_parser(self, subparsers, func):
# Subparser help text
discovery_help = "Display discovery information"
discovery_subcommand_help = "Lists all the devices on the system and the links between devices.\
\nLists all the sockets and for each socket, GPUs and/or CPUs associated to\
\nthat socket alongside some basic information for each device.\
\nIn virtualization environment, it can also list VFs associated to each\
\nGPU with some basic information for each VF."
# Create discovery subparser
discovery_parser = subparsers.add_parser('discovery', help=discovery_help, description=discovery_subcommand_help, aliases=['list'])
discovery_parser.formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=80, width=90)
discovery_parser.set_defaults(func=func)
# Add Command Modifiers
self._add_command_modifiers(discovery_parser)
# Add Device args
self._add_device_arguments(discovery_parser, required=False)
def _add_static_parser(self, subparsers, func):
# Subparser help text
static_help = "Gets static information about the specified GPU"
static_subcommand_help = "If no argument is provided, return static information for all GPUs on the system.\
\nIf no static argument is specified all static information will be displayed."
static_optionals_title = "Static Arguments"
# Optional arguments help text
asic_help = "All asic information"
bus_help = "All bus information"
vbios_help = "All video bios information (if available)"
limit_help = "All limit metric values (i.e. power and thermal limits)"
driver_help = "Displays driver version"
caps_help = "All caps information"
# Options arguments help text for Hypervisors and Baremetal
ras_help = "Displays RAS features information"
board_help = "All board information" # Linux Baremetal only
# Options arguments help text for Hypervisors
dfc_help = "All DFC FW table information"
fb_help = "Displays Frame Buffer information"
num_vf_help = "Displays number of supported and enabled VFs"
# Create static subparser
static_parser = subparsers.add_parser('static', help=static_help, description=static_subcommand_help)
static_parser._optionals.title = static_optionals_title
static_parser.formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=80, width=90)
static_parser.set_defaults(func=func)
self._add_command_modifiers(static_parser)
# Add Device args
self._add_device_arguments(static_parser, required=False)
# Optional Args
static_parser.add_argument('-a', '--asic', action='store_true', required=False, help=asic_help)
static_parser.add_argument('-b', '--bus', action='store_true', required=False, help=bus_help)
static_parser.add_argument('-V', '--vbios', action='store_true', required=False, help=vbios_help)
static_parser.add_argument('-l', '--limit', action='store_true', required=False, help=limit_help)
static_parser.add_argument('-d', '--driver', action='store_true', required=False, help=driver_help)
static_parser.add_argument('-c', '--caps', action='store_true', required=False, help=caps_help)
# Options to display on Hypervisors and Baremetal
if self.amd_smi_helpers.is_hypervisor() or self.amd_smi_helpers.is_baremetal():
static_parser.add_argument('-r', '--ras', action='store_true', required=False, help=ras_help)
if self.amd_smi_helpers.is_linux():
static_parser.add_argument('-B', '--board', action='store_true', required=False, help=board_help)
# Options to only display on a Hypervisor
if self.amd_smi_helpers.is_hypervisor():
static_parser.add_argument('-d', '--dfc-ucode', action='store_true', required=False, help=dfc_help)
static_parser.add_argument('-f', '--fb-info', action='store_true', required=False, help=fb_help)
static_parser.add_argument('-n', '--num-vf', action='store_true', required=False, help=num_vf_help)
def _add_firmware_parser(self, subparsers, func):
# Subparser help text
firmware_help = "Gets firmware information about the specified GPU"
firmware_subcommand_help = "If no argument is provided, return firmware information for all GPUs on the system."
firmware_optionals_title = "Firmware Arguments"
# Optional arguments help text
fw_list_help = "All FW list information"
err_records_help = "All error records information"
# Create firmware subparser
firmware_parser = subparsers.add_parser('firmware', help=firmware_help, description=firmware_subcommand_help, aliases=['ucode'])
firmware_parser._optionals.title = firmware_optionals_title
firmware_parser.formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=80, width=90)
firmware_parser.set_defaults(func=func)
self._add_command_modifiers(firmware_parser)
# Add Device args
self._add_device_arguments(firmware_parser, required=False)
# Optional Args
firmware_parser.add_argument('-f', '--ucode-list', '--fw-list', dest='fw_list', action='store_true', required=False, help=fw_list_help, default=True)
# Options to only display on a Hypervisor
if self.amd_smi_helpers.is_hypervisor():
firmware_parser.add_argument('-e', '--error-records', action='store_true', required=False, help=err_records_help)
def _add_bad_pages_parser(self, subparsers, func):
if not (self.amd_smi_helpers.is_baremetal() and self.amd_smi_helpers.is_linux()):
# The bad_pages subcommand is only applicable to Linux Baremetal systems
return
# Subparser help text
bad_pages_help = "Gets bad page information about the specified GPU"
bad_pages_subcommand_help = "If no argument is provided, return bad page information for all GPUs on the system."
bad_pages_optionals_title = "Bad pages Arguments"
# Optional arguments help text
pending_help = "Displays all pending retired pages"
retired_help = "Displays retired pages"
un_res_help = "Displays unreservable pages"
# Create bad_pages subparser
bad_pages_parser = subparsers.add_parser('bad-pages', help=bad_pages_help, description=bad_pages_subcommand_help, aliases=['bad_pages'])
bad_pages_parser._optionals.title = bad_pages_optionals_title
bad_pages_parser.formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=80, width=90)
bad_pages_parser.set_defaults(func=func)
self._add_command_modifiers(bad_pages_parser)
# Add Device args
self._add_device_arguments(bad_pages_parser, required=False)
# Optional Args
bad_pages_parser.add_argument('-p', '--pending', action='store_true', required=False, help=pending_help)
bad_pages_parser.add_argument('-r', '--retired', action='store_true', required=False, help=retired_help)
bad_pages_parser.add_argument('-u', '--un-res', action='store_true', required=False, help=un_res_help)
def _add_metric_parser(self, subparsers, func):
# Subparser help text
metric_help = "Gets metric/performance information about the specified GPU"
metric_subcommand_help = "If no argument is provided, return metric information for all GPUs on the system.\
\nIf no metric argument is specified all metric information will be displayed."
metric_optionals_title = "Metric arguments"
# Optional arguments help text
usage_help = "Displays engine usage information"
watch_help = "Reprint the command in a loop of Interval seconds"
watch_time_help = "The total time to watch the given command"
iterations_help = "Total number of iterations to loop on the given command"
# Help text for Arguments only Available on Virtual OS and Baremetal platforms
fb_usage_help = "Total and used framebuffer"
# Help text for Arguments only on Hypervisor and Baremetal platforms
power_help = "Current power usage"
clock_help = "Average, max, and current clock frequencies"
temperature_help = "Current temperatures"
ecc_help = "Number of ECC errors"
pcie_help = "Current PCIe speed and width"
voltage_help = "Current GPU voltages"
# Help text for Arguments only on Linux Baremetal platforms
fan_help = "Current fan speed"
pcie_usage_help = "Estimated PCIe link usage"
vc_help = "Display voltage curve"
overdrive_help = "Current GPU clock overdrive level"
mo_help = "Current memory clock overdrive level"
perf_level_help = "Current DPM performance level"
replay_count_help = "PCIe replay count"
xgmi_err_help = "XGMI error information since last read"
energy_help = "Amount of energy consumed"
mem_usage_help = "Memory usage per block"
# Help text for Arguments only on Hypervisors
schedule_help = "All scheduling information"
guard_help = "All guard information"
guest_help = "All guest data information"
# Create metric subparser
metric_parser = subparsers.add_parser('metric', help=metric_help, description=metric_subcommand_help)
metric_parser._optionals.title = metric_optionals_title
metric_parser.formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=80, width=90)
metric_parser.set_defaults(func=func)
self._add_command_modifiers(metric_parser)
# Add Device args
self._add_device_arguments(metric_parser, required=False)
# Optional Args
metric_parser.add_argument('-u', '--usage', action='store_true', required=False, help=usage_help)
metric_parser.add_argument('-w', '--watch', action='store', metavar='Interval',
type=self._positive_int, required=False, help=watch_help)
metric_parser.add_argument('-W', '--watch_time', action=self._check_watch_selected(), metavar='Duration',
type=self._positive_int, required=False, help=watch_time_help)
metric_parser.add_argument('-i', '--iterations', action=self._check_watch_selected(), metavar='Iterations',
type=self._positive_int, required=False, help=iterations_help)
# Optional Args for Virtual OS and Baremetal systems
if self.amd_smi_helpers.is_virtual_os() or self.amd_smi_helpers.is_baremetal():
metric_parser.add_argument('-b', '--fb-usage', action='store_true', required=False, help=fb_usage_help)
# Optional Args for Hypervisors and Baremetal systems
if self.amd_smi_helpers.is_hypervisor() or self.amd_smi_helpers.is_baremetal():
metric_parser.add_argument('-p', '--power', action='store_true', required=False, help=power_help)
metric_parser.add_argument('-c', '--clock', action='store_true', required=False, help=clock_help)
metric_parser.add_argument('-t', '--temperature', action='store_true', required=False, help=temperature_help)
metric_parser.add_argument('-e', '--ecc', action='store_true', required=False, help=ecc_help)
metric_parser.add_argument('-P', '--pcie', action='store_true', required=False, help=pcie_help)
metric_parser.add_argument('-V', '--voltage', action='store_true', required=False, help=voltage_help)
# Optional Args for Linux Baremetal Systems
if self.amd_smi_helpers.is_baremetal() and self.amd_smi_helpers.is_linux():
metric_parser.add_argument('-f', '--fan', action='store_true', required=False, help=fan_help)
metric_parser.add_argument('-s', '--pcie-usage', action='store_true', required=False, help=pcie_usage_help)
metric_parser.add_argument('-C', '--voltage-curve', action='store_true', required=False, help=vc_help)
metric_parser.add_argument('-o', '--overdrive', action='store_true', required=False, help=overdrive_help)
metric_parser.add_argument('-M', '--mem-overdrive', action='store_true', required=False, help=mo_help)
metric_parser.add_argument('-l', '--perf-level', action='store_true', required=False, help=perf_level_help)
metric_parser.add_argument('-r', '--replay-count', action='store_true', required=False, help=replay_count_help)
metric_parser.add_argument('-x', '--xgmi-err', action='store_true', required=False, help=xgmi_err_help)
metric_parser.add_argument('-E', '--energy', action='store_true', required=False, help=energy_help)
metric_parser.add_argument('-m', '--mem-usage', action='store_true', required=False, help=mem_usage_help)
# Options to only display to Hypervisors
if self.amd_smi_helpers.is_hypervisor():
metric_parser.add_argument('-s', '--schedule', action='store_true', required=False, help=schedule_help)
metric_parser.add_argument('-G', '--guard', action='store_true', required=False, help=guard_help)
metric_parser.add_argument('-u', '--guest', action='store_true', required=False, help=guest_help)
def _add_process_parser(self, subparsers, func):
if self.amd_smi_helpers.is_hypervisor():
# Don't add this subparser on Hypervisors
# This subparser is only available to Guest and Baremetal systems
return
# Subparser help text
process_help = "Lists general process information running on the specified GPU"
process_subcommand_help = "If no argument is provided, returns information for all GPUs on the system.\
\nIf no argument is provided all process information will be displayed."
process_optionals_title = "Process arguments"
# Optional Arguments help text
general_help = "pid, process name, memory usage"
engine_help = "All engine usages"
pid_help = "Gets all process information about the specified process based on Process ID"
name_help = "Gets all process information about the specified process based on Process Name.\
\nIf multiple processes have the same name information is returned for all of them."
watch_help = "Reprint the command in a loop of Interval seconds"
watch_time_help = "The total time to watch the given command"
iterations_help = "Total number of iterations to loop on the given command"
# Create process subparser
process_parser = subparsers.add_parser('process', help=process_help, description=process_subcommand_help)
process_parser._optionals.title = process_optionals_title
process_parser.formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=80, width=90)
process_parser.set_defaults(func=func)
self._add_command_modifiers(process_parser)
# Add Device args
self._add_device_arguments(process_parser, required=False)
# Optional Args
process_parser.add_argument('-G', '--general', action='store_true', required=False, help=general_help)
process_parser.add_argument('-e', '--engine', action='store_true', required=False, help=engine_help)
process_parser.add_argument('-p', '--pid', action='store', type=self._positive_int, required=False, help=pid_help)
process_parser.add_argument('-n', '--name', action='store', required=False, help=name_help)
process_parser.add_argument('-w', '--watch', action='store', metavar='Interval',
type=self._positive_int, required=False, help=watch_help)
process_parser.add_argument('-W', '--watch_time', action=self._check_watch_selected(), metavar='Duration',
type=self._positive_int, required=False, help=watch_time_help)
process_parser.add_argument('-i', '--iterations', action=self._check_watch_selected(), metavar='Iterations',
type=self._positive_int, required=False, help=iterations_help)
def _add_profile_parser(self, subparsers, func):
if not (self.amd_smi_helpers.is_windows() and self.amd_smi_helpers.is_hypervisor()):
# This subparser only applies to Hypervisors
return
# Subparser help text
profile_help = "Displays information about all profiles and current profile"
profile_subcommand_help = "If no argument is provided, returns information for all GPUs on the system."
profile_optionals_title = "Profile Arguments"
# Create profile subparser
profile_parser = subparsers.add_parser('profile', help=profile_help, description=profile_subcommand_help)
profile_parser._optionals.title = profile_optionals_title
profile_parser.formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=80, width=90)
profile_parser.set_defaults(func=func)
self._add_command_modifiers(profile_parser)
# Add Device args
self._add_device_arguments(profile_parser, required=False)
def _add_event_parser(self, subparsers, func):
if self.amd_smi_helpers.is_linux() and not self.amd_smi_helpers.is_virtual_os():
# This subparser only applies to Linux BareMetal & Linux Hypervisors
return
# Subparser help text
event_help = "Displays event information for the given GPU"
event_subcommand_help = "If no argument is provided, returns event information for all GPUs on the system."
event_optionals_title = "Event Arguments"
# Create event subparser
event_parser = subparsers.add_parser('event', help=event_help, description=event_subcommand_help)
event_parser._optionals.title = event_optionals_title
event_parser.formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=80, width=90)
event_parser.set_defaults(func=func)
self._add_command_modifiers(event_parser)
# Add Device args
self._add_device_arguments(event_parser, required=False)
def _add_topology_parser(self, subparsers, func):
if not(self.amd_smi_helpers.is_baremetal() and self.amd_smi_helpers.is_linux()):
# This subparser is only applicable to Baremetal Linux
return
# Subparser help text
topology_help = "Displays topology information of the devices."
topology_subcommand_help = "If no argument is provided, returns information for all GPUs on the system."
topology_optionals_title = "Topology arguments"
# Help text for Arguments only on Guest and BM platforms
topo_access_help = "Displays link accessibility between GPUs"
topo_weight_help = "Displays relative weight between GPUs"
topo_hops_help = "Displays the number of hops between GPUs"
topo_type_help = "Displays the link type between GPUs."
topo_numa_help = "Displays the numa nodes."
# Create topology subparser
topology_parser = subparsers.add_parser('topology', help=topology_help, description=topology_subcommand_help)
topology_parser._optionals.title = topology_optionals_title
topology_parser.formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=80, width=90)
topology_parser.set_defaults(func=func)
self._add_command_modifiers(topology_parser)
# Add Device args
self._add_device_arguments(topology_parser, required=False)
# Optional Args
topology_parser.add_argument('-a', '--topo-access', action='store_true', required=False, help=topo_access_help)
topology_parser.add_argument('-w', '--topo-weight', action='store_true', required=False, help=topo_weight_help)
topology_parser.add_argument('-o', '--topo-hops', action='store_true', required=False, help=topo_hops_help)
topology_parser.add_argument('-t', '--topo-type', action='store_true', required=False, help=topo_type_help)
topology_parser.add_argument('-n', '--topo-numa', action='store_true', required=False, help=topo_numa_help)
def _add_set_value_parser(self, subparsers, func):
if not(self.amd_smi_helpers.is_baremetal() and self.amd_smi_helpers.is_linux()):
# This subparser is only applicable to Baremetal Linux
return
# Subparser help text
set_value_help = "Set options for devices."
set_value_subcommand_help = "The user must specify one of the options for the set configuration."
set_value_optionals_title = "Set Arguments"
# Help text for Arguments only on Guest and BM platforms
set_clk_help = "Sets clock frequency levels for specified clocks"
set_sclk_help = "Sets GPU clock frequency levels"
set_mclk_help = "Sets memory clock frequency levels"
set_pcie_help = "Sets PCIe Bandwith "
set_slevel_help = "Change GPU clock frequency and voltage for a specific level"
set_mlevel_help = "Change GPU memory frequency and voltage for a specific level"
set_vc_help = "Change SCLK voltage curve for a specified point"
set_srange_help = "Sets min and max SCLK speed"
set_mrange_help = "Sets min and max MCLK speed"
set_fan_help = "Sets GPU fan speed (level or %)"
set_perf_level_help = "Sets performance level"
set_overdrive_help = "Set GPU overdrive level"
set_mem_overdrive_help = "Set memory overclock overdrive level"
set_power_overdrive_help = "Set the maximum GPU power using power overdrive in Watts"
set_profile_help = "Set power profile level (#) or a quoted string of custom profile attributes"
set_perf_det_help = "Set GPU clock frequency limit to get minimal performance variation"
ras_enable_help = "Enable RAS for specified block and error type"
ras_disable_help = "Disable RAS for specified block and error type."
# Create set_value subparser
set_value_parser = subparsers.add_parser('set', help=set_value_help, description=set_value_subcommand_help)
set_value_parser._optionals.title = set_value_optionals_title
set_value_parser.formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=80, width=90)
set_value_parser.set_defaults(func=func)
self._add_command_modifiers(set_value_parser)
# Device args are required as safeguard from the user applying the operation to all gpus unintentionally
self._add_device_arguments(set_value_parser, required=True)
# Optional Args
set_value_parser.add_argument('-c', '--clk', action='store', required=False, help=set_clk_help)
set_value_parser.add_argument('-s', '--sclk', action='store', required=False, help=set_sclk_help)
set_value_parser.add_argument('-m', '--mclk', action='store', required=False, help=set_mclk_help)
set_value_parser.add_argument('-p', '--pcie', action='store', required=False, help=set_pcie_help)
set_value_parser.add_argument('-S', '--slevel', action='store', required=False, help=set_slevel_help)
set_value_parser.add_argument('-M', '--mlevel', action='store', required=False, help=set_mlevel_help)
set_value_parser.add_argument('-V', '--vc', action='store', required=False, help=set_vc_help)
set_value_parser.add_argument('-r', '--srange', action='store', required=False, help=set_srange_help)
set_value_parser.add_argument('-R', '--mrange', action='store', required=False, help=set_mrange_help)
set_value_parser.add_argument('-f', '--fan', action='store', required=False, help=set_fan_help)
set_value_parser.add_argument('-l', '--perflevel', action='store', required=False, help=set_perf_level_help)
set_value_parser.add_argument('-o', '--overdrive', action='store', required=False, help=set_overdrive_help)
set_value_parser.add_argument('-O', '--memoverdrive', action='store', required=False, help=set_mem_overdrive_help)
set_value_parser.add_argument('-w', '--poweroverdrive', action='store', required=False, help=set_power_overdrive_help)
set_value_parser.add_argument('-P', '--profile', action='store', required=False, help=set_profile_help)
set_value_parser.add_argument('-d', '--perfdet', action='store', required=False, help=set_perf_det_help)
set_value_parser.add_argument('-e', '--rasenable', action='store', required=False, help=ras_enable_help)
set_value_parser.add_argument('-D', '--rasdisable', action='store', required=False, help=ras_disable_help)
def _add_reset_parser(self, subparsers, func):
if not(self.amd_smi_helpers.is_baremetal() and self.amd_smi_helpers.is_linux()):
# This subparser is only applicable to Baremetal Linux
return
# Subparser help text
reset_help = "Reset options for devices."
reset_subcommand_help = "The user must specify one of the options to reset devices."
reset_optionals_title = "Reset Arguments"
# Help text for Arguments only on Guest and BM platforms
gpureset_help = "Reset the specified GPU"
resetclocks_help = "Reset clocks and overdrive to default"
resetfans_help = "Reset fans to automatic (driver) control"
resetprofile_help = "Reset power profile back to default"
resetpoweroverdrive_help = "Set the maximum GPU power back to the device default state"
resetxgmierr_help = "Reset XGMI error counts"
resetperfdet_help = "Disable performance determinism"
# Create reset subparser
reset_parser = subparsers.add_parser('reset', help=reset_help, description=reset_subcommand_help)
reset_parser._optionals.title = reset_optionals_title
reset_parser.formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=80, width=90)
reset_parser.set_defaults(func=func)
self._add_command_modifiers(reset_parser)
# Device args are required as safeguard from the user applying the operation to all gpus unintentionally
self._add_device_arguments(reset_parser, required=True)
# Optional Args
reset_parser.add_argument('-G', '--gpureset', action='store_true', required=False, help=gpureset_help)
reset_parser.add_argument('-c', '--resetclocks', action='store_true', required=False, help=resetclocks_help)
reset_parser.add_argument('-f', '--resetfans', action='store_true', required=False, help=resetfans_help)
reset_parser.add_argument('-p', '--resetprofile', action='store_true', required=False, help=resetprofile_help)
reset_parser.add_argument('-o', '--resetpoweroverdrive', action='store_true', required=False, help=resetpoweroverdrive_help)
reset_parser.add_argument('-x', '--resetxgmierr', action='store_true', required=False, help=resetxgmierr_help)
reset_parser.add_argument('-d', '--resetperfdet', action='store_true', required=False, help=resetperfdet_help)
def _add_rocm_smi_parser(self, subparsers, func):
# Subparser help text
rocm_smi_help = "Legacy rocm_smi commands ported for backward compatibility"
rocm_smi_subcommand_help = "If no argument is provided, return showall and print the information for all\
GPUs on the system."
rocm_smi_optionals_title = "rocm_smi Arguments"
# Optional arguments help text
load_help = "Load clock, fan, performance, and profile settings from a given file."
save_help = "Save clock, fan, performance, and profile settings to a given file."
showpidgpus_help = "Display's all the pids in a table sorted by gpu's"
showtopo_help = "Show combinded table to individual topo info"
showallinfo_help = "Show Temperature, Fan and Clock values"
showcompactview_help = "Show main points of interest"
showuse_help = "Show gpu usage"
showmemuse_help = "Show usage of gpu and memory"
# Create rocm_smi subparser
rocm_smi_parser = subparsers.add_parser('rocm-smi', help=rocm_smi_help, description=rocm_smi_subcommand_help, aliases=['rocm_smi'])
rocm_smi_parser._optionals.title = rocm_smi_optionals_title
rocm_smi_parser.formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=80, width=90)
rocm_smi_parser.set_defaults(func=func)
self._add_command_modifiers(rocm_smi_parser)
# Add Device args
self._add_device_arguments(rocm_smi_parser, required=False)
# Optional Args
rocm_smi_parser.add_argument('-l', '--load', action=self._check_input_file_path(), type=str, required=False, help=load_help)
rocm_smi_parser.add_argument('-s', '--save', action=self._check_output_file_path(), type=str, required=False, help=save_help)
rocm_smi_parser.add_argument('-T', '--showtempgraph', action='store_true', required=False, help=showpidgpus_help)
rocm_smi_parser.add_argument('-P', '--showprofile', action='store_true', required=False, help=showpidgpus_help)
rocm_smi_parser.add_argument('-M', '--showmaxpower', action='store_true', required=False, help=showpidgpus_help)
rocm_smi_parser.add_argument('-p', '--showpidgpus', action='store_true', required=False, help=showpidgpus_help)
rocm_smi_parser.add_argument('-t', '--showtopo', action='store_true', required=False, help=showtopo_help)
rocm_smi_parser.add_argument('-a', '--showallinfo', action='store_true', required=False, help=showallinfo_help)
rocm_smi_parser.add_argument('-c', '--showcompactview', action='store_true', required=False, help=showcompactview_help)
rocm_smi_parser.add_argument('-u', '--showuse', action='store_true', required=False, help=showuse_help)
rocm_smi_parser.add_argument('-m', '--showmemuse', action='store_true', required=False, help=showmemuse_help)
+33 -65
Datei anzeigen
@@ -5,17 +5,18 @@
set(clang_ver 14.0)
set(ctypeslib_ver 2.3.2)
set(PY_BUILD_DIR "python_package" CACHE STRING "")
set(PY_BUILD_DIR "python_package")
# amdsmi part of this string is the directory containing all python files
# additionally defined in pyproject.toml
set(PY_PACKAGE_DIR "${PY_BUILD_DIR}/amdsmi" CACHE STRING "")
set(PY_PACKAGE_DIR "${PY_BUILD_DIR}/amdsmi")
set(PY_WRAPPER_INSTALL_DIR "${SHARE_INSTALL_PREFIX}" CACHE STRING "Wrapper installation directory")
# if Python3 is found but the version is below 3.7 - Python3_FOUND is set to FALSE
find_package(Python3 3.7 COMPONENTS Interpreter)
find_package(Python3 3.7 COMPONENTS Interpreter Development)
# WARN: This is a HACK to pass compile on AMD rhel8 CI systems!
# WARN: This is a HACK to pass compile on AMD rhel8 and centos7 CI systems!
# Those still use python3.6 which is too old for this project!
# TODO: drop python3.6 support
# TODO: Promote to an error when python3.7 or above is installed on CI
if(NOT Python3_FOUND)
message(AUTHOR_WARNING "Python3 DOESN'T EXIST OR VERSION IS TOO OLD!: ${Python3_VERSION}")
message(AUTHOR_WARNING "The wrapper will not be created and the project will not be packaged!")
@@ -23,57 +24,28 @@ if(NOT Python3_FOUND)
return()
endif()
# check if virtualenv is installed
execute_process(COMMAND "${Python3_EXECUTABLE}" -m pip show virtualenv
ERROR_VARIABLE VIRTUALENV_NOT_FOUND)
if(VIRTUALENV_NOT_FOUND)
message(FATAL_ERROR "Python virtualenv is not installed!
${VIRTUALENV_NOT_FOUND}
Please run:
${Python3_EXECUTABLE} -m pip install virtualenv")
endif()
# set up the Python environment
execute_process(COMMAND "${Python3_EXECUTABLE}" -m venv "${CMAKE_CURRENT_BINARY_DIR}/venv")
# venv trick borrowed from:
# https://discourse.cmake.org/t/possible-to-create-a-python-virtual-env-from-cmake-and-then-find-it-with-findpython3/1132
# update the environment with VIRTUAL_ENV variable(mimic the activate script)
set(ENV{VIRTUAL_ENV} "${CMAKE_CURRENT_BINARY_DIR}/venv")
# change the context of the search
set(Python3_FIND_VIRTUALENV FIRST)
# unset Python3_EXECUTABLE because it is also an input variable(see documentation, Artifacts Specification section)
unset(Python3_EXECUTABLE)
# launch a new search
find_package(Python3 3.7 COMPONENTS Interpreter Development REQUIRED)
add_custom_target(
python_pre_reqs
COMMAND ${Python3_EXECUTABLE} -m pip install clang==${clang_ver} ctypeslib2==${ctypeslib_ver})
# TODO: Figure out how python-clang and clang are related
# Currently only a very specific combination works
# try to find clang of the right version
set(GOOD_CLANG_FOUND FALSE)
find_program(clang NAMES clang)
if(clang STREQUAL "clang-NOTFOUND")
message(AUTHOR_WARNING "NO CLANG FOUND!")
else()
# only try to re-generate amdsmi_wrapper.py if BUILD_WRAPPER is on/true
if(BUILD_WRAPPER)
find_program(clang NAMES clang REQUIRED)
# extract clang version manually because find_package(clang) doesn't work
execute_process(COMMAND ${clang} --version OUTPUT_VARIABLE clang_full_version_string)
string (REGEX REPLACE ".*clang version ([0-9]+\\.[0-9]+).*" "\\1" CLANG_VERSION_STRING ${clang_full_version_string})
if(CLANG_VERSION_STRING VERSION_EQUAL 14.0)
if(CLANG_VERSION_STRING VERSION_EQUAL clang_ver)
message("GOOD CLANG VERSION: ${CLANG_VERSION_STRING}")
set(GOOD_CLANG_FOUND TRUE)
else()
message(AUTHOR_WARNING "CLANG VERSION TOO OLD!: ${CLANG_VERSION_STRING}")
message(FATAL_ERROR "${clang} VERSION TOO OLD!: ${CLANG_VERSION_STRING}")
endif()
endif()
if(NOT GOOD_CLANG_FOUND)
# keep old wrapper because no clang found
message(AUTHOR_WARNING "A wrapper will not be generated! Using old wrapper instead.")
message(AUTHOR_WARNING "A wrapper will not be re-generated! Using old wrapper instead.\nSet -DBUILD_WRAPPER=ON to re-build the wrapper")
add_custom_command(
OUTPUT amdsmi_wrapper.py
${PY_PACKAGE_DIR}/amdsmi_wrapper.py
@@ -85,8 +57,11 @@ if(NOT GOOD_CLANG_FOUND)
s:"@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@":"${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}":g
${CMAKE_CURRENT_BINARY_DIR}/amdsmi_wrapper.py
COMMAND mkdir -p ${PY_PACKAGE_DIR}
COMMAND ln -sf ${CMAKE_CURRENT_BINARY_DIR}/amdsmi_wrapper.py ${PY_PACKAGE_DIR}/)
COMMAND ln -Pf ${CMAKE_CURRENT_BINARY_DIR}/amdsmi_wrapper.py ${PY_PACKAGE_DIR}/)
else()
add_custom_target(
python_pre_reqs
COMMAND ${Python3_EXECUTABLE} -m pip install clang==${clang_ver} ctypeslib2==${ctypeslib_ver})
# generate new wrapper
configure_file(${PROJECT_SOURCE_DIR}/tools/generator.py generator.py @ONLY COPYONLY)
add_custom_command(
@@ -106,14 +81,14 @@ else()
s:"@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@":"${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}":g
${CMAKE_CURRENT_BINARY_DIR}/amdsmi_wrapper.py
COMMAND mkdir -p ${PY_PACKAGE_DIR}
COMMAND ln -sf ${CMAKE_CURRENT_BINARY_DIR}/amdsmi_wrapper.py ${PY_PACKAGE_DIR}/)
COMMAND ln -Pf ${CMAKE_CURRENT_BINARY_DIR}/amdsmi_wrapper.py ${PY_PACKAGE_DIR}/)
endif()
add_custom_target(
python_wrapper
DEPENDS amdsmi_wrapper.py)
# symlinking instead of copying avoids unnecessarry regeneration of packaged files
# hard-linking instead of copying avoids unnecessarry regeneration of packaged files
add_custom_command(
OUTPUT ${PY_BUILD_DIR}/pyproject.toml
${PY_PACKAGE_DIR}/__init__.py
@@ -122,33 +97,26 @@ add_custom_command(
${PY_PACKAGE_DIR}/README.md
${PY_PACKAGE_DIR}/LICENSE
DEPENDS python_wrapper
COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/pyproject.toml ${PY_BUILD_DIR}/
COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py ${PY_PACKAGE_DIR}/
COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_exception.py ${PY_PACKAGE_DIR}/
COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_interface.py ${PY_PACKAGE_DIR}/
COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${PY_PACKAGE_DIR}/
COMMAND ln -sf ${PROJECT_SOURCE_DIR}/LICENSE ${PY_PACKAGE_DIR}/)
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/pyproject.toml ${PY_BUILD_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_exception.py ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_interface.py ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${PY_PACKAGE_DIR}/
COMMAND ln -Pf ${PROJECT_SOURCE_DIR}/LICENSE ${PY_PACKAGE_DIR}/)
# NOTE: changing this does not change the generated file!
# it WILL break
set(PY_PACKAGE "${PY_BUILD_DIR}/amdsmi-0.1-py3-none-any.whl")
add_custom_command(
OUTPUT ${PY_PACKAGE}
add_custom_target(
python_package ALL
DEPENDS ${PY_BUILD_DIR}/pyproject.toml
${PY_PACKAGE_DIR}/__init__.py
${PY_PACKAGE_DIR}/amdsmi_exception.py
${PY_PACKAGE_DIR}/amdsmi_interface.py
${PY_PACKAGE_DIR}/README.md
${PY_PACKAGE_DIR}/LICENSE
COMMAND ${Python3_EXECUTABLE} -m pip install wheel
COMMAND ${Python3_EXECUTABLE} -m pip wheel ./${PY_BUILD_DIR} --wheel-dir=${PY_BUILD_DIR})
add_custom_target(
python_package ALL
DEPENDS ${PY_PACKAGE})
${PY_PACKAGE_DIR}/LICENSE)
install(
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${PY_PACKAGE}
DESTINATION ${SHARE_INSTALL_PREFIX})
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PY_BUILD_DIR}/pyproject.toml
DESTINATION ${PY_WRAPPER_INSTALL_DIR})
install(
DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${PY_PACKAGE_DIR}
DESTINATION ${PY_WRAPPER_INSTALL_DIR})
+2 -3
Datei anzeigen
@@ -1,5 +1,5 @@
#
# Copyright (C) 2022 Advanced Micro Devices. All rights reserved.
# Copyright (C) 2023 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
@@ -177,7 +177,6 @@ from .amdsmi_interface import amdsmi_is_P2P_accessible
from .amdsmi_interface import amdsmi_get_xgmi_info
# # Enums
from .amdsmi_interface import AmdSmiInitFlags
from .amdsmi_interface import AmdSmiContainerTypes
from .amdsmi_interface import AmdSmiDeviceType
@@ -205,8 +204,8 @@ from .amdsmi_interface import AmdSmiIoLinkType
from .amdsmi_interface import AmdSmiUtilizationCounterType
from .amdsmi_interface import AmdSmiSwComponent
from .amdsmi_interface import AmdSmiIoLinkType
# Exceptions
# Exceptions
from .amdsmi_exception import AmdSmiLibraryException
from .amdsmi_exception import AmdSmiRetryException
from .amdsmi_exception import AmdSmiParameterException
+1
Datei anzeigen
@@ -0,0 +1 @@
__version__ = "0.0.1"
+4 -2
Datei anzeigen
@@ -1,5 +1,5 @@
#
# Copyright (C) 2022 Advanced Micro Devices. All rights reserved.
# Copyright (C) 2023 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
@@ -22,7 +22,6 @@
from enum import IntEnum
from . import amdsmi_wrapper
class AmdSmiRetCode(IntEnum):
SUCCESS = amdsmi_wrapper.AMDSMI_STATUS_SUCCESS
ERR_INVAL = amdsmi_wrapper.AMDSMI_STATUS_INVAL
@@ -72,6 +71,9 @@ class AmdSmiLibraryException(AmdSmiException):
err_code=self.err_code, err_info=self.err_info
)
def get_error_info(self):
return self.err_info
def get_error_code(self):
return self.err_code
+10 -59
Datei anzeigen
@@ -1,5 +1,5 @@
#
# Copyright (C) 2022 Advanced Micro Devices. All rights reserved.
# Copyright (C) 2023 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
@@ -28,7 +28,6 @@ from collections.abc import Iterable
from . import amdsmi_wrapper
from .amdsmi_exception import *
class AmdSmiInitFlags(IntEnum):
ALL_DEVICES = amdsmi_wrapper.AMDSMI_INIT_ALL_DEVICES
AMD_CPUS = amdsmi_wrapper.AMDSMI_INIT_AMD_CPUS
@@ -292,18 +291,6 @@ class AmdSmiUtilizationCounterType(IntEnum):
COARSE_GRAIN_MEM_ACTIVITY = amdsmi_wrapper.AMDSMI_COARSE_GRAIN_MEM_ACTIVITY
class AmdSmiSwComponent(IntEnum):
DRIVER = amdsmi_wrapper.AMDSMI_SW_COMP_DRIVER
class AmdSmiIoLinkType(IntEnum):
UNDEFINED = amdsmi_wrapper.AMDSMI_IOLINK_TYPE_UNDEFINED
PCIEXPRESS = amdsmi_wrapper.AMDSMI_IOLINK_TYPE_PCIEXPRESS
XGMI = amdsmi_wrapper.AMDSMI_IOLINK_TYPE_XGMI
NUMIOLINKTYPES = amdsmi_wrapper.AMDSMI_IOLINK_TYPE_NUMIOLINKTYPES
SIZE = amdsmi_wrapper.AMDSMI_IOLINK_TYPE_SIZE
class AmdSmiEventReader:
def __init__(
self, device_handle: amdsmi_wrapper.amdsmi_device_handle, *event_types
@@ -498,8 +485,7 @@ def amdsmi_get_socket_handles() -> List[amdsmi_wrapper.amdsmi_socket_handle]:
socket_count.value)()
_check_res(
amdsmi_wrapper.amdsmi_get_socket_handles(
ctypes.byref(socket_count), socket_handles
)
ctypes.byref(socket_count), socket_handles)
)
sockets = [
amdsmi_wrapper.amdsmi_socket_handle(socket_handles[sock_idx])
@@ -513,11 +499,14 @@ def amdsmi_get_socket_info(socket_handle):
if not isinstance(socket_handle, amdsmi_wrapper.amdsmi_socket_handle):
raise AmdSmiParameterException(
socket_handle, amdsmi_wrapper.amdsmi_socket_handle)
socket_info = ctypes.create_string_buffer(128)
return {
"name": ""
}
_check_res(
amdsmi_wrapper.amdsmi_get_socket_info(
socket_handle, ctypes.byref(socket_info), ctypes.c_size_t(128))
)
return socket_info.value.decode()
def amdsmi_get_device_handles() -> List[amdsmi_wrapper.amdsmi_device_handle]:
socket_handles = amdsmi_get_socket_handles()
@@ -677,10 +666,10 @@ def amdsmi_get_vbios_info(
return {
"name": vbios_info.name.decode("utf-8"),
"vbios_version": vbios_info.vbios_version,
"vbios_version_string": vbios_info.vbios_version_string.decode("utf-8"),
"build_date": vbios_info.build_date.decode("utf-8"),
"part_number": vbios_info.part_number.decode("utf-8"),
"vbios_version_string": vbios_info.vbios_version_string.decode("utf-8"),
"vbios_version": vbios_info.vbios_version,
}
@@ -2261,20 +2250,6 @@ def amdsmi_dev_get_perf_level(
return result
def amdsmi_set_perf_determinism_mode(
device_handle: amdsmi_wrapper.amdsmi_device_handle, clkvalue: int
) -> None:
if not isinstance(device_handle, amdsmi_wrapper.amdsmi_device_handle):
raise AmdSmiParameterException(
device_handle, amdsmi_wrapper.amdsmi_device_handle
)
if not isinstance(clkvalue, int):
raise AmdSmiParameterException(clkvalue, int)
_check_res(amdsmi_wrapper.amdsmi_set_perf_determinism_mode(
device_handle, clkvalue))
def amdsmi_dev_get_overdrive_level(
device_handle: amdsmi_wrapper.amdsmi_device_handle,
) -> int:
@@ -2446,30 +2421,6 @@ def amdsmi_dev_get_od_volt_curve_regions(
return result
def amdsmi_dev_get_power_profile_presets(
device_handle: amdsmi_wrapper.amdsmi_device_handle, sensor_idx: int
) -> Dict[str, Any]:
if not isinstance(device_handle, amdsmi_wrapper.amdsmi_device_handle):
raise AmdSmiParameterException(
device_handle, amdsmi_wrapper.amdsmi_device_handle
)
if not isinstance(sensor_idx, int):
raise AmdSmiParameterException(sensor_idx, int)
status = amdsmi_wrapper.amdsmi_power_profile_status_t()
_check_res(
amdsmi_wrapper. amdsmi_dev_get_power_profile_presets(
device_handle, sensor_idx, ctypes.byref(status)
)
)
return {
"available_profiles": status.available_profiles,
"current": status.current,
"num_profiles": status.num_profiles,
}
def amdsmi_dev_get_ecc_count(
device_handle: amdsmi_wrapper.amdsmi_device_handle, block: AmdSmiGpuBlock
) -> Dict[str, int]:
+15 -8
Datei anzeigen
@@ -1,6 +1,6 @@
#
# Copyright (C) 2022 Advanced Micro Devices. All rights reserved.
# Copyright (C) 2023 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
@@ -167,17 +167,24 @@ def char_pointer_cast(string, encoding='utf-8'):
_libraries = {}
from pathlib import Path
libamd_smi_cpack = Path("@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libamd_smi.so")
libamd_smi_optrocm = Path("/opt/rocm/lib/libamd_smi.so")
libamd_smi_parent_dir = Path(__file__).resolve().parent / "libamd_smi.so"
libamd_smi_cwd = Path.cwd()
if os.path.isfile('@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libamd_smi.so'):
if libamd_smi_cpack.is_file():
# try to find library in install directory provided by CMake
_libraries['libamd_smi.so'] = ctypes.CDLL(os.path.join('@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@', 'libamd_smi.so'))
elif os.path.isfile('/opt/rocm/lib/libamd_smi.so'):
_libraries['libamd_smi.so'] = ctypes.CDLL(libamd_smi_cpack)
elif libamd_smi_optrocm.is_file():
# try /opt/rocm/lib as a fallback
_libraries['libamd_smi.so'] = ctypes.CDLL(os.path.join('/opt/rocm/lib', 'libamd_smi.so'))
_libraries['libamd_smi.so'] = ctypes.CDLL(libamd_smi_optrocm)
elif libamd_smi_parent_dir.is_file():
# try to fall back to parent directory
_libraries['libamd_smi.so'] = ctypes.CDLL(libamd_smi_parent_dir)
else:
# lastly - search in current directory
_libraries['libamd_smi.so'] = ctypes.CDLL(os.path.join(os.path.dirname(__file__), 'libamd_smi.so'))
# lastly - search in current working directory
_libraries['libamd_smi.so'] = ctypes.CDLL(libamd_smi_cwd)
+3 -3
Datei anzeigen
@@ -159,14 +159,14 @@ class CmdLineParser:
def get_device_handle(self):
device_handles = []
def parse_device_handle(possition):
self._check_if_arg_exists("Device identifier", "device bdf or device id", possition)
def parse_device_handle(position):
self._check_if_arg_exists("Device identifier", "device bdf or device id", position)
ids = self._parse_vf_id_if_exists()
if ids["gpu_id"] is not None and ids["vf_id"] is not None:
device_handle = self._get_device_handle_from_id(ids["gpu_id"], ids["vf_id"])
else:
gpu_arg = self.cmd_args[possition]
gpu_arg = self.cmd_args[position]
if gpu_arg.isdigit():
device_handle = self._get_device_handle_from_id(int(gpu_arg))
else:
+137 -131
Datei anzeigen
@@ -1,131 +1,137 @@
#
# Copyright (C) 2022 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# 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
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# 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 os
import sys
import argparse
import tempfile
import shutil
import platform
from subprocess import run, PIPE
from ctypeslib.clang2py import main as clangToPy
HEADER = \
"""
#
# Copyright (C) 2022 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# 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
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# 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 os
"""
def parseArgument():
parser = argparse.ArgumentParser(description="parse input arguments")
parser.add_argument('-o','--output', type=str, required=True,
help='The output file name')
parser.add_argument('-i','--input', type=str, required=True,
help='The input file name')
parser.add_argument('-l', '--library', type=str, required=True,
help='Loading dynamic link libraries')
args = vars(parser.parse_args())
return args['output'], args['input'], args['library']
def replace_line(full_path_file_name, string_to_repalce, new_string):
fh, abs_path = tempfile.mkstemp()
with os.fdopen(fh, 'w') as new_file:
new_file.write(HEADER)
with open(full_path_file_name, 'r+') as old_file:
for line in old_file:
new_file.write(line.replace(string_to_repalce, new_string))
shutil.copymode(full_path_file_name, abs_path)
os.remove(full_path_file_name)
shutil.move(abs_path, full_path_file_name)
def main():
output_file, input_file, library = parseArgument()
library_name = os.path.basename(library)
clang_include_dir = \
run(["clang", "--print-resource-dir"], stdout=PIPE, stderr=PIPE, encoding="utf-8").stdout.strip()
os_platform = platform.system()
if os_platform == "Windows":
clang_include_dir += "\include"
if "Program Files(x86)" in clang_include_dir:
clang_include_dir = clang_include_dir.replace("Program Files(x86)", "Progra~2")
elif "Program Files" in clang_include_dir:
clang_include_dir = clang_include_dir.replace("Program Files", "Progra~1")
arguments = [input_file, "-o", output_file]
line_to_replace = "_libraries['FIXME_STUB'] = FunctionFactoryStub() # ctypes.CDLL('FIXME_STUB')"
new_line = "_libraries['FIXME_STUB'] = ctypes.CDLL('{}')".format(library_name)
elif os_platform == "Linux":
clang_include_dir += "/include"
arguments = [input_file, "-o", output_file, "-l", library]
library_path = os.path.join(os.path.dirname(__file__), library)
line_to_replace = "_libraries['{}'] = ctypes.CDLL('{}')".format(library_name, library_path)
new_line = """
if os.path.isfile('@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/{0}'):
# try to find library in install directory provided by CMake
_libraries['{0}'] = ctypes.CDLL(os.path.join('@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@', '{0}'))
elif os.path.isfile('/opt/rocm/lib/{0}'):
# try /opt/rocm/lib as a fallback
_libraries['{0}'] = ctypes.CDLL(os.path.join('/opt/rocm/lib', '{0}'))
else:
# lastly - search in current directory
_libraries['{0}'] = ctypes.CDLL(os.path.join(os.path.dirname(__file__), '{0}'))
""".format(library_name)
else:
print("Unknown operating system. It is only supporing Linux and Windows.")
return
arguments.append(f"--clang-args=-I" + clang_include_dir)
clangToPy(arguments)
replace_line(output_file, line_to_replace, new_line)
if __name__ == "__main__":
main()
#
# Copyright (C) 2023 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# 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
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# 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 os
import argparse
import tempfile
import shutil
import platform
from subprocess import run, PIPE
from ctypeslib.clang2py import main as clangToPy
HEADER = \
"""
#
# Copyright (C) 2023 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# 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
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# 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 os
"""
def parseArgument():
parser = argparse.ArgumentParser(description="parse input arguments")
parser.add_argument('-o','--output', type=str, required=True,
help='The output file name')
parser.add_argument('-i','--input', type=str, required=True,
help='The input file name')
parser.add_argument('-l', '--library', type=str, required=True,
help='Loading dynamic link libraries')
args = vars(parser.parse_args())
return args['output'], args['input'], args['library']
def replace_line(full_path_file_name, string_to_repalce, new_string):
fh, abs_path = tempfile.mkstemp()
with os.fdopen(fh, 'w') as new_file:
new_file.write(HEADER)
with open(full_path_file_name, 'r+', encoding='UTF-8') as old_file:
for line in old_file:
new_file.write(line.replace(string_to_repalce, new_string))
shutil.copymode(full_path_file_name, abs_path)
os.remove(full_path_file_name)
shutil.move(abs_path, full_path_file_name)
def main():
output_file, input_file, library = parseArgument()
library_name = os.path.basename(library)
clang_include_dir = \
run(["clang", "--print-resource-dir"], stdout=PIPE, stderr=PIPE, encoding="utf-8").stdout.strip()
os_platform = platform.system()
if os_platform == "Windows":
clang_include_dir += "\include"
if "Program Files(x86)" in clang_include_dir:
clang_include_dir = clang_include_dir.replace("Program Files(x86)", "Progra~2")
elif "Program Files" in clang_include_dir:
clang_include_dir = clang_include_dir.replace("Program Files", "Progra~1")
arguments = [input_file, "-o", output_file]
line_to_replace = "_libraries['FIXME_STUB'] = FunctionFactoryStub() # ctypes.CDLL('FIXME_STUB')"
new_line = "_libraries['FIXME_STUB'] = ctypes.CDLL('{}')".format(library_name)
elif os_platform == "Linux":
clang_include_dir += "/include"
arguments = [input_file, "-o", output_file, "-l", library]
library_path = os.path.join(os.path.dirname(__file__), library)
line_to_replace = "_libraries['{}'] = ctypes.CDLL('{}')".format(library_name, library_path)
new_line = f"""from pathlib import Path
libamd_smi_cpack = Path("@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/{library_name}")
libamd_smi_optrocm = Path("/opt/rocm/lib/{library_name}")
libamd_smi_parent_dir = Path(__file__).resolve().parent / "{library_name}"
libamd_smi_cwd = Path.cwd()
if libamd_smi_cpack.is_file():
# try to find library in install directory provided by CMake
_libraries['{library_name}'] = ctypes.CDLL(libamd_smi_cpack)
elif libamd_smi_optrocm.is_file():
# try /opt/rocm/lib as a fallback
_libraries['{library_name}'] = ctypes.CDLL(libamd_smi_optrocm)
elif libamd_smi_parent_dir.is_file():
# try to fall back to parent directory
_libraries['{library_name}'] = ctypes.CDLL(libamd_smi_parent_dir)
else:
# lastly - search in current working directory
_libraries['{library_name}'] = ctypes.CDLL(libamd_smi_cwd)"""
else:
print("Unknown operating system. It is only supporing Linux and Windows.")
return
arguments.append("--clang-args=-I" + clang_include_dir)
clangToPy(arguments)
replace_line(output_file, line_to_replace, new_line)
if __name__ == "__main__":
main()