Info Logs Extension#
Introduced in Level Zero version 1.19
API#
Enumerations
Handles
Structures
Functions
Macros
ZES_INFO_LOGS_EXT_NAME
ZES_INFO_LOG_FIELD_DESC_SIZE
Info Logs Overview#
An info log is a stream of records reported by the driver, where each record describes an error or another event of interest that occurred on one of the devices managed by that driver.
Info logs are driver scoped: a single info log may carry records generated by any of the devices managed by the driver. The device that generated a record is identified by the PCI address and the UUID reported in the metadata of that record.
The format of the records is reported by zesInfoLogGetPropertiesExt. Records in
:ref:`ZES_INFO_LOG_FORMAT_EXT_CPER <zes-info-log-format-ext-t>`\ format are UEFI Common Platform Error Records; the
extension returns them as opaque binary data and the application is responsible for
decoding them.
Where the driver can decode that format, an application can have it decode named fields out of those records instead of parsing the record format itself, as described in Info Log Decoder Overview below.
Enumerating Info Logs and Querying Properties#
uint32_t logCount = 0; zesDriverEnumInfoLogsExt(hDriver, &logCount, nullptr); if (logCount == 0) { output("No info logs available\n"); return; } zes_info_log_handle_t* phInfoLogs = (zes_info_log_handle_t*) allocate(logCount * sizeof(zes_info_log_handle_t)); zesDriverEnumInfoLogsExt(hDriver, &logCount, phInfoLogs); zes_info_log_ext_properties_t logProps = {}; logProps.stype = ZES_STRUCTURE_TYPE_INFO_LOG_EXT_PROPERTIES; logProps.pNext = nullptr; zesInfoLogGetPropertiesExt(phInfoLogs[0], &logProps); output("Info log type: %u, format: %u\n", logProps.infoLogType, logProps.infoLogFormat); output("Named collection instances supported: %s\n", logProps.isNamedInstanceSupported ? "yes" : "no"); output("Peek supported: %s\n", logProps.isPeekDataSupported ? "yes" : "no");
Collecting and Reading Records#
uint32_t bufferSizeInKb = 256; zes_info_log_instance_ext_desc_t desc = {}; desc.stype = ZES_STRUCTURE_TYPE_INFO_LOG_INSTANCE_EXT_DESC; desc.pNext = nullptr; desc.pBufferSizeInKb = &bufferSizeInKb; const char* pInstanceName = logProps.isNamedInstanceSupported ? "my_collection" : nullptr; zes_info_log_instance_handle_t hInstance = nullptr; if (zesInfoLogCreateInstanceExt(phInfoLogs[0], pInstanceName, &desc, &hInstance) != ZE_RESULT_SUCCESS) { output("Could not start info log collection\n"); return; } // The driver may round the requested values; re-read what was applied output("Collecting into %u KB\n", bufferSizeInKb); const uint64_t timeoutInMs = 500; const uint32_t bufferSize = 64 * 1024; const uint32_t maxRecords = 64; uint8_t* pBuffer = (uint8_t*) allocate(bufferSize); zes_info_log_metadata_ext_t* pDescriptors = (zes_info_log_metadata_ext_t*) allocate(maxRecords * sizeof(zes_info_log_metadata_ext_t)); ze_bool_t hasDataToRead = true; while (hasDataToRead) { uint32_t size = bufferSize; uint32_t recordCount = maxRecords; for (uint32_t i = 0; i < maxRecords; i++) { pDescriptors[i].stype = ZES_STRUCTURE_TYPE_INFO_LOG_METADATA_EXT; pDescriptors[i].pNext = nullptr; } zes_info_log_read_status_ext_t readStatus = {}; readStatus.stype = ZES_STRUCTURE_TYPE_INFO_LOG_READ_STATUS_EXT; readStatus.pNext = nullptr; ze_result_t result = zesInfoLogInstanceReadWithMetadataExt( hInstance, timeoutInMs, &size, pBuffer, &recordCount, pDescriptors, &readStatus); if (result != ZE_RESULT_SUCCESS && result != ZE_RESULT_WARNING_DROPPED_DATA) { output("Could not read info log records\n"); break; } if (result == ZE_RESULT_WARNING_DROPPED_DATA) { // The collection buffer overflowed. Consider a larger pBufferSizeInKb // or reading more often. if (readStatus.droppedRecordCount == -1) { output("Warning: records were dropped, count unknown\n"); } else { output("Warning: %lld records dropped\n", (long long) readStatus.droppedRecordCount); } } for (uint32_t i = 0; i < recordCount; i++) { output("Record %u: device %04x:%02x:%02x.%x, timestamp %llu ns, %u bytes\n", i, pDescriptors[i].address.domain, pDescriptors[i].address.bus, pDescriptors[i].address.device, pDescriptors[i].address.function, pDescriptors[i].timestamp, pDescriptors[i].lengthOfData); switch (pDescriptors[i].recordType) { case ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_RECOVERABLE: escalate(&pDescriptors[i]); break; case ZES_INFO_LOG_RECORD_TYPE_EXT_ERROR_CORRECTED: case ZES_INFO_LOG_RECORD_TYPE_EXT_INFORMATIONAL: break; default: // Includes values added by a later version of this extension break; } const uint8_t* pRecord = pBuffer + pDescriptors[i].offset; process(pRecord, pDescriptors[i].lengthOfData); } hasDataToRead = readStatus.hasDataToRead; } free(pDescriptors); free(pBuffer); zesInfoLogInstanceDeleteExt(hInstance); free(phInfoLogs);
Info Log Decoder Overview#
The functions described above return records as opaque binary data: a record in ZES_INFO_LOG_FORMAT_EXT_CPER format is a UEFI Common Platform Error Record.
A decoder context reports the fields it can decode from a record, and reads the value of one of those fields out of one record.
Whether the decoder is available depends on the format of the info log: zesInfoLogDecoderContextCreateExt returns ZE_RESULT_ERROR_UNSUPPORTED_FEATURE for an info log whose format the driver cannot decode, so an application must check the result of that call rather than assume that every info log can be decoded.
A decoder context is created from an info log handle. One context decodes records read from any collection instance of the info log, and records held in a buffer whose collection instance was already deleted.
Fields are decoded on demand, so the context reads the record buffer and the descriptor array for as long as it is decoding. The application must keep both allocated, and must not modify either, until zesInfoLogDecoderContextDecodeStopExt returns.
A record that carries no field is not an error: every field of such a record is reported as ZE_RESULT_ERROR_NOT_AVAILABLE, and the other records of the batch are unaffected.
Decoding the Records#
// Runs inside the read loop above, after a read call returns and before the // cleanup, so phInfoLogs, pBuffer, size, pDescriptors and recordCount are valid zes_info_log_decoder_context_handle_t hDecoderContext = nullptr; if (zesInfoLogDecoderContextCreateExt(phInfoLogs[0], &hDecoderContext) != ZE_RESULT_SUCCESS) { output("Records of this info log cannot be decoded\n"); return; } // The fields follow the format of the info log, so query them once uint32_t fieldCount = 0; zesInfoLogDecoderContextGetFieldInfoExt(hDecoderContext, &fieldCount, nullptr); zes_info_log_decodable_field_info_ext_t* pFields = (zes_info_log_decodable_field_info_ext_t*) allocate(fieldCount * sizeof(zes_info_log_decodable_field_info_ext_t)); for (uint32_t f = 0; f < fieldCount; f++) { pFields[f].stype = ZES_STRUCTURE_TYPE_INFO_LOG_DECODABLE_FIELD_INFO_EXT; pFields[f].pNext = nullptr; } zesInfoLogDecoderContextGetFieldInfoExt(hDecoderContext, &fieldCount, pFields); for (uint32_t f = 0; f < fieldCount; f++) { output("Field '%s' (id %u, %zu byte(s), since version %s): %s\n", pFields[f].name, pFields[f].fieldId, pFields[f].requiredBytes, pFields[f].versionString, pFields[f].desc); } if (zesInfoLogDecoderContextDecodeStartExt(hDecoderContext, size, pBuffer, recordCount, pDescriptors) != ZE_RESULT_SUCCESS) { output("Could not start decoding this batch of records\n"); free(pFields); zesInfoLogDecoderContextDeleteExt(phInfoLogs[0], hDecoderContext); return; } ze_bool_t failed = false; for (uint32_t r = 0; r < recordCount && !failed; r++) { for (uint32_t f = 0; f < fieldCount; f++) { uint8_t* pValue = (uint8_t*) allocate(pFields[f].requiredBytes); size_t bytesCopied = 0; ze_result_t result = zesInfoLogDecoderContextReadFieldExt( hDecoderContext, pFields[f].fieldId, r, pValue, pFields[f].requiredBytes, &bytesCopied); if (result == ZE_RESULT_ERROR_NOT_AVAILABLE) { // This record does not carry this field. Not a failure. free(pValue); continue; } if (result != ZE_RESULT_SUCCESS) { output("Could not read field '%s' of record %u\n", pFields[f].name, r); free(pValue); failed = true; break; } // A field may hold several elements; only bytesCopied bytes are valid switch (pFields[f].type) { case ZES_INFO_LOG_FIELD_TYPE_EXT_UINT32: for (size_t e = 0; e < bytesCopied / sizeof(uint32_t); e++) { uint32_t value; memcpy(&value, pValue + e * sizeof(value), sizeof(value)); output("Record %u: %s[%zu] = %u\n", r, pFields[f].name, e, value); } break; case ZES_INFO_LOG_FIELD_TYPE_EXT_INT64: for (size_t e = 0; e < bytesCopied / sizeof(int64_t); e++) { int64_t value; memcpy(&value, pValue + e * sizeof(value), sizeof(value)); output("Record %u: %s[%zu] = %lld\n", r, pFields[f].name, e, (long long) value); } break; case ZES_INFO_LOG_FIELD_TYPE_EXT_DOUBLE: for (size_t e = 0; e < bytesCopied / sizeof(double); e++) { double value; memcpy(&value, pValue + e * sizeof(value), sizeof(value)); output("Record %u: %s[%zu] = %f\n", r, pFields[f].name, e, value); } break; case ZES_INFO_LOG_FIELD_TYPE_EXT_STRING: if (bytesCopied > 0) { output("Record %u: %s = %s\n", r, pFields[f].name, (const char*) pValue); } break; case ZES_INFO_LOG_FIELD_TYPE_EXT_RAW_BYTES: // For example a zes_uuid_t when requiredBytes is ZES_MAX_UUID_SIZE process(pValue, bytesCopied); break; default: // Data types added by a later version of this extension break; } free(pValue); } } // pBuffer and pDescriptors may be reused after this returns zesInfoLogDecoderContextDecodeStopExt(hDecoderContext); free(pFields); zesInfoLogDecoderContextDeleteExt(phInfoLogs[0], hDecoderContext);