diff --git a/projects/amdsmi/amdsmi_cli/amdsmi_cli.py b/projects/amdsmi/amdsmi_cli/amdsmi_cli.py index 83661c7dc3..4b233501ee 100755 --- a/projects/amdsmi/amdsmi_cli/amdsmi_cli.py +++ b/projects/amdsmi/amdsmi_cli/amdsmi_cli.py @@ -29,6 +29,7 @@ from amdsmi_logger import AMDSMILogger import amdsmi_cli_exceptions from amdsmi import amdsmi_interface + def _print_error(e, destination): if destination == 'stdout': print(e) @@ -40,7 +41,6 @@ def _print_error(e, destination): str(destination) + " file") - if __name__ == "__main__": # Set compatability mode based on which cli mapping user selects if 'gpuv-smi' in sys.argv[0]: diff --git a/projects/amdsmi/amdsmi_cli/amdsmi_commands.py b/projects/amdsmi/amdsmi_cli/amdsmi_commands.py index 9215c21213..2debab8041 100644 --- a/projects/amdsmi/amdsmi_cli/amdsmi_commands.py +++ b/projects/amdsmi/amdsmi_cli/amdsmi_commands.py @@ -395,8 +395,7 @@ class AMDSMICommands(): args.gpu = device_handle - values_dict = {} - + fw_list = {} if args.fw_list: try: fw_info = amdsmi_interface.amdsmi_get_fw_info(args.gpu) @@ -421,18 +420,32 @@ class AMDSMICommands(): if self.logger.is_gpuvsmi_compatibility(): fw_info['ucode_list'] = fw_info.pop('fw_list') - values_dict.update(fw_info) + fw_list.update(fw_info) except amdsmi_exception.AmdSmiLibraryException as e: raise e - # Store values in logger.output - self.logger.store_output(args.gpu, 'values', values_dict) + multiple_devices_csv_override = False + # Convert and store output by pid for csv format + if self.logger.is_csv_format(): + if self.logger.is_gpuvsmi_compatibility(): + fw_key = 'ucode_list' + else: + fw_key = 'fw_list' + + for fw_info_dict in fw_list[fw_key]: + for key, value in fw_info_dict.items(): + multiple_devices_csv_override = True + self.logger.store_output(args.gpu, key, value) + self.logger.store_multiple_device_output() + else: + # Store values in logger.output + self.logger.store_output(args.gpu, 'values', fw_list) if multiple_devices: self.logger.store_multiple_device_output() return # Skip printing when there are multiple devices - self.logger.print_output() + self.logger.print_output(multiple_device_output=multiple_devices_csv_override) def bad_pages(self, args, multiple_devices=False, gpu=None, retired=None, pending=None, un_res=None): @@ -817,7 +830,9 @@ class AMDSMICommands(): ecc_dict[state['block']] = {'correctable' : ecc_count['correctable_count'], 'uncorrectable': ecc_count['uncorrectable_count']} if ecc_dict == {}: - ecc_dict = 'No RAS Blocks Enabled' + ecc_dict['correctable'] = 'N/A' + ecc_dict['uncorrectable'] = 'N/A' + values_dict['ecc'] = ecc_dict except amdsmi_exception.AmdSmiLibraryException as e: values_dict['ecc'] = e.get_error_info() @@ -1159,21 +1174,30 @@ class AMDSMICommands(): process_names.append(process_info) filtered_process_values = process_names - # Remove brackets if there is only one value - if len(filtered_process_values) == 1: - filtered_process_values = filtered_process_values[0] - - # Store values in logger.output - if filtered_process_values == []: - self.logger.store_output(args.gpu, 'values', {'process_info': 'Not Found'}) + multiple_devices_csv_override = False + # Convert and store output by pid for csv format + if self.logger.is_csv_format(): + for process_info in filtered_process_values: + for key, value in process_info['process_info'].items(): + multiple_devices_csv_override = True + self.logger.store_output(args.gpu, key, value) + self.logger.store_multiple_device_output() else: - self.logger.store_output(args.gpu, 'values', filtered_process_values) + # Remove brackets if there is only one value + if len(filtered_process_values) == 1: + filtered_process_values = filtered_process_values[0] + + # Store values in logger.output + if filtered_process_values == []: + self.logger.store_output(args.gpu, 'values', {'process_info': 'Not Found'}) + else: + self.logger.store_output(args.gpu, 'values', filtered_process_values) if multiple_devices: self.logger.store_multiple_device_output() return # Skip printing when there are multiple devices - self.logger.print_output() + self.logger.print_output(multiple_device_output=multiple_devices_csv_override) if watching_output: # End of single gpu add to watch_output self.logger.store_watch_output(multiple_devices=False) @@ -1372,23 +1396,31 @@ class AMDSMICommands(): try: perf_level = amdsmi_interface.amdsmi_dev_get_perf_level(args.gpu) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to get performance level of {gpu_string}") from e if 'manual' in perf_level.lower(): try: amdsmi_interface.amdsmi_dev_set_perf_level_v1(args.gpu, amdsmi_interface.AmdSmiDevPerfLevel.MANUAL) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set the performance level of {gpu_string} to manual") from e if clock_type != amdsmi_interface.AmdSmiClkType.PCIE: try: amdsmi_interface.amdsmi_dev_set_clk_freq(args.gpu, clock_type, freq_bitmask) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set the {clock_type} clock frequency on {gpu_string}") from e else: try: amdsmi_interface.amdsmi_dev_set_pci_bandwidth(args.gpu, freq_bitmask) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set the {clock_type} clock frequency on {gpu_string}") from e self.logger.store_output(args.gpu, 'clock', f'Successfully set clock frequency bitmask for {clock_type}') @@ -1400,17 +1432,23 @@ class AMDSMICommands(): try: perf_level = amdsmi_interface.amdsmi_dev_get_perf_level(args.gpu) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to get performance level of {gpu_string}") from e if 'manual' in perf_level.lower(): try: amdsmi_interface.amdsmi_dev_set_perf_level_v1(args.gpu, amdsmi_interface.AmdSmiDevPerfLevel.MANUAL) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set the performance level of {gpu_string} to manual") from e try: amdsmi_interface.amdsmi_dev_set_clk_freq(args.gpu, clock_type, freq_bitmask) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set the {clock_type} clock frequency on {gpu_string}") from e self.logger.store_output(args.gpu, 'sclk', 'Successfully set clock frequency bitmask') @@ -1421,17 +1459,23 @@ class AMDSMICommands(): try: perf_level = amdsmi_interface.amdsmi_dev_get_perf_level(args.gpu) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to get performance level of {gpu_string}") from e if 'manual' in perf_level.lower(): try: amdsmi_interface.amdsmi_dev_set_perf_level_v1(args.gpu, amdsmi_interface.AmdSmiDevPerfLevel.MANUAL) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set the performance level of {gpu_string} to manual") from e try: amdsmi_interface.amdsmi_dev_set_clk_freq(args.gpu, clock_type, freq_bitmask) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set the {clock_type} clock frequency on {gpu_string}") from e self.logger.store_output(args.gpu, 'mclk', 'Successfully set clock frequency bitmask') @@ -1442,16 +1486,22 @@ class AMDSMICommands(): try: perf_level = amdsmi_interface.amdsmi_dev_get_perf_level(args.gpu) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to get performance level of {gpu_string}") from e if 'manual' in perf_level.lower(): try: amdsmi_interface.amdsmi_dev_set_perf_level_v1(args.gpu, amdsmi_interface.AmdSmiDevPerfLevel.MANUAL) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set the performance level of {gpu_string} to manual") from e try: amdsmi_interface.amdsmi_dev_set_pci_bandwidth(args.gpu, freq_bitmask) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set the {clock_type} clock frequency on {gpu_string}") from e self.logger.store_output(args.gpu, 'pcie', 'Successfully set clock frequency bitmask') @@ -1462,6 +1512,8 @@ class AMDSMICommands(): try: amdsmi_interface.amdsmi_dev_set_od_clk_info(args.gpu, level, value, clock_type) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to change the {clock_type} clock frequency in the PowerPlay table on {gpu_string}") from e self.logger.store_output(args.gpu, 'slevel', 'Successfully changed clock frequency') @@ -1472,6 +1524,8 @@ class AMDSMICommands(): try: amdsmi_interface.amdsmi_dev_set_od_clk_info(args.gpu, level, value, clock_type) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to change the {clock_type} clock frequency in the PowerPlay table on {gpu_string}") from e self.logger.store_output(args.gpu, 'mlevel', 'Successfully changed clock frequency') @@ -1480,6 +1534,8 @@ class AMDSMICommands(): try: amdsmi_interface.amdsmi_dev_set_od_volt_info(args.gpu, point, clk, volt) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set the Voltage Curve point {point} to {clk}(MHz) {volt}(mV) on {gpu_string}") from e self.logger.store_output(args.gpu, 'vc', f'Successfully set voltage point {point} to {clk}(MHz) {volt}(mV)') @@ -1489,6 +1545,8 @@ class AMDSMICommands(): try: amdsmi_interface.amdsmi_dev_set_clk_range(args.gpu, min_value, max_value, clock_type) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set {clock_type} from {min_value}(MHz) to {max_value}(MHz) on {gpu_string}") from e self.logger.store_output(args.gpu, 'srange', f"Successfully set {clock_type} from {min_value}(MHz) to {max_value}(MHz)") @@ -1498,6 +1556,8 @@ class AMDSMICommands(): try: amdsmi_interface.amdsmi_dev_set_clk_range(args.gpu, min_value, max_value, clock_type) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set {clock_type} from {min_value}(MHz) to {max_value}(MHz) on {gpu_string}") from e self.logger.store_output(args.gpu, 'mrange', f"Successfully set {clock_type} from {min_value}(MHz) to {max_value}(MHz)") @@ -1505,6 +1565,8 @@ class AMDSMICommands(): try: amdsmi_interface.amdsmi_dev_set_fan_speed(args.gpu, 0, args.fan) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set fan speed {args.fan} on {gpu_string}") from e self.logger.store_output(args.gpu, 'fan', f"Successfully set fan speed {args.fan}") @@ -1513,6 +1575,8 @@ class AMDSMICommands(): try: amdsmi_interface.amdsmi_dev_set_perf_level_v1(args.gpu, perf_level) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set performance level {args.perflevel} on {gpu_string}") from e self.logger.store_output(args.gpu, 'perflevel', f"Successfully set performance level {args.perflevel}") @@ -1521,17 +1585,23 @@ class AMDSMICommands(): try: perf_level = amdsmi_interface.amdsmi_dev_get_perf_level(args.gpu) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to get performance level of {gpu_string}") from e if 'manual' in perf_level.lower(): try: amdsmi_interface.amdsmi_dev_set_perf_level_v1(args.gpu, amdsmi_interface.AmdSmiDevPerfLevel.MANUAL) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set the performance level of {gpu_string} to manual") from e try: amdsmi_interface.amdsmi_dev_set_overdrive_level_v1(args.gpu, args.overdrive) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set overdrive {args.overdrive} to {gpu_string}") from e self.logger.store_output(args.gpu, 'overdrive', f"Successfully to set overdrive level to {args.overdrive}") @@ -1540,12 +1610,16 @@ class AMDSMICommands(): try: perf_level = amdsmi_interface.amdsmi_dev_get_perf_level(args.gpu) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to get performance level of {gpu_string}") from e if 'manual' in perf_level.lower(): try: amdsmi_interface.amdsmi_dev_set_perf_level_v1(args.gpu, amdsmi_interface.AmdSmiDevPerfLevel.MANUAL) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set the performance level of {gpu_string} to manual") from e self.logger.store_output(args.gpu, 'memoverdrive', f"Successfully to set memoverdrive level to {args.memoverdrive}") @@ -1554,6 +1628,8 @@ class AMDSMICommands(): try: power_caps = amdsmi_interface.amdsmi_get_power_cap_info(args.gpu) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to get the power cap info for {gpu_string}") from e if overdrive_power_cap == 0: overdrive_power_cap = power_caps['power_cap_default'] @@ -1572,11 +1648,15 @@ class AMDSMICommands(): try: amdsmi_interface.amdsmi_dev_set_power_cap(args.gpu, 0, overdrive_power_cap) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set power cap to {overdrive_power_cap} on {gpu_string}") from e try: power_caps = amdsmi_interface.amdsmi_get_power_cap_info(args.gpu) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to get the power cap info for {gpu_string} post set") from e if power_caps['power_cap'] == overdrive_power_cap: @@ -1589,6 +1669,8 @@ class AMDSMICommands(): try: amdsmi_interface.amdsmi_set_perf_determinism_mode(args.gpu, args.perfdeterminism) except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') raise ValueError(f"Unable to set performance determinism and clock frequency to {args.perfdeterminism} on {gpu_string}") from e self.logger.store_output(args.gpu, 'perfdeterminism', f"Successfully enabled performance determinism and set GFX clock frequency to {args.perfdeterminism}") @@ -1659,6 +1741,8 @@ class AMDSMICommands(): amdsmi_interface.amdsmi_dev_reset_gpu(args.gpu) result = 'Successfully reset GPU' except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') result = e.get_error_info() else: result = 'Unable to reset non-amd GPU' @@ -1673,6 +1757,8 @@ class AMDSMICommands(): amdsmi_interface.amdsmi_dev_set_overdrive_level_v1(args.gpu, 0) reset_clocks_results['overdrive'] = 'Overdrive set to 0' except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') reset_clocks_results['overdrive'] = e.get_error_info() try: @@ -1680,6 +1766,8 @@ class AMDSMICommands(): amdsmi_interface.amdsmi_dev_set_perf_level_v1(args.gpu, level_auto) reset_clocks_results['clocks'] = 'Successfully reset clocks' except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') reset_clocks_results['clocks'] = e.get_error_info() try: @@ -1687,6 +1775,8 @@ class AMDSMICommands(): amdsmi_interface.amdsmi_dev_set_perf_level_v1(args.gpu, level_auto) reset_clocks_results['performance'] = 'Performance level reset to auto' except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') reset_clocks_results['performance'] = e.get_error_info() self.logger.store_output(args.gpu, 'reset_clocks', reset_clocks_results) @@ -1695,6 +1785,8 @@ class AMDSMICommands(): amdsmi_interface.amdsmi_dev_reset_fan(args.gpu, 0) result = 'Successfully reset fan speed to driver control' except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') result = e.get_error_info() self.logger.store_output(args.gpu, 'reset_fans', result) @@ -1706,6 +1798,8 @@ class AMDSMICommands(): amdsmi_interface.amdsmi_dev_set_power_profile(args.gpu, 0, power_profile_mask) reset_profile_results['power_profile'] = 'Successfully reset Power Profile' except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') reset_profile_results['power_profile'] = e.get_error_info() try: @@ -1713,6 +1807,8 @@ class AMDSMICommands(): amdsmi_interface.amdsmi_dev_set_perf_level_v1(args.gpu, level_auto) reset_profile_results['performance_level'] = 'Successfully reset Performance Level' except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') reset_profile_results['performance_level'] = e.get_error_info() self.logger.store_output(args.gpu, 'reset_profile', reset_profile_results) @@ -1721,6 +1817,8 @@ class AMDSMICommands(): amdsmi_interface.amdsmi_dev_reset_xgmi_error(args.gpu) result = 'Successfully reset XGMI Error count' except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') result = e.get_error_info() self.logger.store_output(args.gpu, 'reset_xgmi_err', result) if args.perfdeterminism: @@ -1729,6 +1827,8 @@ class AMDSMICommands(): amdsmi_interface.amdsmi_dev_set_perf_level_v1(args.gpu, level_auto) result = 'Successfully disabled performance determinism' except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_exception.AmdSmiRetCode.ERR_NO_PERM: + raise PermissionError('Command requires elevation') result = e.get_error_info() self.logger.store_output(args.gpu, 'reset_perf_determinism', result) diff --git a/projects/amdsmi/amdsmi_cli/amdsmi_logger.py b/projects/amdsmi/amdsmi_cli/amdsmi_logger.py index 11486fd361..a31ea7a6a9 100644 --- a/projects/amdsmi/amdsmi_cli/amdsmi_logger.py +++ b/projects/amdsmi/amdsmi_cli/amdsmi_logger.py @@ -20,13 +20,15 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # +import csv import json +import re import time import yaml -import re from enum import Enum from amdsmi_helpers import AMDSMIHelpers +import amdsmi_cli_exceptions class AMDSMILogger(): def __init__(self, compatibility='amdsmi', format='human_readable', @@ -77,178 +79,18 @@ class AMDSMILogger(): 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) + class CsvStdoutBuilder(object): + def __init__(self): + self.csv_string = [] + + def write(self, row): + self.csv_string.append(row) + + def __str__(self): + return ''.join(self.csv_string) - 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): + def _capitalize_keys(self, input_dict): output_dict = {} for key in input_dict.keys(): # Capitalize key if it is a string @@ -258,12 +100,12 @@ class AMDSMILogger(): cap_key = key if isinstance(input_dict[key], dict): - output_dict[cap_key] = self.capitalize_keys(input_dict[key]) + 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)) + cap_key_list.append(self._capitalize_keys(data)) else: cap_key_list.append(data) output_dict[cap_key] = cap_key_list @@ -273,9 +115,9 @@ class AMDSMILogger(): return output_dict - def convert_json_to_human_readable(self, json_object): + def _convert_json_to_human_readable(self, json_object): # First Capitalize all keys in the json object - capitalized_json = self.capitalize_keys(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) @@ -303,6 +145,180 @@ class AMDSMILogger(): return clean_yaml_output + def flatten_dict(self, target_dict): + """This will flatten a dictionary out to a single level of key value stores + removing key's with dictionaries and wrapping each value to in a list + ex: + { + 'usage': { + 'gfx_usage': 0, + 'mem_usage': 0, + 'mm_usage_list': [22,0,0] + } + } + to: + { + 'gfx_usage': 0, + 'mem_usage': 0, + 'mm_usage_list': [22,0,0]} + } + + Args: + target_dict (dict): Dictionary to flatten + parent_key (str): + """ + # print(target_dict) + output_dict = {} + # First flatten out values + + # separetly handle ras and process and firmware + + # If there are multi values, and the values are all dicts + # Then flatten the sub values with parent key + for key, value in target_dict.items(): + if isinstance(value, dict): + # Check number of items in the dict + if len(value.values()) > 1: + value_with_parent_key = {} + for parent_key, child_dict in value.items(): + if isinstance(child_dict, dict): + for child_key, value1 in child_dict.items(): + value_with_parent_key[parent_key + '_' + child_key] = value1 + else: + value_with_parent_key[parent_key] = child_dict + value = value_with_parent_key + + if self.is_gpuvsmi_compatibility(): + if key in ('asic', 'bus', 'pcie', 'vbios','board', 'limit'): + value_with_parent_key = {} + for child_key, child_value in value.items(): + value_with_parent_key[key + '_' + child_key] = child_value + value = value_with_parent_key + + output_dict.update(self.flatten_dict(value).items()) + else: + output_dict[key] = value + return output_dict + + + 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(): + # New way is in gpuvsmi func + self.output['gpu'] = int(gpu_id) + + if argument == 'values' or isinstance(data, dict): + flat_dict = self.flatten_dict(data) + self.output.update(flat_dict) + else: + self.output[argument] = data + + else: + raise amdsmi_cli_exceptions(self, "Invalid output format given, only json, csv, and human_readable supported") + + + 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 amdsmi_cli_exceptions(self, "Invalid output format given, only json, csv, and human_readable supported") + + + 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(): + self.output['gpu'] = int(gpu_id) + + if argument == 'values' or isinstance(data, dict): + flat_dict = self.flatten_dict(data) + self.output.update(flat_dict) + else: + self.output[argument] = data + + gpuv_flat_dict = {} + for key, value in self.output.items(): + gpuv_flat_dict[key] = value + + # Change AMDSMI_STATUS strings to N/A for gpuv compatability + if isinstance(value, str): + if 'AMDSMI_STATUS' in value: + gpuv_flat_dict[key] = 'N/A' + + # Change bdf and uuid keys for gpuv compatability + if isinstance(key, str): + if key in ('bdf','uuid'): + gpuv_flat_dict['gpu_' + key] = gpuv_flat_dict.pop(key) + + self.output = gpuv_flat_dict + + else: + raise amdsmi_cli_exceptions(self, "Invalid output format given, only json, csv, and human_readable supported") + + + 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 not self.output: + return + + self.multiple_device_output.append(self.output) + self.output = {} + + + 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 + """ + values = self.output + if multiple_devices: + values = self.multiple_device_output + + self.watch_output.append({'timestamp': int(time.time()), + 'values': values}) + + def print_output(self, multiple_device_output=False, watch_output=False): """ Print current output acording to format and then destination params: @@ -324,70 +340,73 @@ class AMDSMILogger(): 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 multiple_device_output: + json_output = self.multiple_device_output + else: + json_output = self.output + 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) + json_std_output = json.dumps(json_output, indent = 4) + print(json_std_output) else: # Write output to file - if watch_output: + if watch_output: # Flush the full JSON output to the file on watch command completion 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) + json.dump(json_output, output_file, indent=4) def _print_csv_output(self, multiple_device_output=False, watch_output=False): + if watch_output: # Don't print output if it's for watch + return + + if multiple_device_output: + stored_csv_output = self.multiple_device_output + else: + if not isinstance(self.output, list): + stored_csv_output = [self.output] + 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 + csv_header = stored_csv_output[0].keys() + csv_stdout_output = self.CsvStdoutBuilder() + writer = csv.DictWriter(csv_stdout_output, csv_header) + writer.writeheader() + writer.writerows(stored_csv_output) + + if self.is_gpuvsmi_compatibility(): + print(str(csv_stdout_output).replace('"','')) else: - pass - else: # Write output to file - if watch_output: - pass - elif multiple_device_output: - pass - else: - pass + print(str(csv_stdout_output)) + else: + with self.destination.open('a', newline = '') as output_file: + csv_header = stored_csv_output[0].keys() + writer = csv.DictWriter(output_file, csv_header) + writer.writeheader() + writer.writerows(stored_csv_output) def _print_human_readable_output(self, multiple_device_output=False, watch_output=False): + if watch_output: # Don't print output if it's for watch + return + if multiple_device_output: - human_readable = '' + human_readable_output = '' for output in self.multiple_device_output: - human_readable += (self.convert_json_to_human_readable(output)) + human_readable_output += (self._convert_json_to_human_readable(output)) else: - human_readable = self.convert_json_to_human_readable(self.output) + human_readable_output = self._convert_json_to_human_readable(self.output) if self.destination == 'stdout': - if watch_output: - # print_output may need another value: flush_output vs watch_output - return - # printing as unicode may fail if locale is not set properly - # see: https://stackoverflow.com/questions/9942594/unicodeencodeerror-ascii-codec-cant-encode-character-u-xa0-in-position-20 - # export PYTHONIOENCODING=utf8 try: - # print as unicode - print(human_readable) + # printing as unicode may fail if locale is not set properly + print(human_readable_output) except UnicodeEncodeError: # print as ascii, ignore incompatible characters - print(human_readable.encode('ascii', 'ignore').decode('ascii')) - + print(human_readable_output.encode('ascii', 'ignore').decode('ascii')) else: - if watch_output: - return with self.destination.open('a') as output_file: - output_file.write(human_readable) - - return + output_file.write(human_readable_output) diff --git a/projects/amdsmi/amdsmi_cli/amdsmi_parser.py b/projects/amdsmi/amdsmi_cli/amdsmi_parser.py index 8f71ed5d29..4c2361076f 100644 --- a/projects/amdsmi/amdsmi_cli/amdsmi_parser.py +++ b/projects/amdsmi/amdsmi_cli/amdsmi_parser.py @@ -59,7 +59,7 @@ class AMDSMIParser(argparse.ArgumentParser): width=90), description=f"AMD System Management Interface | {version_string} | {platform_string}", add_help=True, - prog="amdsmi_cli") + prog="amd-smi") # Setup subparsers subparsers = self.add_subparsers( @@ -112,7 +112,14 @@ class AMDSMIParser(argparse.ArgumentParser): raise amdsmi_cli_exceptions.AmdSmiInvalidFilePathException(path, CheckOutputFilePath.outputformat) if path.is_dir(): - path = path / f"{int(time.time())}-amdsmi-output.txt" + file_name = f"{int(time.time())}-amdsmi-output" + if args.json: + file_name += ".json" + elif args.csv: + file_name += ".csv" + else: + file_name += "txt" + path = path / file_name path.touch() setattr(args, self.dest, path) elif path.is_file(): diff --git a/projects/amdsmi/py-interface/_version.py b/projects/amdsmi/py-interface/_version.py deleted file mode 100644 index e34424611d..0000000000 --- a/projects/amdsmi/py-interface/_version.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "0.0.3" \ No newline at end of file diff --git a/projects/amdsmi/py-interface/amdsmi_interface.py b/projects/amdsmi/py-interface/amdsmi_interface.py index 5b1d888150..0e93d31111 100644 --- a/projects/amdsmi/py-interface/amdsmi_interface.py +++ b/projects/amdsmi/py-interface/amdsmi_interface.py @@ -823,7 +823,7 @@ def amdsmi_get_board_info( def amdsmi_get_ras_block_features_enabled( device_handle: amdsmi_wrapper.amdsmi_device_handle, -) -> Dict[str, Any]: +) -> List[Dict[str, str]]: if not isinstance(device_handle, amdsmi_wrapper.amdsmi_device_handle): raise AmdSmiParameterException( device_handle, amdsmi_wrapper.amdsmi_device_handle