From b155d429c9ef61311760b9a8f0872de909d6c76f Mon Sep 17 00:00:00 2001 From: Dalibor Stanisavljevic Date: Wed, 9 Nov 2022 15:38:30 +0100 Subject: [PATCH] SWDEV-361376 - Add events to python interface - Implement context manager event class for easier usage instead of having 4 different APIs. - Add support for with statement for easier cleanup. Change-Id: I396947a93d3f04efc068e17804eab12eefa97cf0 Signed-off-by: Dalibor Stanisavljevic [ROCm/amdsmi commit: 23160fa8e0710ba46583351501146a1e5f2698fd] --- projects/amdsmi/py-interface/README.md | 71 ++++++++++++------- projects/amdsmi/py-interface/__init__.py | 2 +- .../amdsmi/py-interface/amdsmi_interface.py | 53 +++++++++++++- 3 files changed, 100 insertions(+), 26 deletions(-) diff --git a/projects/amdsmi/py-interface/README.md b/projects/amdsmi/py-interface/README.md index e463e48e48..f8841c1c32 100644 --- a/projects/amdsmi/py-interface/README.md +++ b/projects/amdsmi/py-interface/README.md @@ -1089,50 +1089,73 @@ except SmiException as e: print(e) ``` -## EventListen class +## AmdSmiEventReader class -Description: Providing methods for event monitoring +Description: Providing methods for event monitoring. This is context manager class. +Can be used with `with` statement for automatic cleanup. Methods: ## Constructor -Description: Allocates a new event reader notifier to monitor different types of events with the multiple GPUs +Description: Allocates a new event reader notifier to monitor different types of events for the given GPU Input parameters: -* `event_types` types of events to monitor and react on +* `device_handle` device handle corresponding to the device on which to listen for events +* `event_types` list of event types from AmdSmiEvtNotificationType enum. Specifying which events to collect for the given device. + +Event Type | Description +---|------ +`VMFAULT` | VM page fault +`THERMAL_THROTTLE` | thermal throttle +`GPU_PRE_RESET` | gpu pre reset +`GPU_POST_RESET` | gpu post reset ## read -Description: Reads events on GPUs. When event is caught, device handle, event id, message, event type and - time are returned. Reading events stops when timestamp passes without event reading. +Description: Reads events on the given device. When event is caught, device handle, message and event type are returned. Reading events stops when timestamp passes without event reading. Input parameters: -* `timestamp` Amount of miliseconds to wait for event. If event does not happen monitoring is finished -* `i` GPU index to which we need to listen to events. For example 0,1,2... +* `timestamp` number of milliseconds to wait for an event to occur. If event does not happen monitoring is finished +* `num_elem` number of events. This is optional parameter. Default value is 10. -Example: -```python -try: - devices = gpuvsmi_get_devices() - if len(devices) == 0: - print("No GPUs on machine") - else: - device = devices[0] - listener = EventListen(SmiEventType.GPU_PRE_RESET) - listener.read(10000) -except SmiException as e: - print(e) -``` +## stop -## Destructor - -Description: Detroys event listener object, closes all open files and directories +Description: Any resources used by event notification for the the given device will be freed with this function. This can be used explicitly or +automatically using `with` statement, like in the examples below. This should be called either manually or automatically for every created AmdSmiEventReader object. Input parameters: `None` +Example with manual cleanup of AmdSmiEventReader: +```python +try: + devices = amdsmi_get_device_handles() + if len(devices) == 0: + print("No GPUs on machine") + else: + event = AmdSmiEventReader(device[0], AmdSmiEvtNotificationType.GPU_PRE_RESET, AmdSmiEvtNotificationType.GPU_POST_RESET) + event.read(10000) +except AmdSmiException as e: + print(e) +finally: + event.stop() +``` + +Example with automatic cleanup using `with` statement: +```python +try: + devices = amdsmi_get_device_handles() + if len(devices) == 0: + print("No GPUs on machine") + else: + with AmdSmiEventReader(device[0], AmdSmiEvtNotificationType.GPU_PRE_RESET, AmdSmiEvtNotificationType.GPU_POST_RESET) as event: + event.read(10000) +except AmdSmiException as e: + print(e) + +``` ## amdsmi_dev_supported_func_iterator_open Description: Get a function name iterator of supported AMDSMI functions for a device diff --git a/projects/amdsmi/py-interface/__init__.py b/projects/amdsmi/py-interface/__init__.py index 4a2c7855a2..5016deb3be 100644 --- a/projects/amdsmi/py-interface/__init__.py +++ b/projects/amdsmi/py-interface/__init__.py @@ -89,7 +89,7 @@ from .amdsmi_interface import amdsmi_dev_perf_level_set_v1 # # Events -# from .smi_interface import EventListen +from .amdsmi_interface import AmdSmiEventReader # # Enums diff --git a/projects/amdsmi/py-interface/amdsmi_interface.py b/projects/amdsmi/py-interface/amdsmi_interface.py index 90c930a24c..4be19e4321 100644 --- a/projects/amdsmi/py-interface/amdsmi_interface.py +++ b/projects/amdsmi/py-interface/amdsmi_interface.py @@ -22,7 +22,7 @@ import ctypes from typing import Union, Any, Dict, List, Tuple from enum import IntEnum - +from collections.abc import Iterable from . import amdsmi_wrapper from .amdsmi_exception import * @@ -291,6 +291,57 @@ class AmdSmiUtilizationCounterType(IntEnum): COARSE_GRAIN_MEM_ACTIVITY = amdsmi_wrapper.AMDSMI_COARSE_GRAIN_MEM_ACTIVITY +class AmdSmiEventReader: + def __init__(self, device_handle: amdsmi_wrapper.amdsmi_device_handle, *event_types): + if not isinstance(device_handle, amdsmi_wrapper.amdsmi_device_handle): + raise AmdSmiParameterException( + device_handle, amdsmi_wrapper.amdsmi_device_handle + ) + if not isinstance(event_types, Iterable): + raise AmdSmiParameterException( + event_types, Iterable + ) + + for event_type in event_types: + if not isinstance(event_type, AmdSmiEvtNotificationType): + raise AmdSmiParameterException( + event_type, AmdSmiEvtNotificationType + ) + + self.device_handle = device_handle + mask = 0 + for event_type in event_types: + mask |= (1 << (int(event_type) - 1)) + + _check_res(amdsmi_wrapper.amdsmi_event_notification_init(device_handle)) + _check_res(amdsmi_wrapper.amdsmi_event_notification_mask_set(device_handle, ctypes.c_uint64(mask))) + + def read(self, timestamp, num_elem = 10): + self.event_info = (amdsmi_wrapper.amdsmi_evt_notification_data_t * num_elem)() + _check_res(amdsmi_wrapper.amdsmi_event_notification_get(ctypes.c_int(timestamp), ctypes.byref( + ctypes.c_uint32(num_elem)), self.event_info)) + + ret = list() + for i in range(0, num_elem): + if self.event_info[i].event in set(event.value for event in AmdSmiEvtNotificationType): + ret.append({ + 'device_handle' : self.event_info[i].device_handle, + 'event': AmdSmiEvtNotificationType(self.event_info[i].event).name, + 'message': self.event_info[i].message.decode("utf-8") + }) + + return ret + + def stop(self): + _check_res(amdsmi_wrapper.amdsmi_event_notification_stop(self.device_handle)) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.stop() + + _AMDSMI_MAX_DRIVER_VERSION_LENGTH = 80 _AMDSMI_GPU_UUID_SIZE = 38