From 9d1d0142beade5084716a32495bdbb7f21ef0a22 Mon Sep 17 00:00:00 2001 From: Aviral Nigam Date: Wed, 23 Sep 2026 11:21:44 +0530 Subject: [PATCH 1/7] feature: Add pyzes support for info log APIs Add python bindings, unit tests, black box test coverage and README entries for: 1. zesDriverEnumInfoLogsExt 2. zesInfoLogGetPropertiesExt 3. zesInfoLogCreateInstanceExt 4. zesInfoLogInstanceReadWithMetadataExt 5. zesInfoLogInstancePeekWithMetadataExt 6. zesInfoLogInstanceDeleteExt Related-To: SYSM-432 Signed-off-by: Aviral Nigam --- bindings/sysman/python/README.md | 7 + .../sysman/python/source/pyzes/__init__.py | 269 +++++++++++++ .../pyzes/examples/pyzes_black_box_test.py | 305 +++++++++++++++ .../python/test/unit_tests/test_info_log.py | 359 ++++++++++++++++++ 4 files changed, 940 insertions(+) create mode 100644 bindings/sysman/python/test/unit_tests/test_info_log.py diff --git a/bindings/sysman/python/README.md b/bindings/sysman/python/README.md index 6b5c36ba..9d0cae44 100644 --- a/bindings/sysman/python/README.md +++ b/bindings/sysman/python/README.md @@ -204,6 +204,13 @@ SUPPORTED APIs | `zesDeviceEccConfigurable` | ECC | 0.1.2 | None | | `zesDeviceGetEccState` | ECC | 0.1.2 | None | | `zesDeviceSetEccState` | ECC | 0.1.2 | None | +| **Info Log Management** |-|-|-| +| `zesDriverEnumInfoLogsExt` | Info Log | 0.1.3 | None | +| `zesInfoLogGetPropertiesExt` | Info Log | 0.1.3 | None | +| `zesInfoLogCreateInstanceExt` | Info Log | 0.1.3 | None | +| `zesInfoLogInstanceReadWithMetadataExt` | Info Log | 0.1.3 | Records returned are consumed | +| `zesInfoLogInstancePeekWithMetadataExt` | Info Log | 0.1.3 | Requires `isPeekDataSupported` | +| `zesInfoLogInstanceDeleteExt` | Info Log | 0.1.3 | None | RELEASE NOTES ------------- diff --git a/bindings/sysman/python/source/pyzes/__init__.py b/bindings/sysman/python/source/pyzes/__init__.py index 25ab2e3d..f3a484b9 100644 --- a/bindings/sysman/python/source/pyzes/__init__.py +++ b/bindings/sysman/python/source/pyzes/__init__.py @@ -148,6 +148,14 @@ class zes_engine_handle_t(c_void_p): pass +class zes_info_log_handle_t(c_void_p): + pass + + +class zes_info_log_instance_handle_t(c_void_p): + pass + + ## ze_bool_t = c_uint8 @@ -294,6 +302,23 @@ class zes_engine_handle_t(c_void_p): ZES_ENGINE_GROUP_MEDIA_CODEC_SINGLE = 14 ZES_ENGINE_GROUP_FORCE_UINT32 = 0x7FFFFFFF +## Info log enums ## +zes_info_log_type_ext_t = c_int32 +ZES_INFO_LOG_TYPE_EXT_DEVICE = 0 +ZES_INFO_LOG_TYPE_EXT_FORCE_UINT32 = 0x7FFFFFFF + +zes_info_log_format_ext_t = c_int32 +ZES_INFO_LOG_FORMAT_EXT_CPER = 0 +ZES_INFO_LOG_FORMAT_EXT_FORCE_UINT32 = 0x7FFFFFFF + +zes_info_log_record_type_ext_t = c_int32 +ZES_INFO_LOG_RECORD_TYPE_EXT_UNKNOWN = 0 +ZES_INFO_LOG_RECORD_TYPE_EXT_INFORMATIONAL = 1 +ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_CORRECTED = 2 +ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_RECOVERABLE = 3 +ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_FATAL = 4 +ZES_INFO_LOG_RECORD_TYPE_EXT_FORCE_UINT32 = 0x7FFFFFFF + ze_result_t = c_int32 ZE_RESULT_SUCCESS = 0 ZE_RESULT_NOT_READY = 1 @@ -376,6 +401,12 @@ class zes_engine_handle_t(c_void_p): ZES_STRUCTURE_TYPE_TEMP_PROPERTIES = 0x14 ZES_STRUCTURE_TYPE_TEMP_CONFIG = 0x23 ZES_STRUCTURE_TYPE_ENGINE_PROPERTIES = 0x5 +ZES_STRUCTURE_TYPE_INFO_LOG_EXT_PROPERTIES = 0x00020020 # Info log properties +ZES_STRUCTURE_TYPE_INFO_LOG_INSTANCE_EXT_DESC = ( + 0x00020021 # Info log instance descriptor +) +ZES_STRUCTURE_TYPE_INFO_LOG_METADATA_EXT = 0x00020022 # Info log record metadata +ZES_STRUCTURE_TYPE_INFO_LOG_READ_STATUS_EXT = 0x00020023 # Info log read status ## Core ze_device UUID struct ## @@ -779,6 +810,51 @@ class zes_engine_stats_t(_PrintableStructure): _fmt_ = {"activeTime": "%d", "timestamp": "%d"} +## Info log structures ## +class zes_info_log_ext_properties_t(_PrintableStructure): + _fields_ = [ + ("stype", c_int32), # ZES_STRUCTURE_TYPE_INFO_LOG_EXT_PROPERTIES + ("pNext", c_void_p), + ("infoLogType", zes_info_log_type_ext_t), # type of info carried + ("infoLogFormat", zes_info_log_format_ext_t), # format of records + ("isNamedInstanceSupported", ze_bool_t), # named instances supported + ("isPeekDataSupported", ze_bool_t), # peek without consuming supported + ] + + +class zes_info_log_instance_ext_desc_t(_PrintableStructure): + _fields_ = [ + ("stype", c_int32), # ZES_STRUCTURE_TYPE_INFO_LOG_INSTANCE_EXT_DESC + ("pNext", c_void_p), + ("pBufferSizeInKb", POINTER(c_uint32)), # optional buffer size in KB + ] + + +class zes_info_log_metadata_ext_t(_PrintableStructure): + _fields_ = [ + ("stype", c_int32), # ZES_STRUCTURE_TYPE_INFO_LOG_METADATA_EXT + ("pNext", c_void_p), + ("address", zes_pci_address_t), # PCI address of originating device + ("uuid", zes_uuid_t), # UUID of originating device + ("timestamp", c_uint64), # record time in nanoseconds + ("lengthOfData", c_uint32), # record length in bytes + ("offset", c_uint32), # record byte offset in data buffer + ("recordType", zes_info_log_record_type_ext_t), # record type + ] + _fmt_ = {"timestamp": "%d ns", "lengthOfData": "%d bytes"} + + +class zes_info_log_read_status_ext_t(_PrintableStructure): + _fields_ = [ + ("stype", c_int32), # ZES_STRUCTURE_TYPE_INFO_LOG_READ_STATUS_EXT + ("pNext", c_void_p), + ("droppedRecordCount", c_int64), # dropped records, -1 if unknown + ("consumedDataSize", c_uint64), # bytes read out of the instance + ("hasDataToRead", ze_bool_t), # records remain to be read + ] + _fmt_ = {"consumedDataSize": "%d bytes"} + + ## Function access ## _getFunctionPointerList: Dict[str, Any] = dict() @@ -1556,3 +1632,196 @@ def zesEngineGetActivity(hEngine, pStats): retVal = funcPtr(hEngine, pStats) return retVal + + +## Info log functions ## + + +def zesDriverEnumInfoLogsExt(hDriver, pCount, phInfoLogs): + """Wraps API: + ze_result_t zesDriverEnumInfoLogsExt( + zes_driver_handle_t hDriver, + uint32_t* pCount, + zes_info_log_handle_t* phInfoLogs) + + Parameters: + hDriver: driver handle + pCount: POINTER(c_uint32) + phInfoLogs: POINTER(zes_info_log_handle_t) or None + """ + funcPtr = getFunctionPointerList("zesDriverEnumInfoLogsExt") + funcPtr.argtypes = [ + zes_driver_handle_t, + POINTER(c_uint32), + POINTER(zes_info_log_handle_t), + ] + funcPtr.restype = ze_result_t + retVal = funcPtr(hDriver, pCount, phInfoLogs) + return retVal + + +def zesInfoLogGetPropertiesExt(hInfoLog, pProperties): + """Wraps API: + ze_result_t zesInfoLogGetPropertiesExt( + zes_info_log_handle_t hInfoLog, + zes_info_log_ext_properties_t* pProperties) + + Parameters: + hInfoLog: info log handle + pProperties: POINTER(zes_info_log_ext_properties_t) - properties structure to fill + Returns: + ze_result_t - return code only, properties are filled into pProperties + """ + funcPtr = getFunctionPointerList("zesInfoLogGetPropertiesExt") + funcPtr.argtypes = [zes_info_log_handle_t, POINTER(zes_info_log_ext_properties_t)] + funcPtr.restype = ze_result_t + + retVal = funcPtr(hInfoLog, pProperties) + return retVal + + +def zesInfoLogCreateInstanceExt(hInfoLog, pInstanceName, pDesc, phInfoLogInstance): + """Wraps API: + ze_result_t zesInfoLogCreateInstanceExt( + zes_info_log_handle_t hInfoLog, + const char* pInstanceName, + zes_info_log_instance_ext_desc_t* pDesc, + zes_info_log_instance_handle_t* phInfoLogInstance) + + Parameters: + hInfoLog: info log handle + pInstanceName: bytes or None - name of the instance; None uses the default buffer + pDesc: POINTER(zes_info_log_instance_ext_desc_t) - instance descriptor + phInfoLogInstance: POINTER(zes_info_log_instance_handle_t) - created instance handle + Returns: + ze_result_t - return code only, instance handle is filled into phInfoLogInstance + """ + funcPtr = getFunctionPointerList("zesInfoLogCreateInstanceExt") + funcPtr.argtypes = [ + zes_info_log_handle_t, + c_char_p, + POINTER(zes_info_log_instance_ext_desc_t), + POINTER(zes_info_log_instance_handle_t), + ] + funcPtr.restype = ze_result_t + + retVal = funcPtr(hInfoLog, pInstanceName, pDesc, phInfoLogInstance) + return retVal + + +def zesInfoLogInstanceReadWithMetadataExt( + hInfoLogInstance, timeout, pSize, pBuffer, pRecordCount, pDescriptors, pReadStatus +): + """Wraps API: + ze_result_t zesInfoLogInstanceReadWithMetadataExt( + zes_info_log_instance_handle_t hInfoLogInstance, + uint64_t timeout, + uint32_t* pSize, + uint8_t* pBuffer, + uint32_t* pRecordCount, + zes_info_log_metadata_ext_t* pDescriptors, + zes_info_log_read_status_ext_t* pReadStatus) + + Records returned are consumed and are not returned again. + + Parameters: + hInfoLogInstance: info log collection instance handle + timeout: c_uint64 - maximum search time in milliseconds + pSize: POINTER(c_uint32) - size of pBuffer in bytes; 0 makes this a query call + pBuffer: POINTER(c_uint8) or None - buffer for record data + pRecordCount: POINTER(c_uint32) - number of elements in pDescriptors; 0 makes this a query call + pDescriptors: POINTER(zes_info_log_metadata_ext_t) or None - metadata for each record + pReadStatus: POINTER(zes_info_log_read_status_ext_t) or None - status of this call + Returns: + ze_result_t - return code only, records are filled into pBuffer and pDescriptors + """ + funcPtr = getFunctionPointerList("zesInfoLogInstanceReadWithMetadataExt") + funcPtr.argtypes = [ + zes_info_log_instance_handle_t, + c_uint64, + POINTER(c_uint32), + POINTER(c_uint8), + POINTER(c_uint32), + POINTER(zes_info_log_metadata_ext_t), + POINTER(zes_info_log_read_status_ext_t), + ] + funcPtr.restype = ze_result_t + + retVal = funcPtr( + hInfoLogInstance, + timeout, + pSize, + pBuffer, + pRecordCount, + pDescriptors, + pReadStatus, + ) + return retVal + + +def zesInfoLogInstancePeekWithMetadataExt( + hInfoLogInstance, timeout, pSize, pBuffer, pRecordCount, pDescriptors, pReadStatus +): + """Wraps API: + ze_result_t zesInfoLogInstancePeekWithMetadataExt( + zes_info_log_instance_handle_t hInfoLogInstance, + uint64_t timeout, + uint32_t* pSize, + uint8_t* pBuffer, + uint32_t* pRecordCount, + zes_info_log_metadata_ext_t* pDescriptors, + zes_info_log_read_status_ext_t* pReadStatus) + + Records returned are not consumed. + + Parameters: + hInfoLogInstance: info log collection instance handle + timeout: c_uint64 - maximum search time in milliseconds + pSize: POINTER(c_uint32) - size of pBuffer in bytes; 0 makes this a query call + pBuffer: POINTER(c_uint8) or None - buffer for record data + pRecordCount: POINTER(c_uint32) - number of elements in pDescriptors; 0 makes this a query call + pDescriptors: POINTER(zes_info_log_metadata_ext_t) or None - metadata for each record + pReadStatus: POINTER(zes_info_log_read_status_ext_t) or None - status of this call + Returns: + ze_result_t - return code only, records are filled into pBuffer and pDescriptors + """ + funcPtr = getFunctionPointerList("zesInfoLogInstancePeekWithMetadataExt") + funcPtr.argtypes = [ + zes_info_log_instance_handle_t, + c_uint64, + POINTER(c_uint32), + POINTER(c_uint8), + POINTER(c_uint32), + POINTER(zes_info_log_metadata_ext_t), + POINTER(zes_info_log_read_status_ext_t), + ] + funcPtr.restype = ze_result_t + + retVal = funcPtr( + hInfoLogInstance, + timeout, + pSize, + pBuffer, + pRecordCount, + pDescriptors, + pReadStatus, + ) + return retVal + + +def zesInfoLogInstanceDeleteExt(hInfoLogInstance): + """Wraps API: + ze_result_t zesInfoLogInstanceDeleteExt( + zes_info_log_instance_handle_t hInfoLogInstance) + + Parameters: + hInfoLogInstance: info log collection instance handle to delete + Returns: + ze_result_t - return code only + """ + funcPtr = getFunctionPointerList("zesInfoLogInstanceDeleteExt") + funcPtr.argtypes = [zes_info_log_instance_handle_t] + funcPtr.restype = ze_result_t + + retVal = funcPtr(hInfoLogInstance) + return retVal diff --git a/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py b/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py index 471d182d..8d5adc38 100755 --- a/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py +++ b/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py @@ -321,6 +321,36 @@ def get_device_action_string(action): return action_map.get(action, f"UNKNOWN_DEVICE_ACTION_{action}") +def get_info_log_type_string(info_log_type): + """Convert info log type enum to string""" + type_map = { + pz.ZES_INFO_LOG_TYPE_EXT_DEVICE: "ZES_INFO_LOG_TYPE_EXT_DEVICE", + } + return type_map.get(info_log_type, f"UNKNOWN_INFO_LOG_TYPE_{info_log_type}") + + +def get_info_log_format_string(info_log_format): + """Convert info log format enum to string""" + format_map = { + pz.ZES_INFO_LOG_FORMAT_EXT_CPER: "ZES_INFO_LOG_FORMAT_EXT_CPER", + } + return format_map.get(info_log_format, f"UNKNOWN_INFO_LOG_FORMAT_{info_log_format}") + + +def get_info_log_record_type_string(record_type): + """Convert info log record type enum to string""" + record_type_map = { + pz.ZES_INFO_LOG_RECORD_TYPE_EXT_UNKNOWN: "ZES_INFO_LOG_RECORD_TYPE_EXT_UNKNOWN", + pz.ZES_INFO_LOG_RECORD_TYPE_EXT_INFORMATIONAL: "ZES_INFO_LOG_RECORD_TYPE_EXT_INFORMATIONAL", + pz.ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_CORRECTED: "ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_CORRECTED", + pz.ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_RECOVERABLE: "ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_RECOVERABLE", + pz.ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_FATAL: "ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_FATAL", + } + return record_type_map.get( + record_type, f"UNKNOWN_INFO_LOG_RECORD_TYPE_{record_type}" + ) + + def is_root_user(): """Return whether the current user has root privileges on platforms that support it""" geteuid = getattr(os, "geteuid", None) @@ -840,6 +870,232 @@ def test_engine_modules(device_handle, device_index): return True +INFO_LOG_READ_TIMEOUT_MS = 1000 + + +def collect_info_log_records( + collect_fn, api_name, instance_handle, timeout, info_log_index +): + """Query and then collect info log records with the given read or peek function. + + Returns (record_count, size, buffer, descriptors, status) or None on failure. + """ + size = c_uint32(0) + record_count = c_uint32(0) + rc = collect_fn( + instance_handle, timeout, byref(size), None, byref(record_count), None, None + ) + if not check_rc(f"{api_name}(info log {info_log_index}, query)", rc): + return None + + print_verbose( + f" {api_name} query: {record_count.value} record(s), {size.value} bytes" + ) + if size.value == 0 or record_count.value == 0: + return 0, 0, None, None, None + + buffer = (c_uint8 * size.value)() + descriptors = (pz.zes_info_log_metadata_ext_t * record_count.value)() + for j in range(record_count.value): + descriptors[j].stype = pz.ZES_STRUCTURE_TYPE_INFO_LOG_METADATA_EXT + descriptors[j].pNext = None + + status = pz.zes_info_log_read_status_ext_t() + status.stype = pz.ZES_STRUCTURE_TYPE_INFO_LOG_READ_STATUS_EXT + status.pNext = None + + rc = collect_fn( + instance_handle, + timeout, + byref(size), + buffer, + byref(record_count), + descriptors, + byref(status), + ) + if rc == pz.ZE_RESULT_WARNING_DROPPED_DATA: + print_verbose(f" Dropped Records: {status.droppedRecordCount}") + elif not check_rc(f"{api_name}(info log {info_log_index}, data)", rc): + return None + + return record_count.value, size.value, buffer, descriptors, status + + +def print_info_log_records(action, record_count, size, descriptors, status): + """Print the metadata of collected info log records and the read status""" + print_verbose(f" {action} {record_count} record(s), {size} bytes:") + for j in range(record_count): + record = descriptors[j] + print_verbose(f" Record {j}:") + print_verbose( + f" Type: {get_info_log_record_type_string(record.recordType)}" + ) + print_verbose( + f" BDF: {record.address.domain:04X}:{record.address.bus:02X}:" + f"{record.address.device:02X}.{record.address.function:X}" + ) + print_verbose(f" Timestamp: {record.timestamp} ns") + print_verbose(f" Offset: {record.offset}") + print_verbose(f" Length: {record.lengthOfData} bytes") + print_verbose(f" Consumed Data Size: {status.consumedDataSize} bytes") + print_verbose(f" Has Data To Read: {bool(status.hasDataToRead)}") + + +def test_info_log_module( + driver_handle, + driver_index, + instance_name=None, + buffer_size_kb=None, + timeout=INFO_LOG_READ_TIMEOUT_MS, + read_records=False, +): + """Test info log enumeration, properties, and collection instance operations""" + print(f"\n---- Driver {driver_index} Info Log Test ----") + + info_log_count = c_uint32(0) + rc = pz.zesDriverEnumInfoLogsExt(driver_handle, byref(info_log_count), None) + if not check_rc(f"zesDriverEnumInfoLogsExt(driver {driver_index}, count)", rc): + return False + + if info_log_count.value == 0: + print_verbose("No info logs found on this driver") + return True + + print_verbose(f"Found {info_log_count.value} info log(s)") + + InfoLogArray = pz.zes_info_log_handle_t * info_log_count.value + info_log_handles = InfoLogArray() + + rc = pz.zesDriverEnumInfoLogsExt( + driver_handle, byref(info_log_count), info_log_handles + ) + if not check_rc(f"zesDriverEnumInfoLogsExt(driver {driver_index}, handles)", rc): + return False + + for i in range(info_log_count.value): + print_verbose(f"\n Info Log {i}:") + + props = pz.zes_info_log_ext_properties_t() + props.stype = pz.ZES_STRUCTURE_TYPE_INFO_LOG_EXT_PROPERTIES + props.pNext = None + + rc = pz.zesInfoLogGetPropertiesExt(info_log_handles[i], byref(props)) + if not check_rc(f"zesInfoLogGetPropertiesExt(info log {i})", rc): + continue + + print_verbose(f" Type: {get_info_log_type_string(props.infoLogType)}") + print_verbose(f" Format: {get_info_log_format_string(props.infoLogFormat)}") + print_verbose( + f" Named Instance Supported: {bool(props.isNamedInstanceSupported)}" + ) + print_verbose(f" Peek Data Supported: {bool(props.isPeekDataSupported)}") + + if instance_name is not None and not props.isNamedInstanceSupported: + print_verbose( + f" Skipping named instance '{instance_name}' since named instances are not supported" + ) + continue + + # Unset options use the default buffer and default buffer size + desc = pz.zes_info_log_instance_ext_desc_t() + desc.stype = pz.ZES_STRUCTURE_TYPE_INFO_LOG_INSTANCE_EXT_DESC + desc.pNext = None + buffer_size = None + if buffer_size_kb is not None: + buffer_size = c_uint32(buffer_size_kb) + desc.pBufferSizeInKb = pointer(buffer_size) + else: + desc.pBufferSizeInKb = None + + name = instance_name.encode() if instance_name is not None else None + instance_handle = pz.zes_info_log_instance_handle_t() + rc = pz.zesInfoLogCreateInstanceExt( + info_log_handles[i], name, byref(desc), byref(instance_handle) + ) + if rc != pz.ZE_RESULT_SUCCESS: + print_verbose( + f" Collection Instance: Not available ({get_result_string(rc)})" + ) + continue + + label = ( + f"'{instance_name}'" + if instance_name is not None + else "on the default buffer" + ) + print_verbose(f" Created collection instance {label} successfully") + if buffer_size is not None: + print_verbose( + f" Buffer Size: requested {buffer_size_kb} KB, applied {buffer_size.value} KB" + ) + + peeked_count = None + if props.isPeekDataSupported: + peeked = collect_info_log_records( + pz.zesInfoLogInstancePeekWithMetadataExt, + "zesInfoLogInstancePeekWithMetadataExt", + instance_handle, + timeout, + i, + ) + if peeked is not None: + peeked_count = peeked[0] + if peeked_count > 0: + print_info_log_records( + "Peeked", peeked[0], peeked[1], peeked[3], peeked[4] + ) + + if read_records: + # Records are consumed, so keep reading while the driver reports more data + read_count = 0 + while True: + read = collect_info_log_records( + pz.zesInfoLogInstanceReadWithMetadataExt, + "zesInfoLogInstanceReadWithMetadataExt", + instance_handle, + timeout, + i, + ) + if read is None or read[0] == 0: + break + read_count += read[0] + print_info_log_records("Read", read[0], read[1], read[3], read[4]) + if not read[4].hasDataToRead: + break + + print_verbose(f" Total Records Read: {read_count}") + if peeked_count is not None and peeked_count != read_count: + print_verbose( + f" Note: peek returned {peeked_count} record(s) and read returned {read_count}, " + "records may have arrived between the calls" + ) + else: + # Only a query call is made, so records in the shared default buffer are not consumed + size = c_uint32(0) + record_count = c_uint32(0) + rc = pz.zesInfoLogInstanceReadWithMetadataExt( + instance_handle, + timeout, + byref(size), + None, + byref(record_count), + None, + None, + ) + if check_rc( + f"zesInfoLogInstanceReadWithMetadataExt(info log {i}, query)", rc + ): + print_verbose( + f" Read Query: {record_count.value} record(s), {size.value} bytes" + ) + + rc = pz.zesInfoLogInstanceDeleteExt(instance_handle) + if check_rc(f"zesInfoLogInstanceDeleteExt(info log {i})", rc): + print_verbose(" Deleted collection instance successfully") + + return True + + def test_memory_modules(device_handle, device_index): """Test memory module enumeration and operations""" print(f"\n---- Device {device_index} Memory Modules Test ----") @@ -1395,6 +1651,9 @@ def run_all_tests(): # Test engine modules test_engine_modules(devices[device_idx], device_idx) + # Test info logs (driver scoped) + test_info_log_module(drivers[driver_idx], driver_idx) + print("\n=== Test Completed ===") return True @@ -1413,6 +1672,9 @@ def main(): %(prog)s -f # Frequency tests only %(prog)s -t # Temperature tests only %(prog)s -e # Engine tests only + %(prog)s -l # Info log tests only + %(prog)s -l --instance-read --timeout 5000 # Info log tests, peek then consume all records + %(prog)s -l --instance mylog --buffer-size 1024 # Info log tests on a named 1 MB collection instance %(prog)s -h # Show help message""", formatter_class=argparse.RawDescriptionHelpFormatter, ) @@ -1450,8 +1712,38 @@ def main(): version="Python Level Zero Sysman Black Box Test v1.0", ) parser.add_argument("-e", "--engine", action="store_true", help="Run engine tests ") + parser.add_argument( + "-l", "--infolog", action="store_true", help="Run only info log tests" + ) + parser.add_argument( + "--instance", + metavar="NAME", + help="With -l, collect into a named instance instead of the default buffer", + ) + parser.add_argument( + "--buffer-size", + type=int, + metavar="KB", + help="With -l, request a collection buffer size in kilobytes (0 reports the default size)", + ) + parser.add_argument( + "--timeout", + type=int, + default=INFO_LOG_READ_TIMEOUT_MS, + metavar="MS", + help=f"With -l, maximum time in milliseconds to search for records, default is {INFO_LOG_READ_TIMEOUT_MS}", + ) + parser.add_argument( + "--instance-read", + action="store_true", + help="With -l, read and consume all collected records after peeking them", + ) args = parser.parse_args() + if args.buffer_size is not None and args.buffer_size < 0: + parser.error("--buffer-size must be a non-negative integer") + if args.timeout < 0: + parser.error("--timeout must be a non-negative integer") # Check if any specific test is requested specific_test = ( @@ -1463,6 +1755,7 @@ def main(): or args.frequency or args.temperature or args.engine + or args.infolog or args.all ) @@ -1489,6 +1782,18 @@ def main(): print("No devices available for testing") return 1 + # Info logs are driver scoped, so run them once per driver + if args.infolog: + for driver_idx in range(driver_count): + test_info_log_module( + drivers[driver_idx], + driver_idx, + instance_name=args.instance, + buffer_size_kb=args.buffer_size, + timeout=args.timeout, + read_records=args.instance_read, + ) + # Run selected tests on all devices for device_idx in range(device_count): if getattr(args, "global", False): diff --git a/bindings/sysman/python/test/unit_tests/test_info_log.py b/bindings/sysman/python/test/unit_tests/test_info_log.py new file mode 100644 index 00000000..7947cd24 --- /dev/null +++ b/bindings/sysman/python/test/unit_tests/test_info_log.py @@ -0,0 +1,359 @@ +## +# Copyright (C) 2026 Intel Corporation +# +# SPDX-License-Identifier: MIT +# +## + +import os +import sys +import unittest +from ctypes import * +from unittest.mock import MagicMock, patch + +# Add the source directory to Python path so we can import pyzes +script_dir = os.path.dirname(os.path.abspath(__file__)) +source_dir = os.path.join(script_dir, "..", "..", "source") +source_dir = os.path.abspath(source_dir) +if source_dir not in sys.path: + sys.path.insert(0, source_dir) + + +@patch("pyzes.getFunctionPointerList") +class TestInfoLogFunctions(unittest.TestCase): + def setUp(self): + import pyzes + + self.pyzes = pyzes + + def test_GivenValidDriverHandleWhenCallingZesDriverEnumInfoLogsExtThenCallSucceedsWithValidCount( + self, mock_get_func + ): + mock_count = 1 + + def mock_enum_info_logs(driver_handle, count_ptr, handles_ptr): + count_ptr._obj.value = mock_count + return self.pyzes.ZE_RESULT_SUCCESS + + mock_func = MagicMock(side_effect=mock_enum_info_logs) + mock_get_func.return_value = mock_func + + driver_handle = self.pyzes.zes_driver_handle_t() + count = c_uint32(0) + + result = self.pyzes.zesDriverEnumInfoLogsExt(driver_handle, byref(count), None) + + self.assertEqual(result, self.pyzes.ZE_RESULT_SUCCESS) + self.assertEqual(count.value, mock_count) + mock_get_func.assert_called_with("zesDriverEnumInfoLogsExt") + mock_func.assert_called_once() + + def test_GivenValidInfoLogHandleWhenCallingZesInfoLogGetPropertiesExtThenCallSucceedsWithValidProperties( + self, mock_get_func + ): + def mock_get_properties(info_log_handle, properties_ptr): + properties = properties_ptr._obj + properties.infoLogType = self.pyzes.ZES_INFO_LOG_TYPE_EXT_DEVICE + properties.infoLogFormat = self.pyzes.ZES_INFO_LOG_FORMAT_EXT_CPER + properties.isNamedInstanceSupported = 1 + properties.isPeekDataSupported = 1 + return self.pyzes.ZE_RESULT_SUCCESS + + mock_func = MagicMock(side_effect=mock_get_properties) + mock_get_func.return_value = mock_func + + info_log_handle = self.pyzes.zes_info_log_handle_t() + properties = self.pyzes.zes_info_log_ext_properties_t() + properties.stype = self.pyzes.ZES_STRUCTURE_TYPE_INFO_LOG_EXT_PROPERTIES + + result = self.pyzes.zesInfoLogGetPropertiesExt( + info_log_handle, byref(properties) + ) + + self.assertEqual(result, self.pyzes.ZE_RESULT_SUCCESS) + self.assertEqual( + properties.infoLogType, self.pyzes.ZES_INFO_LOG_TYPE_EXT_DEVICE + ) + self.assertEqual( + properties.infoLogFormat, self.pyzes.ZES_INFO_LOG_FORMAT_EXT_CPER + ) + self.assertEqual(properties.isNamedInstanceSupported, 1) + self.assertEqual(properties.isPeekDataSupported, 1) + mock_get_func.assert_called_with("zesInfoLogGetPropertiesExt") + mock_func.assert_called_once() + + def test_GivenValidInfoLogHandleWhenCallingZesInfoLogCreateInstanceExtThenCallSucceedsWithInstanceHandle( + self, mock_get_func + ): + def mock_create_instance(info_log_handle, name, desc_ptr, instance_ptr): + self.assertEqual(name, b"pyzes_instance") + desc = desc_ptr._obj + self.assertEqual( + desc.stype, self.pyzes.ZES_STRUCTURE_TYPE_INFO_LOG_INSTANCE_EXT_DESC + ) + # Driver reports the buffer size it actually applied + desc.pBufferSizeInKb[0] = 64 + instance_ptr._obj.value = 0x1234 + return self.pyzes.ZE_RESULT_SUCCESS + + mock_func = MagicMock(side_effect=mock_create_instance) + mock_get_func.return_value = mock_func + + info_log_handle = self.pyzes.zes_info_log_handle_t() + buffer_size_kb = c_uint32(0) + desc = self.pyzes.zes_info_log_instance_ext_desc_t() + desc.stype = self.pyzes.ZES_STRUCTURE_TYPE_INFO_LOG_INSTANCE_EXT_DESC + desc.pBufferSizeInKb = pointer(buffer_size_kb) + instance_handle = self.pyzes.zes_info_log_instance_handle_t() + + result = self.pyzes.zesInfoLogCreateInstanceExt( + info_log_handle, b"pyzes_instance", byref(desc), byref(instance_handle) + ) + + self.assertEqual(result, self.pyzes.ZE_RESULT_SUCCESS) + self.assertEqual(buffer_size_kb.value, 64) + self.assertEqual(instance_handle.value, 0x1234) + mock_get_func.assert_called_with("zesInfoLogCreateInstanceExt") + mock_func.assert_called_once() + + def test_GivenValidInstanceHandleWhenCallingZesInfoLogInstanceReadWithMetadataExtWithZeroSizeThenQueryReturnsSizeAndRecordCount( + self, mock_get_func + ): + def mock_query( + instance_handle, + timeout, + size_ptr, + buffer, + count_ptr, + descriptors, + status_ptr, + ): + self.assertEqual(timeout, 1000) + self.assertIsNone(buffer) + self.assertIsNone(descriptors) + size_ptr._obj.value = 512 + count_ptr._obj.value = 2 + return self.pyzes.ZE_RESULT_SUCCESS + + mock_func = MagicMock(side_effect=mock_query) + mock_get_func.return_value = mock_func + + instance_handle = self.pyzes.zes_info_log_instance_handle_t() + size = c_uint32(0) + record_count = c_uint32(0) + + result = self.pyzes.zesInfoLogInstanceReadWithMetadataExt( + instance_handle, 1000, byref(size), None, byref(record_count), None, None + ) + + self.assertEqual(result, self.pyzes.ZE_RESULT_SUCCESS) + self.assertEqual(size.value, 512) + self.assertEqual(record_count.value, 2) + mock_get_func.assert_called_with("zesInfoLogInstanceReadWithMetadataExt") + mock_func.assert_called_once() + + def test_GivenValidInstanceHandleWhenCallingZesInfoLogInstanceReadWithMetadataExtWithBuffersThenRecordsAndStatusAreReturned( + self, mock_get_func + ): + def mock_read( + instance_handle, + timeout, + size_ptr, + buffer, + count_ptr, + descriptors, + status_ptr, + ): + buffer[0] = 0x43 + buffer[300] = 0x50 + descriptors[0].lengthOfData = 300 + descriptors[0].offset = 0 + descriptors[0].timestamp = 123456789 + descriptors[0].recordType = ( + self.pyzes.ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_CORRECTED + ) + descriptors[0].address.bus = 0x3A + descriptors[1].lengthOfData = 212 + descriptors[1].offset = 300 + descriptors[1].recordType = ( + self.pyzes.ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_FATAL + ) + status = status_ptr._obj + status.droppedRecordCount = 0 + status.consumedDataSize = 4096 + status.hasDataToRead = 0 + return self.pyzes.ZE_RESULT_SUCCESS + + mock_func = MagicMock(side_effect=mock_read) + mock_get_func.return_value = mock_func + + instance_handle = self.pyzes.zes_info_log_instance_handle_t() + size = c_uint32(512) + record_count = c_uint32(2) + buffer = (c_uint8 * size.value)() + descriptors = (self.pyzes.zes_info_log_metadata_ext_t * record_count.value)() + for i in range(record_count.value): + descriptors[i].stype = self.pyzes.ZES_STRUCTURE_TYPE_INFO_LOG_METADATA_EXT + status = self.pyzes.zes_info_log_read_status_ext_t() + status.stype = self.pyzes.ZES_STRUCTURE_TYPE_INFO_LOG_READ_STATUS_EXT + + result = self.pyzes.zesInfoLogInstanceReadWithMetadataExt( + instance_handle, + 1000, + byref(size), + buffer, + byref(record_count), + descriptors, + byref(status), + ) + + self.assertEqual(result, self.pyzes.ZE_RESULT_SUCCESS) + self.assertEqual(buffer[0], 0x43) + self.assertEqual(buffer[300], 0x50) + self.assertEqual(descriptors[0].lengthOfData, 300) + self.assertEqual(descriptors[0].timestamp, 123456789) + self.assertEqual( + descriptors[0].recordType, + self.pyzes.ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_CORRECTED, + ) + self.assertEqual(descriptors[0].address.bus, 0x3A) + self.assertEqual(descriptors[1].offset, 300) + self.assertEqual( + descriptors[1].recordType, + self.pyzes.ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_FATAL, + ) + self.assertEqual(status.droppedRecordCount, 0) + self.assertEqual(status.consumedDataSize, 4096) + self.assertEqual(status.hasDataToRead, 0) + mock_get_func.assert_called_with("zesInfoLogInstanceReadWithMetadataExt") + mock_func.assert_called_once() + + def test_GivenValidInstanceHandleWhenCallingZesInfoLogInstancePeekWithMetadataExtWithZeroSizeThenQueryReturnsSizeAndRecordCount( + self, mock_get_func + ): + def mock_query( + instance_handle, + timeout, + size_ptr, + buffer, + count_ptr, + descriptors, + status_ptr, + ): + self.assertEqual(timeout, 1000) + self.assertIsNone(buffer) + self.assertIsNone(descriptors) + size_ptr._obj.value = 512 + count_ptr._obj.value = 2 + return self.pyzes.ZE_RESULT_SUCCESS + + mock_func = MagicMock(side_effect=mock_query) + mock_get_func.return_value = mock_func + + instance_handle = self.pyzes.zes_info_log_instance_handle_t() + size = c_uint32(0) + record_count = c_uint32(0) + + result = self.pyzes.zesInfoLogInstancePeekWithMetadataExt( + instance_handle, 1000, byref(size), None, byref(record_count), None, None + ) + + self.assertEqual(result, self.pyzes.ZE_RESULT_SUCCESS) + self.assertEqual(size.value, 512) + self.assertEqual(record_count.value, 2) + mock_get_func.assert_called_with("zesInfoLogInstancePeekWithMetadataExt") + mock_func.assert_called_once() + + def test_GivenValidInstanceHandleWhenCallingZesInfoLogInstancePeekWithMetadataExtWithBuffersThenRecordsAndStatusAreReturned( + self, mock_get_func + ): + def mock_read( + instance_handle, + timeout, + size_ptr, + buffer, + count_ptr, + descriptors, + status_ptr, + ): + buffer[0] = 0x43 + buffer[300] = 0x50 + descriptors[0].lengthOfData = 300 + descriptors[0].offset = 0 + descriptors[0].timestamp = 123456789 + descriptors[0].recordType = ( + self.pyzes.ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_CORRECTED + ) + descriptors[0].address.bus = 0x3A + descriptors[1].lengthOfData = 212 + descriptors[1].offset = 300 + descriptors[1].recordType = ( + self.pyzes.ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_FATAL + ) + status = status_ptr._obj + status.droppedRecordCount = 0 + status.consumedDataSize = 4096 + status.hasDataToRead = 0 + return self.pyzes.ZE_RESULT_SUCCESS + + mock_func = MagicMock(side_effect=mock_read) + mock_get_func.return_value = mock_func + + instance_handle = self.pyzes.zes_info_log_instance_handle_t() + size = c_uint32(512) + record_count = c_uint32(2) + buffer = (c_uint8 * size.value)() + descriptors = (self.pyzes.zes_info_log_metadata_ext_t * record_count.value)() + for i in range(record_count.value): + descriptors[i].stype = self.pyzes.ZES_STRUCTURE_TYPE_INFO_LOG_METADATA_EXT + status = self.pyzes.zes_info_log_read_status_ext_t() + status.stype = self.pyzes.ZES_STRUCTURE_TYPE_INFO_LOG_READ_STATUS_EXT + + result = self.pyzes.zesInfoLogInstancePeekWithMetadataExt( + instance_handle, + 1000, + byref(size), + buffer, + byref(record_count), + descriptors, + byref(status), + ) + + self.assertEqual(result, self.pyzes.ZE_RESULT_SUCCESS) + self.assertEqual(buffer[0], 0x43) + self.assertEqual(buffer[300], 0x50) + self.assertEqual(descriptors[0].lengthOfData, 300) + self.assertEqual(descriptors[0].timestamp, 123456789) + self.assertEqual( + descriptors[0].recordType, + self.pyzes.ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_CORRECTED, + ) + self.assertEqual(descriptors[0].address.bus, 0x3A) + self.assertEqual(descriptors[1].offset, 300) + self.assertEqual( + descriptors[1].recordType, + self.pyzes.ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_FATAL, + ) + self.assertEqual(status.droppedRecordCount, 0) + self.assertEqual(status.consumedDataSize, 4096) + self.assertEqual(status.hasDataToRead, 0) + mock_get_func.assert_called_with("zesInfoLogInstancePeekWithMetadataExt") + mock_func.assert_called_once() + + def test_GivenValidInstanceHandleWhenCallingZesInfoLogInstanceDeleteExtThenCallSucceeds( + self, mock_get_func + ): + mock_func = MagicMock(return_value=self.pyzes.ZE_RESULT_SUCCESS) + mock_get_func.return_value = mock_func + + instance_handle = self.pyzes.zes_info_log_instance_handle_t(0x1234) + + result = self.pyzes.zesInfoLogInstanceDeleteExt(instance_handle) + + self.assertEqual(result, self.pyzes.ZE_RESULT_SUCCESS) + mock_get_func.assert_called_with("zesInfoLogInstanceDeleteExt") + mock_func.assert_called_once_with(instance_handle) + + +if __name__ == "__main__": + unittest.main() From 86925847da99de3fde4ae52512a47bb05be13300 Mon Sep 17 00:00:00 2001 From: Aviral Nigam Date: Mon, 28 Sep 2026 14:18:06 +0530 Subject: [PATCH 2/7] fix: Align pyzes black box info log test with zello_sysman Report info log properties by default and collect records only with --instance-api, --instance-peek or --instance-read as root. --- .../pyzes/examples/pyzes_black_box_test.py | 651 +++++++++++++----- 1 file changed, 483 insertions(+), 168 deletions(-) diff --git a/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py b/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py index 8d5adc38..cf589f13 100755 --- a/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py +++ b/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py @@ -871,86 +871,444 @@ def test_engine_modules(device_handle, device_index): INFO_LOG_READ_TIMEOUT_MS = 1000 +INFO_LOG_RECORD_WAIT_MS = 5000 +INFO_LOG_POLL_INTERVAL_MS = 200 +INFO_LOG_MAX_DRAIN_CALLS = 16 +INFO_LOG_INSTANCE_NAME = "pyzes_black_box_infolog" -def collect_info_log_records( - collect_fn, api_name, instance_handle, timeout, info_log_index -): - """Query and then collect info log records with the given read or peek function. +class InfoLogChecks: + """Count and print the pass and fail results of the info log checks""" + + def __init__(self): + self.passed = 0 + self.failed = 0 + + def __call__(self, condition, description): + if condition: + print_verbose(f" [PASS] {description}") + self.passed += 1 + else: + print_verbose(f" [FAIL] {description}") + self.failed += 1 + + def print_summary(self): + print_verbose(f"{self.passed} check(s) passed, {self.failed} check(s) failed") + + +class InfoLogRecords: + """Records returned by one read or peek call""" + + def __init__(self): + self.size = 0 + self.record_count = 0 + self.buffer = None + self.descriptors = None + self.status = None + + +def query_info_log_records(collect_fn, instance_handle, timeout): + """Query the records held by the instance without consuming them. - Returns (record_count, size, buffer, descriptors, status) or None on failure. + A call with *pSize and *pRecordCount zero on input is a query call for both read and peek. + Returns (rc, size, record_count). """ size = c_uint32(0) record_count = c_uint32(0) rc = collect_fn( instance_handle, timeout, byref(size), None, byref(record_count), None, None ) - if not check_rc(f"{api_name}(info log {info_log_index}, query)", rc): + return rc, size.value, record_count.value + + +def collect_info_log_records(collect_fn, api_name, instance_handle, timeout): + """Query the records held by the instance, then collect them in a second call. + + Returns an InfoLogRecords object, or None on failure. + """ + records = InfoLogRecords() + rc, records.size, records.record_count = query_info_log_records( + collect_fn, instance_handle, timeout + ) + if not check_rc(f"{api_name}(query)", rc): return None print_verbose( - f" {api_name} query: {record_count.value} record(s), {size.value} bytes" + f"{api_name}() query: {records.record_count} record(s), {records.size} byte(s) pending" ) - if size.value == 0 or record_count.value == 0: - return 0, 0, None, None, None - - buffer = (c_uint8 * size.value)() - descriptors = (pz.zes_info_log_metadata_ext_t * record_count.value)() + if records.size == 0 or records.record_count == 0: + records.size = 0 + records.record_count = 0 + return records + + size = c_uint32(records.size) + record_count = c_uint32(records.record_count) + records.buffer = (c_uint8 * size.value)() + records.descriptors = (pz.zes_info_log_metadata_ext_t * record_count.value)() for j in range(record_count.value): - descriptors[j].stype = pz.ZES_STRUCTURE_TYPE_INFO_LOG_METADATA_EXT - descriptors[j].pNext = None + records.descriptors[j].stype = pz.ZES_STRUCTURE_TYPE_INFO_LOG_METADATA_EXT + records.descriptors[j].pNext = None - status = pz.zes_info_log_read_status_ext_t() - status.stype = pz.ZES_STRUCTURE_TYPE_INFO_LOG_READ_STATUS_EXT - status.pNext = None + records.status = pz.zes_info_log_read_status_ext_t() + records.status.stype = pz.ZES_STRUCTURE_TYPE_INFO_LOG_READ_STATUS_EXT + records.status.pNext = None rc = collect_fn( instance_handle, timeout, byref(size), - buffer, + records.buffer, byref(record_count), - descriptors, - byref(status), + records.descriptors, + byref(records.status), + ) + if rc != pz.ZE_RESULT_WARNING_DROPPED_DATA and not check_rc(f"{api_name}()", rc): + return None + + records.size = size.value + records.record_count = record_count.value + print_verbose( + f"{api_name}(): {records.record_count} record(s), {records.size} byte(s) returned" + ) + dropped = ( + records.status.droppedRecordCount + if rc == pz.ZE_RESULT_WARNING_DROPPED_DATA + else 0 + ) + if dropped == -1: + dropped = "unknown" + print_verbose( + f" readStatus.hasDataToRead = {bool(records.status.hasDataToRead)}, " + f"readStatus.droppedRecordCount = {dropped}, " + f"readStatus.consumedDataSize = {records.status.consumedDataSize} bytes" + ) + if records.status.hasDataToRead: + print_verbose( + f" Records remain on the instance, {api_name}() has to be called again" + ) + return records + + +def print_info_log_records(records, first_record_number): + """Print the metadata and the start of the data of collected info log records""" + print_verbose("-" * 80) + for j in range(records.record_count): + record = records.descriptors[j] + print_verbose(f"\nEvent #{first_record_number + j}:") + print_verbose(f" Timestamp: {record.timestamp} nano seconds") + print_verbose( + f" BDF: {record.address.domain:04x}:{record.address.bus:02x}:" + f"{record.address.device:02x}.{record.address.function:x}" + ) + print_verbose( + f" Platform ID: {'-'.join(f'{record.uuid.id[k]:02x}' for k in range(16))}" + ) + print_verbose( + f" Record Type: {get_info_log_record_type_string(record.recordType)}" + ) + print_verbose(f" Data Size: {record.lengthOfData} bytes") + print_verbose(f" Data Offset: {record.offset}") + start = record.offset + end = min(start + record.lengthOfData, records.size) + stop = min(end, start + 64) + data = bytes(records.buffer[start:stop]) + suffix = " ..." if end - start > 64 else "" + print_verbose(f" CPER Data: {data.hex(' ')}{suffix}") + print_verbose("-" * 80) + + +def drain_info_log_records( + collect_fn, api_name, instance_handle, timeout, first_record_number +): + """Keep collecting while the driver reports more data. Returns (records collected, success).""" + collected = 0 + for _ in range(INFO_LOG_MAX_DRAIN_CALLS): + records = collect_info_log_records( + collect_fn, api_name, instance_handle, timeout + ) + if records is None: + return collected, False + if records.record_count == 0: + break + print_info_log_records(records, first_record_number + collected) + collected += records.record_count + if not records.status.hasDataToRead: + break + return collected, True + + +def records_match(first, second): + """Check that the first record of two collections has the same data""" + if first.record_count == 0 or second.record_count == 0: + return False + a = first.descriptors[0] + b = second.descriptors[0] + if a.lengthOfData != b.lengthOfData: + return False + a_start, a_end = a.offset, a.offset + a.lengthOfData + b_start, b_end = b.offset, b.offset + b.lengthOfData + return bytes(first.buffer[a_start:a_end]) == bytes(second.buffer[b_start:b_end]) + + +def get_info_log_properties(info_log_handle): + """Return the properties of an info log, or None on failure""" + props = pz.zes_info_log_ext_properties_t() + props.stype = pz.ZES_STRUCTURE_TYPE_INFO_LOG_EXT_PROPERTIES + props.pNext = None + rc = pz.zesInfoLogGetPropertiesExt(info_log_handle, byref(props)) + if not check_rc("zesInfoLogGetPropertiesExt", rc): + return None + return props + + +def create_info_log_instance(info_log_handle, instance_name, buffer_size_kb): + """Create a collection instance, unset options use the default buffer and buffer size""" + desc = pz.zes_info_log_instance_ext_desc_t() + desc.stype = pz.ZES_STRUCTURE_TYPE_INFO_LOG_INSTANCE_EXT_DESC + desc.pNext = None + buffer_size = None + if buffer_size_kb is not None: + buffer_size = c_uint32(buffer_size_kb) + desc.pBufferSizeInKb = pointer(buffer_size) + print_verbose(f" requested buffer size: {buffer_size_kb} KB") + else: + desc.pBufferSizeInKb = None + + name = instance_name.encode() if instance_name is not None else None + instance_handle = pz.zes_info_log_instance_handle_t() + rc = pz.zesInfoLogCreateInstanceExt( + info_log_handle, name, byref(desc), byref(instance_handle) ) - if rc == pz.ZE_RESULT_WARNING_DROPPED_DATA: - print_verbose(f" Dropped Records: {status.droppedRecordCount}") - elif not check_rc(f"{api_name}(info log {info_log_index}, data)", rc): + if not check_rc("zesInfoLogCreateInstanceExt", rc): return None - return record_count.value, size.value, buffer, descriptors, status + # The descriptor members are updated in place with the values which were actually applied + if buffer_size is not None: + print_verbose(f" applied buffer size: {buffer_size.value} KB") + return instance_handle -def print_info_log_records(action, record_count, size, descriptors, status): - """Print the metadata of collected info log records and the read status""" - print_verbose(f" {action} {record_count} record(s), {size} bytes:") - for j in range(record_count): - record = descriptors[j] - print_verbose(f" Record {j}:") +def test_info_log_instance_read_peek(info_log_handle, props, timeout): + """Peek twice, read and drain the records of a named instance and check the results""" + print("\n---- Info Log instance read and peek tests ----") + + if not props.isNamedInstanceSupported: print_verbose( - f" Type: {get_info_log_record_type_string(record.recordType)}" + "Named collection instances are not supported. Skipping the test." + ) + return + if not props.isPeekDataSupported: + print_verbose("Peek is not supported. Skipping the test.") + return + + instance_handle = create_info_log_instance( + info_log_handle, INFO_LOG_INSTANCE_NAME, None + ) + if instance_handle is None: + return + print_verbose(f"Created collection instance '{INFO_LOG_INSTANCE_NAME}'") + + # Only records generated after the instance was created are collected into it, so the + # instance is polled with the non-consuming query until it reports data + print_verbose( + f"\nWaiting up to {INFO_LOG_RECORD_WAIT_MS} ms for CPER records. Records are generated " + "when the uncorrectable RAS counters are read, for example with " + "'zello_sysman -L --instanceapi' " + "from another shell." + ) + peek_name = "zesInfoLogInstancePeekWithMetadataExt" + read_name = "zesInfoLogInstanceReadWithMetadataExt" + pending_size = 0 + pending_count = 0 + for _ in range(0, INFO_LOG_RECORD_WAIT_MS, INFO_LOG_POLL_INTERVAL_MS): + rc, pending_size, pending_count = query_info_log_records( + pz.zesInfoLogInstancePeekWithMetadataExt, instance_handle, timeout ) + if not check_rc(f"{peek_name}(query)", rc) or pending_count != 0: + break + time.sleep(INFO_LOG_POLL_INTERVAL_MS / 1000) + + if pending_count == 0: print_verbose( - f" BDF: {record.address.domain:04X}:{record.address.bus:02X}:" - f"{record.address.device:02X}.{record.address.function:X}" + f"No CPER record was collected within {INFO_LOG_RECORD_WAIT_MS} ms. " + "Skipping the read and peek verification." + ) + check_rc( + "zesInfoLogInstanceDeleteExt", + pz.zesInfoLogInstanceDeleteExt(instance_handle), + ) + return + print_verbose( + f"{pending_count} record(s), {pending_size} byte(s) are pending on the instance" + ) + + check = InfoLogChecks() + + print_verbose("\nFirst peek:") + first_peek = collect_info_log_records( + pz.zesInfoLogInstancePeekWithMetadataExt, peek_name, instance_handle, timeout + ) + check( + first_peek is not None and first_peek.record_count != 0, + "peek returned the pending records", + ) + if first_peek is None: + first_peek = InfoLogRecords() + if first_peek.record_count != 0: + print_info_log_records(first_peek, 1) + + print_verbose("\nSecond peek, on the same records:") + second_peek = collect_info_log_records( + pz.zesInfoLogInstancePeekWithMetadataExt, peek_name, instance_handle, timeout + ) + check( + second_peek is not None + and second_peek.record_count == first_peek.record_count + and second_peek.size == first_peek.size, + "a second peek reports the same records, peek does not consume", + ) + + print_verbose("\nRead, on the records the peek left in place:") + read = collect_info_log_records( + pz.zesInfoLogInstanceReadWithMetadataExt, read_name, instance_handle, timeout + ) + check( + read is not None and read.record_count >= first_peek.record_count, + "read returned at least the records the peek reported", + ) + if read is None: + read = InfoLogRecords() + if read.record_count != 0: + print_info_log_records(read, 1) + + check( + records_match(first_peek, read), + "the first record the read returned is byte identical to the peeked one", + ) + + if read.status is not None and read.status.hasDataToRead: + drained, success = drain_info_log_records( + pz.zesInfoLogInstanceReadWithMetadataExt, + read_name, + instance_handle, + timeout, + read.record_count + 1, ) - print_verbose(f" Timestamp: {record.timestamp} ns") - print_verbose(f" Offset: {record.offset}") - print_verbose(f" Length: {record.lengthOfData} bytes") - print_verbose(f" Consumed Data Size: {status.consumedDataSize} bytes") - print_verbose(f" Has Data To Read: {bool(status.hasDataToRead)}") + print_verbose(f"{drained} further record(s) were read to drain the instance") + check(success, "the reads which drained the remaining records succeeded") + + rc, pending_size, pending_count = query_info_log_records( + pz.zesInfoLogInstanceReadWithMetadataExt, instance_handle, timeout + ) + check( + rc == pz.ZE_RESULT_SUCCESS and pending_count == 0, + "the instance is drained after the read, nothing is pending", + ) + + print_verbose("") + check.print_summary() + + rc = pz.zesInfoLogInstanceDeleteExt(instance_handle) + if check_rc("zesInfoLogInstanceDeleteExt", rc): + print_verbose(f"Deleted collection instance '{INFO_LOG_INSTANCE_NAME}'") + + +def test_info_log_instance_collect( + info_log_handle, props, instance_name, buffer_size_kb, timeout, use_peek +): + """Peek or read the records collected into an instance and check the results""" + operation = "peek" if use_peek else "read" + print(f"\n---- Info Log instance {operation} tests ----") + + if use_peek and not props.isPeekDataSupported: + print_verbose("Peek is not supported. Skipping the test.") + return + if instance_name is not None and not props.isNamedInstanceSupported: + print_verbose( + "Named collection instances are not supported. Skipping the test." + ) + return + + print_verbose( + f"Collection instance: {instance_name if instance_name is not None else 'the default buffer'}" + ) + instance_handle = create_info_log_instance( + info_log_handle, instance_name, buffer_size_kb + ) + if instance_handle is None: + return + + if use_peek: + collect_fn = pz.zesInfoLogInstancePeekWithMetadataExt + api_name = "zesInfoLogInstancePeekWithMetadataExt" + else: + collect_fn = pz.zesInfoLogInstanceReadWithMetadataExt + api_name = "zesInfoLogInstanceReadWithMetadataExt" + + check = InfoLogChecks() + records = collect_info_log_records(collect_fn, api_name, instance_handle, timeout) + if records is not None and records.record_count == 0: + print_verbose( + "No record is pending on the instance. Records are generated when the " + "uncorrectable RAS counters are read, for example with 'zello_sysman -L --instanceapi'." + ) + elif records is not None: + print_info_log_records(records, 1) + if use_peek: + # The peek left the records in place, so the same totals must still be reported + rc, pending_size, pending_count = query_info_log_records( + collect_fn, instance_handle, timeout + ) + check( + rc == pz.ZE_RESULT_SUCCESS + and pending_count == records.record_count + and pending_size == records.size, + "the peeked records are still pending on the instance", + ) + else: + if records.status.hasDataToRead: + drained, success = drain_info_log_records( + collect_fn, + api_name, + instance_handle, + timeout, + records.record_count + 1, + ) + print_verbose( + f"{drained} further record(s) were read to drain the instance" + ) + check( + success, "the reads which drained the remaining records succeeded" + ) + rc, pending_size, pending_count = query_info_log_records( + collect_fn, instance_handle, timeout + ) + check( + rc == pz.ZE_RESULT_SUCCESS and pending_count == 0, + "the read consumed the records, nothing is pending", + ) + print_verbose("") + check.print_summary() + + rc = pz.zesInfoLogInstanceDeleteExt(instance_handle) + if check_rc("zesInfoLogInstanceDeleteExt", rc): + print_verbose("Deleted collection instance") def test_info_log_module( driver_handle, driver_index, + mode=None, instance_name=None, buffer_size_kb=None, timeout=INFO_LOG_READ_TIMEOUT_MS, - read_records=False, ): - """Test info log enumeration, properties, and collection instance operations""" - print(f"\n---- Driver {driver_index} Info Log Test ----") + """Test info log properties, and with a mode the collection instance operations. + + mode is None for properties only, "api" to verify peek and read on a named instance, + "peek" to peek or "read" to read the records of an instance. + """ + print(f"\n---- Driver {driver_index} Info Log properties tests ----") info_log_count = c_uint32(0) rc = pz.zesDriverEnumInfoLogsExt(driver_handle, byref(info_log_count), None) @@ -958,10 +1316,10 @@ def test_info_log_module( return False if info_log_count.value == 0: - print_verbose("No info logs found on this driver") + print_verbose("Could not retrieve Info Log handles") return True - print_verbose(f"Found {info_log_count.value} info log(s)") + print_verbose(f"Found {info_log_count.value} info log handles..") InfoLogArray = pz.zes_info_log_handle_t * info_log_count.value info_log_handles = InfoLogArray() @@ -972,127 +1330,58 @@ def test_info_log_module( if not check_rc(f"zesDriverEnumInfoLogsExt(driver {driver_index}, handles)", rc): return False + cper_handle = None + cper_props = None for i in range(info_log_count.value): - print_verbose(f"\n Info Log {i}:") - - props = pz.zes_info_log_ext_properties_t() - props.stype = pz.ZES_STRUCTURE_TYPE_INFO_LOG_EXT_PROPERTIES - props.pNext = None - - rc = pz.zesInfoLogGetPropertiesExt(info_log_handles[i], byref(props)) - if not check_rc(f"zesInfoLogGetPropertiesExt(info log {i})", rc): + props = get_info_log_properties(info_log_handles[i]) + if props is None: continue - - print_verbose(f" Type: {get_info_log_type_string(props.infoLogType)}") - print_verbose(f" Format: {get_info_log_format_string(props.infoLogFormat)}") print_verbose( - f" Named Instance Supported: {bool(props.isNamedInstanceSupported)}" + f"properties.infoLogType = {get_info_log_type_string(props.infoLogType)}" ) - print_verbose(f" Peek Data Supported: {bool(props.isPeekDataSupported)}") - - if instance_name is not None and not props.isNamedInstanceSupported: - print_verbose( - f" Skipping named instance '{instance_name}' since named instances are not supported" - ) - continue - - # Unset options use the default buffer and default buffer size - desc = pz.zes_info_log_instance_ext_desc_t() - desc.stype = pz.ZES_STRUCTURE_TYPE_INFO_LOG_INSTANCE_EXT_DESC - desc.pNext = None - buffer_size = None - if buffer_size_kb is not None: - buffer_size = c_uint32(buffer_size_kb) - desc.pBufferSizeInKb = pointer(buffer_size) - else: - desc.pBufferSizeInKb = None - - name = instance_name.encode() if instance_name is not None else None - instance_handle = pz.zes_info_log_instance_handle_t() - rc = pz.zesInfoLogCreateInstanceExt( - info_log_handles[i], name, byref(desc), byref(instance_handle) + print_verbose( + f"properties.infoLogFormat = {get_info_log_format_string(props.infoLogFormat)}" ) - if rc != pz.ZE_RESULT_SUCCESS: - print_verbose( - f" Collection Instance: Not available ({get_result_string(rc)})" - ) - continue - - label = ( - f"'{instance_name}'" - if instance_name is not None - else "on the default buffer" + print_verbose( + f"properties.isNamedInstanceSupported = {bool(props.isNamedInstanceSupported)}" ) - print_verbose(f" Created collection instance {label} successfully") - if buffer_size is not None: - print_verbose( - f" Buffer Size: requested {buffer_size_kb} KB, applied {buffer_size.value} KB" - ) + print_verbose( + f"properties.isPeekDataSupported = {bool(props.isPeekDataSupported)}" + ) + if ( + cper_handle is None + and props.infoLogFormat == pz.ZES_INFO_LOG_FORMAT_EXT_CPER + ): + cper_handle = info_log_handles[i] + cper_props = props - peeked_count = None - if props.isPeekDataSupported: - peeked = collect_info_log_records( - pz.zesInfoLogInstancePeekWithMetadataExt, - "zesInfoLogInstancePeekWithMetadataExt", - instance_handle, - timeout, - i, - ) - if peeked is not None: - peeked_count = peeked[0] - if peeked_count > 0: - print_info_log_records( - "Peeked", peeked[0], peeked[1], peeked[3], peeked[4] - ) + if mode is None: + print_verbose( + "\nRecords are collected with --instance-api, --instance-peek or --instance-read" + ) + return True - if read_records: - # Records are consumed, so keep reading while the driver reports more data - read_count = 0 - while True: - read = collect_info_log_records( - pz.zesInfoLogInstanceReadWithMetadataExt, - "zesInfoLogInstanceReadWithMetadataExt", - instance_handle, - timeout, - i, - ) - if read is None or read[0] == 0: - break - read_count += read[0] - print_info_log_records("Read", read[0], read[1], read[3], read[4]) - if not read[4].hasDataToRead: - break - - print_verbose(f" Total Records Read: {read_count}") - if peeked_count is not None and peeked_count != read_count: - print_verbose( - f" Note: peek returned {peeked_count} record(s) and read returned {read_count}, " - "records may have arrived between the calls" - ) - else: - # Only a query call is made, so records in the shared default buffer are not consumed - size = c_uint32(0) - record_count = c_uint32(0) - rc = pz.zesInfoLogInstanceReadWithMetadataExt( - instance_handle, - timeout, - byref(size), - None, - byref(record_count), - None, - None, - ) - if check_rc( - f"zesInfoLogInstanceReadWithMetadataExt(info log {i}, query)", rc - ): - print_verbose( - f" Read Query: {record_count.value} record(s), {size.value} bytes" - ) + if not is_root_user(): + print_verbose( + "Not running as Root. Skipping the info log collection instance test." + ) + return True - rc = pz.zesInfoLogInstanceDeleteExt(instance_handle) - if check_rc(f"zesInfoLogInstanceDeleteExt(info log {i})", rc): - print_verbose(" Deleted collection instance successfully") + if cper_handle is None: + print_verbose("No info log handle reports the CPER format") + return True + if mode == "api": + test_info_log_instance_read_peek(cper_handle, cper_props, timeout) + else: + test_info_log_instance_collect( + cper_handle, + cper_props, + instance_name, + buffer_size_kb, + timeout, + use_peek=(mode == "peek"), + ) return True @@ -1672,9 +1961,9 @@ def main(): %(prog)s -f # Frequency tests only %(prog)s -t # Temperature tests only %(prog)s -e # Engine tests only - %(prog)s -l # Info log tests only - %(prog)s -l --instance-read --timeout 5000 # Info log tests, peek then consume all records - %(prog)s -l --instance mylog --buffer-size 1024 # Info log tests on a named 1 MB collection instance + %(prog)s -l # Info log properties tests only + %(prog)s -l --instance-api # Info log tests, verify peek and read on a named collection instance + %(prog)s -l --instance-read --instance mylog --buffer-size 1024 # Info log tests, read the records of a named 1 MB collection instance %(prog)s -h # Show help message""", formatter_class=argparse.RawDescriptionHelpFormatter, ) @@ -1713,18 +2002,43 @@ def main(): ) parser.add_argument("-e", "--engine", action="store_true", help="Run engine tests ") parser.add_argument( - "-l", "--infolog", action="store_true", help="Run only info log tests" + "-l", + "--infolog", + action="store_true", + help="Run only info log tests, reporting what each info log supports", + ) + instance_mode = parser.add_mutually_exclusive_group() + instance_mode.add_argument( + "--instance-api", + action="store_const", + const="api", + dest="instance_mode", + help="With -l, create a named collection instance, wait for CPER records, then verify peek and read (requires root)", + ) + instance_mode.add_argument( + "--instance-peek", + action="store_const", + const="peek", + dest="instance_mode", + help="With -l, create a collection instance and peek the records (requires root)", + ) + instance_mode.add_argument( + "--instance-read", + action="store_const", + const="read", + dest="instance_mode", + help="With -l, create a collection instance and read the records (requires root)", ) parser.add_argument( "--instance", metavar="NAME", - help="With -l, collect into a named instance instead of the default buffer", + help="With --instance-peek/--instance-read, collect into a named instance instead of the default buffer", ) parser.add_argument( "--buffer-size", type=int, metavar="KB", - help="With -l, request a collection buffer size in kilobytes (0 reports the default size)", + help="With --instance-peek/--instance-read, request a total collection buffer size in kilobytes", ) parser.add_argument( "--timeout", @@ -1733,15 +2047,16 @@ def main(): metavar="MS", help=f"With -l, maximum time in milliseconds to search for records, default is {INFO_LOG_READ_TIMEOUT_MS}", ) - parser.add_argument( - "--instance-read", - action="store_true", - help="With -l, read and consume all collected records after peeking them", - ) args = parser.parse_args() - if args.buffer_size is not None and args.buffer_size < 0: - parser.error("--buffer-size must be a non-negative integer") + if args.buffer_size is not None and args.buffer_size <= 0: + parser.error("--buffer-size must be a positive integer") + if ( + args.instance is not None or args.buffer_size is not None + ) and args.instance_mode not in ("peek", "read"): + parser.error( + "--instance and --buffer-size are used with --instance-peek or --instance-read" + ) if args.timeout < 0: parser.error("--timeout must be a non-negative integer") @@ -1788,10 +2103,10 @@ def main(): test_info_log_module( drivers[driver_idx], driver_idx, + mode=args.instance_mode, instance_name=args.instance, buffer_size_kb=args.buffer_size, timeout=args.timeout, - read_records=args.instance_read, ) # Run selected tests on all devices From 8b37dbd67d324644639fe1e640367116fe41e1a3 Mon Sep 17 00:00:00 2001 From: Aviral Nigam Date: Thu, 8 Oct 2026 12:07:03 +0530 Subject: [PATCH 3/7] fix: Propagate info log test failures in pyzes black box test A failed zesDriverEnumInfoLogsExt call printed an error but both -a and -l still exited with status 0. Aggregate the info log test result into the exit status. Info logs are driver scoped, so also run them when device enumeration fails or returns no devices. Related-To: SYSM-432 Signed-off-by: Aviral Nigam --- .../pyzes/examples/pyzes_black_box_test.py | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py b/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py index cf589f13..b2beba7a 100755 --- a/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py +++ b/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py @@ -1899,16 +1899,17 @@ def run_all_tests(): return False drivers, driver_count = driver_info + success = True # Test each driver for driver_idx in range(driver_count): print(f"\n=== Driver {driver_idx} Tests ===") - # Get devices for this driver + # Get devices for this driver, driver scoped tests still run without them device_info = get_devices(drivers[driver_idx]) if device_info is None: print(f"No devices found for driver {driver_idx}") - continue + device_info = (None, 0) devices, device_count = device_info @@ -1941,10 +1942,11 @@ def run_all_tests(): test_engine_modules(devices[device_idx], device_idx) # Test info logs (driver scoped) - test_info_log_module(drivers[driver_idx], driver_idx) + if not test_info_log_module(drivers[driver_idx], driver_idx): + success = False print("\n=== Test Completed ===") - return True + return success def main(): @@ -2093,21 +2095,24 @@ def main(): print("No drivers available for testing") return 1 - if not devices or device_count == 0: - print("No devices available for testing") - return 1 + success = True - # Info logs are driver scoped, so run them once per driver + # Info logs are driver scoped, so run them once per driver before the device check if args.infolog: for driver_idx in range(driver_count): - test_info_log_module( + if not test_info_log_module( drivers[driver_idx], driver_idx, mode=args.instance_mode, instance_name=args.instance, buffer_size_kb=args.buffer_size, timeout=args.timeout, - ) + ): + success = False + + if not devices or device_count == 0: + print("No devices available for testing") + return 1 # Run selected tests on all devices for device_idx in range(device_count): @@ -2135,8 +2140,6 @@ def main(): if args.temperature: test_temperature_sensors(devices[device_idx], device_idx) - success = True - return 0 if success else 1 except Exception as e: From 41f8bcb821b724658557c282cb650113b871a713 Mon Sep 17 00:00:00 2001 From: Aviral Nigam Date: Thu, 8 Oct 2026 12:25:56 +0530 Subject: [PATCH 4/7] fix: Accept dropped data warning on info log query calls in pyzes black box test ZE_RESULT_WARNING_DROPPED_DATA can be returned by a query call and still comes with a valid size and record count. The query helper passed no read status and its callers failed on the warning. Pass a read status, report the dropped record count and treat the warning as success, as queryInfoLogRecords in zello_sysman does. Related-To: SYSM-432 Signed-off-by: Aviral Nigam --- .../pyzes/examples/pyzes_black_box_test.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py b/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py index b2beba7a..1d723ee5 100755 --- a/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py +++ b/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py @@ -911,13 +911,31 @@ def query_info_log_records(collect_fn, instance_handle, timeout): """Query the records held by the instance without consuming them. A call with *pSize and *pRecordCount zero on input is a query call for both read and peek. + ZE_RESULT_WARNING_DROPPED_DATA still returns a valid query result, so it is reported as success. Returns (rc, size, record_count). """ size = c_uint32(0) record_count = c_uint32(0) + status = pz.zes_info_log_read_status_ext_t() + status.stype = pz.ZES_STRUCTURE_TYPE_INFO_LOG_READ_STATUS_EXT + status.pNext = None rc = collect_fn( - instance_handle, timeout, byref(size), None, byref(record_count), None, None + instance_handle, + timeout, + byref(size), + None, + byref(record_count), + None, + byref(status), ) + if rc == pz.ZE_RESULT_WARNING_DROPPED_DATA: + dropped = status.droppedRecordCount + if dropped == -1: + dropped = "unknown" + print_verbose( + f" query: records were dropped, readStatus.droppedRecordCount = {dropped}" + ) + rc = pz.ZE_RESULT_SUCCESS return rc, size.value, record_count.value From 7003d8db35d178b10810da686201065c7fdb7def Mon Sep 17 00:00:00 2001 From: Aviral Nigam Date: Thu, 8 Oct 2026 18:54:25 +0530 Subject: [PATCH 5/7] fix: Use zello_sysman option names for info log tests in pyzes black box test zello_sysman runs the info log test with -L and takes --instanceapi, --instancepeek, --instanceread and --buffersize. Use the same spellings instead of -l, --instance-api, --instance-peek, --instance-read and --buffer-size. Related-To: SYSM-432 Signed-off-by: Aviral Nigam --- .../pyzes/examples/pyzes_black_box_test.py | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py b/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py index 1d723ee5..509df3a5 100755 --- a/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py +++ b/bindings/sysman/python/source/pyzes/examples/pyzes_black_box_test.py @@ -1375,7 +1375,7 @@ def test_info_log_module( if mode is None: print_verbose( - "\nRecords are collected with --instance-api, --instance-peek or --instance-read" + "\nRecords are collected with --instanceapi, --instancepeek or --instanceread" ) return True @@ -1981,9 +1981,9 @@ def main(): %(prog)s -f # Frequency tests only %(prog)s -t # Temperature tests only %(prog)s -e # Engine tests only - %(prog)s -l # Info log properties tests only - %(prog)s -l --instance-api # Info log tests, verify peek and read on a named collection instance - %(prog)s -l --instance-read --instance mylog --buffer-size 1024 # Info log tests, read the records of a named 1 MB collection instance + %(prog)s -L # Info log properties tests only + %(prog)s -L --instanceapi # Info log tests, verify peek and read on a named collection instance + %(prog)s -L --instanceread --instance mylog --buffersize 1024 # Info log tests, read the records of a named 1 MB collection instance %(prog)s -h # Show help message""", formatter_class=argparse.RawDescriptionHelpFormatter, ) @@ -2022,60 +2022,60 @@ def main(): ) parser.add_argument("-e", "--engine", action="store_true", help="Run engine tests ") parser.add_argument( - "-l", + "-L", "--infolog", action="store_true", help="Run only info log tests, reporting what each info log supports", ) instance_mode = parser.add_mutually_exclusive_group() instance_mode.add_argument( - "--instance-api", + "--instanceapi", action="store_const", const="api", dest="instance_mode", - help="With -l, create a named collection instance, wait for CPER records, then verify peek and read (requires root)", + help="With -L, create a named collection instance, wait for CPER records, then verify peek and read (requires root)", ) instance_mode.add_argument( - "--instance-peek", + "--instancepeek", action="store_const", const="peek", dest="instance_mode", - help="With -l, create a collection instance and peek the records (requires root)", + help="With -L, create a collection instance and peek the records (requires root)", ) instance_mode.add_argument( - "--instance-read", + "--instanceread", action="store_const", const="read", dest="instance_mode", - help="With -l, create a collection instance and read the records (requires root)", + help="With -L, create a collection instance and read the records (requires root)", ) parser.add_argument( "--instance", metavar="NAME", - help="With --instance-peek/--instance-read, collect into a named instance instead of the default buffer", + help="With --instancepeek/--instanceread, collect into a named instance instead of the default buffer", ) parser.add_argument( - "--buffer-size", + "--buffersize", type=int, metavar="KB", - help="With --instance-peek/--instance-read, request a total collection buffer size in kilobytes", + help="With --instancepeek/--instanceread, request a total collection buffer size in kilobytes", ) parser.add_argument( "--timeout", type=int, default=INFO_LOG_READ_TIMEOUT_MS, metavar="MS", - help=f"With -l, maximum time in milliseconds to search for records, default is {INFO_LOG_READ_TIMEOUT_MS}", + help=f"With -L, maximum time in milliseconds to search for records, default is {INFO_LOG_READ_TIMEOUT_MS}", ) args = parser.parse_args() - if args.buffer_size is not None and args.buffer_size <= 0: - parser.error("--buffer-size must be a positive integer") + if args.buffersize is not None and args.buffersize <= 0: + parser.error("--buffersize must be a positive integer") if ( - args.instance is not None or args.buffer_size is not None + args.instance is not None or args.buffersize is not None ) and args.instance_mode not in ("peek", "read"): parser.error( - "--instance and --buffer-size are used with --instance-peek or --instance-read" + "--instance and --buffersize are used with --instancepeek or --instanceread" ) if args.timeout < 0: parser.error("--timeout must be a non-negative integer") @@ -2123,7 +2123,7 @@ def main(): driver_idx, mode=args.instance_mode, instance_name=args.instance, - buffer_size_kb=args.buffer_size, + buffer_size_kb=args.buffersize, timeout=args.timeout, ): success = False From 3e1ef1f3c68909b557308e3cad17f4898f5af98a Mon Sep 17 00:00:00 2001 From: Aviral Nigam Date: Fri, 9 Oct 2026 10:05:06 +0530 Subject: [PATCH 6/7] feature: Add pyzes support for info log APIs Add python bindings, unit tests, black box test coverage and README entries for: 1. zesDriverEnumInfoLogsExt 2. zesInfoLogGetPropertiesExt 3. zesInfoLogCreateInstanceExt 4. zesInfoLogInstanceReadWithMetadataExt 5. zesInfoLogInstancePeekWithMetadataExt 6. zesInfoLogInstanceDeleteExt Related-To: SYSM-432 Signed-off-by: Aviral Nigam From 7deff11a18e9d5b4253788435bbb20618e87e7ec Mon Sep 17 00:00:00 2001 From: Aviral Nigam Date: Tue, 13 Oct 2026 16:30:40 +0530 Subject: [PATCH 7/7] feature: Add pyzes support for info log APIs Add python bindings, unit tests, black box test coverage and README entries for: 1. zesDriverEnumInfoLogsExt 2. zesInfoLogGetPropertiesExt 3. zesInfoLogCreateInstanceExt 4. zesInfoLogInstanceReadWithMetadataExt 5. zesInfoLogInstancePeekWithMetadataExt 6. zesInfoLogInstanceDeleteExt Related-To: SYSM-432 Signed-off-by: Aviral Nigam --- bindings/sysman/python/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bindings/sysman/python/README.md b/bindings/sysman/python/README.md index 9d0cae44..3bba1dee 100644 --- a/bindings/sysman/python/README.md +++ b/bindings/sysman/python/README.md @@ -205,12 +205,12 @@ SUPPORTED APIs | `zesDeviceGetEccState` | ECC | 0.1.2 | None | | `zesDeviceSetEccState` | ECC | 0.1.2 | None | | **Info Log Management** |-|-|-| -| `zesDriverEnumInfoLogsExt` | Info Log | 0.1.3 | None | +| `zesDriverEnumInfoLogsExt` | Info Log | 0.1.3 | Linux: Requires superuser | | `zesInfoLogGetPropertiesExt` | Info Log | 0.1.3 | None | -| `zesInfoLogCreateInstanceExt` | Info Log | 0.1.3 | None | -| `zesInfoLogInstanceReadWithMetadataExt` | Info Log | 0.1.3 | Records returned are consumed | -| `zesInfoLogInstancePeekWithMetadataExt` | Info Log | 0.1.3 | Requires `isPeekDataSupported` | -| `zesInfoLogInstanceDeleteExt` | Info Log | 0.1.3 | None | +| `zesInfoLogCreateInstanceExt` | Info Log | 0.1.3 | Linux: Requires superuser | +| `zesInfoLogInstanceReadWithMetadataExt` | Info Log | 0.1.3 | Records returned are consumed. Linux: Requires superuser | +| `zesInfoLogInstancePeekWithMetadataExt` | Info Log | 0.1.3 | Requires `isPeekDataSupported`. Linux: Requires superuser | +| `zesInfoLogInstanceDeleteExt` | Info Log | 0.1.3 | Linux: Requires superuser | RELEASE NOTES -------------