diff --git a/src/intel/tools/aubinator_error_decode.c b/src/intel/tools/aubinator_error_decode.c index 319159891bd..9f716269a36 100644 --- a/src/intel/tools/aubinator_error_decode.c +++ b/src/intel/tools/aubinator_error_decode.c @@ -941,7 +941,7 @@ main(int argc, char *argv[]) getline(&line, &line_size, file); rewind(file); if (strncmp(line, XE_KMD_ERROR_DUMP_IDENTIFIER, strlen(XE_KMD_ERROR_DUMP_IDENTIFIER)) == 0) - read_xe_data_file(file, batch_flags); + read_xe_data_file(file, batch_flags, xml_path, option_dump_kernels, option_print_all_bb); else read_i915_data_file(file, batch_flags); free(line); diff --git a/src/intel/tools/aubinator_error_decode_xe.c b/src/intel/tools/aubinator_error_decode_xe.c index 49895af9a6d..94b26f88bf2 100644 --- a/src/intel/tools/aubinator_error_decode_xe.c +++ b/src/intel/tools/aubinator_error_decode_xe.c @@ -5,7 +5,427 @@ #include "aubinator_error_decode_xe.h" -void -read_xe_data_file(FILE *file, enum intel_batch_decode_flags batch_flags) +#include +#include +#include + +#include "aubinator_error_decode_lib.h" +#include "intel/compiler/brw_isa_info.h" +#include "intel/dev/intel_device_info.h" + +enum xe_vm_topic_type { + XE_VM_TOPIC_TYPE_UNKNOWN = 0, + XE_VM_TOPIC_TYPE_LENGTH, + XE_VM_TOPIC_TYPE_DATA, + XE_VM_TOPIC_TYPE_ERROR, +}; + +struct xe_vm_entry { + uint64_t address; + uint32_t length; + const uint32_t *data; +}; + +struct xe_vm { + /* TODO: entries could be appended sorted or a hash could be used to + * optimize performance + */ + struct xe_vm_entry *entries; + uint32_t entries_len; +}; + +static const char * +read_parameter_helper(const char *line, const char *parameter) { + if (!strstr(line, parameter)) + return NULL; + + while (*line != ':') + line++; + /* skip ':' and ' ' */ + line += 2; + + return line; +} + +/* parse lines like 'batch_addr[0]: 0x0000effeffff5000 */ +static bool +read_u64_hexacimal_parameter(const char *line, const char *parameter, uint64_t *value) +{ + line = read_parameter_helper(line, parameter); + if (!line) + return false; + + *value = (uint64_t)strtoull(line, NULL, 0); + return true; +} + +/* parse lines like 'PCI ID: 0x9a49' */ +static bool +read_hexacimal_parameter(const char *line, const char *parameter, int *value) +{ + line = read_parameter_helper(line, parameter); + if (!line) + return false; + + *value = (int)strtol(line, NULL, 0); + return true; +} + +/* parse lines like 'rcs0 (physical), logical instance=0' */ +static bool +read_xe_engine_name(const char *line, char *ring_name) +{ + int i; + + if (!strstr(line, " (physical), logical instance=")) + return false; + + i = 0; + for (i = 0; *line != ' '; i++, line++) + ring_name[i] = *line; + + ring_name[i] = 0; + return true; +} + +/* return type of VM topic lines like '[200000].data: x...' and points + * value_ptr to first char of data of topic type + */ +static enum xe_vm_topic_type +read_xe_vm_line(const char *line, uint64_t *address, const char **value_ptr) +{ + enum xe_vm_topic_type type; + char text_addr[64]; + int i; + + if (*line != '[') + return XE_VM_TOPIC_TYPE_UNKNOWN; + + for (i = 0, line++; *line != ']'; i++, line++) + text_addr[i] = *line; + + text_addr[i] = 0; + *address = (uint64_t)strtoull(text_addr, NULL, 16); + + /* at this point line points to last address digit so +3 to point to type */ + line += 2; + switch (*line) { + case 'd': + type = XE_VM_TOPIC_TYPE_DATA; + break; + case 'l': + type = XE_VM_TOPIC_TYPE_LENGTH; + break; + case 'e': + type = XE_VM_TOPIC_TYPE_ERROR; + break; + default: + printf("type char: %c\n", *line); + return XE_VM_TOPIC_TYPE_UNKNOWN; + } + + for (; *line != ':'; line++); + + *value_ptr = line + 2; + return type; +} + +static void xe_vm_init(struct xe_vm *xe_vm) +{ + xe_vm->entries = NULL; + xe_vm->entries_len = 0; +} + +static void xe_vm_fini(struct xe_vm *xe_vm) +{ + uint32_t i; + + for (i = 0; i < xe_vm->entries_len; i++) + free((uint32_t *)xe_vm->entries[i].data); + + free(xe_vm->entries); +} + +/* + * xe_vm_fini() will take care to free data + */ +static bool +xe_vm_append(struct xe_vm *xe_vm, const uint64_t address, const uint32_t length, const uint32_t *data) +{ + size_t len = sizeof(*xe_vm->entries) * (xe_vm->entries_len + 1); + + xe_vm->entries = realloc(xe_vm->entries, len); + if (!xe_vm->entries) + return false; + + xe_vm->entries[xe_vm->entries_len].address = address; + xe_vm->entries[xe_vm->entries_len].length = length; + xe_vm->entries[xe_vm->entries_len].data = data; + xe_vm->entries_len++; + return true; +} + +static const struct xe_vm_entry * +xe_vm_entry_get(struct xe_vm *xe_vm, const uint64_t address) +{ + uint32_t i; + + for (i = 0; i < xe_vm->entries_len; i++) { + struct xe_vm_entry *entry = &xe_vm->entries[i]; + + if (entry->address == address) + return entry; + + if (address > entry->address && + address < (entry->address + entry->length)) + return entry; + } + + return NULL; +} + +static uint32_t * +xe_vm_entry_address_get_data(const struct xe_vm_entry *entry, const uint64_t address) +{ + uint32_t offset = (address - entry->address) / sizeof(uint32_t); + return (uint32_t *)&entry->data[offset]; +} + +static uint32_t +xe_vm_entry_address_get_len(const struct xe_vm_entry *entry, const uint64_t address) +{ + return entry->length - (address - entry->address); +} + +static bool +ascii85_decode_allocated(const char *in, uint32_t *out, uint32_t vm_entry_bytes_len) +{ + const uint32_t dword_len = vm_entry_bytes_len / sizeof(uint32_t); + uint32_t i; + + for (i = 0; (*in >= '!') && (*in <= 'z') && (i < dword_len); i++) + in = ascii85_decode_char(in, &out[i]); + + if (dword_len != i) + printf("mismatch dword_len=%u i=%u\n", dword_len, i); + + return dword_len == i && (*in < '!' || *in > 'z'); +} + +static struct intel_batch_decode_bo +get_bo(void *user_data, bool ppgtt, uint64_t bo_addr) +{ + struct intel_batch_decode_bo ret = {}; + const struct xe_vm_entry *vm_entry; + struct xe_vm *xe_vm = user_data; + + if (!ppgtt) + return ret; + + vm_entry = xe_vm_entry_get(xe_vm, bo_addr); + if (!vm_entry) + return ret; + + ret.addr = bo_addr; + ret.map = xe_vm_entry_address_get_data(vm_entry, bo_addr); + ret.size = xe_vm_entry_address_get_len(vm_entry, bo_addr); + + return ret; +} + +void +read_xe_data_file(FILE *file, + enum intel_batch_decode_flags batch_flags, + const char *spec_xml_path, + bool option_dump_kernels, + bool option_print_all_bb) +{ + struct intel_batch_decode_ctx batch_ctx; + struct intel_device_info devinfo; + struct intel_spec *spec = NULL; + struct brw_isa_info isa; + struct { + uint64_t *addrs; + uint8_t len; + } batch_buffers = { .addrs = NULL, .len = 0 }; + enum intel_engine_class engine_class = INTEL_ENGINE_CLASS_INVALID; + uint32_t *vm_entry_data = NULL; + uint32_t vm_entry_len = 0; + bool ring_wraps = false; + uint64_t acthd = 0; + struct xe_vm xe_vm; + char *line = NULL; + size_t line_size; + + xe_vm_init(&xe_vm); + + while (getline(&line, &line_size, file) > 0) { + static const char *xe_topic_strings[] = { + "**** Xe Device Coredump ****", + "**** GuC CT ****", + "**** Job ****", + "**** HW Engines ****", + "**** VM state ****", + }; + enum { + TOPIC_DEVICE = 0, + TOPIC_GUC_CT, + TOPIC_JOB, + TOPIC_HW_ENGINES, + TOPIC_VM, + TOPIC_INVALID, + } xe_topic = TOPIC_INVALID; + bool topic_changed = false; + bool print_line = true; + + /* handle Xe dump topics */ + for (int i = 0; i < ARRAY_SIZE(xe_topic_strings); i++) { + if (strncmp(xe_topic_strings[i], line, strlen(xe_topic_strings[i])) == 0) { + topic_changed = true; + xe_topic = i; + print_line = (xe_topic != TOPIC_VM); + break; + } + } + if (topic_changed) { + if (print_line) + fputs(line, stdout); + continue; + } + + switch (xe_topic) { + case TOPIC_DEVICE: { + int int_value; + + if (read_hexacimal_parameter(line, "PCI ID", &int_value)) { + if (intel_get_device_info_from_pci_id(int_value, &devinfo)) { + printf("Detected GFX ver %i\n", devinfo.verx10); + brw_init_isa_info(&isa, &devinfo); + + if (spec_xml_path == NULL) + spec = intel_spec_load(&devinfo); + else + spec = intel_spec_load_from_path(&devinfo, spec_xml_path); + } else { + printf("Unable to identify devid: 0x%x\n", int_value); + } + } + + break; + } + case TOPIC_HW_ENGINES: { + char engine_name[64]; + uint64_t u64_reg; + + if (read_xe_engine_name(line, engine_name)) + ring_name_to_class(engine_name, &engine_class); + + if (read_u64_hexacimal_parameter(line, "ACTHD", &u64_reg)) + acthd = u64_reg; + + /* TODO: parse other engine registers */ + break; + } + case TOPIC_JOB: { + uint64_t u64_value; + + if (read_u64_hexacimal_parameter(line, "batch_addr[", &u64_value)) { + batch_buffers.addrs = realloc(batch_buffers.addrs, sizeof(uint64_t) * (batch_buffers.len + 1)); + batch_buffers.addrs[batch_buffers.len] = u64_value; + batch_buffers.len++; + } + + break; + } + case TOPIC_VM: { + enum xe_vm_topic_type type; + const char *value_ptr; + uint64_t address; + + print_line = false; + type = read_xe_vm_line(line, &address, &value_ptr); + switch (type) { + case XE_VM_TOPIC_TYPE_DATA: { + if (!ascii85_decode_allocated(value_ptr, vm_entry_data, vm_entry_len)) + printf("Failed to parse VMA 0x%lx data\n", address); + break; + } + case XE_VM_TOPIC_TYPE_LENGTH: { + vm_entry_len = strtoul(value_ptr, NULL, 0); + vm_entry_data = calloc(1, vm_entry_len); + if (!vm_entry_data) { + printf("Out of memory to allocate a buffer to store content of VMA 0x%lx\n", address); + break; + } + if (!xe_vm_append(&xe_vm, address, vm_entry_len, vm_entry_data)) { + printf("xe_vm_append() failed for VMA 0x%lx\n", address); + break; + } + break; + } + case XE_VM_TOPIC_TYPE_ERROR: + printf("VMA 0x%lx not present in dump, content will be zeroed. %s\n", address, line); + break; + default: + printf("Not expected line in VM state: %s", line); + } + break; + } + default: + break; + } + + if (print_line) + fputs(line, stdout); + } + + printf("**** Batch buffers ****\n"); + intel_batch_decode_ctx_init_brw(&batch_ctx, &isa, &devinfo, stdout, + batch_flags, spec_xml_path, get_bo, + NULL, &xe_vm); + batch_ctx.acthd = acthd; + + if (option_dump_kernels) + batch_ctx.shader_binary = dump_shader_binary; + + for (int i = 0; i < batch_buffers.len; i++) { + const uint64_t bb_addr = batch_buffers.addrs[i]; + const struct xe_vm_entry *vm_entry = xe_vm_entry_get(&xe_vm, bb_addr); + const char *engine_name = intel_engines_class_to_string(engine_class); + const char *buffer_name = "batch buffer"; + const uint32_t *bb_data; + bool is_ring_buffer; + uint32_t bb_len; + + if (!vm_entry) + continue; + + bb_data = xe_vm_entry_address_get_data(vm_entry, bb_addr); + bb_len = xe_vm_entry_address_get_len(vm_entry, bb_addr); + + printf("--- %s (%s) at 0x%016"PRIx64"\n", + buffer_name, engine_name, batch_buffers.addrs[i]); + + /* TODO: checks around buffer_name are copied from i915, if Xe KMD + * starts to dump HW context or ring buffer this might become + * useful. + */ + is_ring_buffer = strcmp(buffer_name, "ring buffer") == 0; + if (option_print_all_bb || is_ring_buffer || + strcmp(buffer_name, "batch buffer") == 0 || + strcmp(buffer_name, "HW Context") == 0) { + if (is_ring_buffer && ring_wraps) + batch_ctx.flags &= ~INTEL_BATCH_DECODE_OFFSETS; + batch_ctx.engine = engine_class; + intel_print_batch(&batch_ctx, bb_data, bb_len, bb_addr, is_ring_buffer); + batch_ctx.flags = batch_flags; + } + } + + intel_batch_decode_ctx_finish(&batch_ctx); + intel_spec_destroy(spec); + free(batch_buffers.addrs); + free(line); + xe_vm_fini(&xe_vm); } diff --git a/src/intel/tools/aubinator_error_decode_xe.h b/src/intel/tools/aubinator_error_decode_xe.h index f3d0970267c..0fd8bb1b83c 100644 --- a/src/intel/tools/aubinator_error_decode_xe.h +++ b/src/intel/tools/aubinator_error_decode_xe.h @@ -6,7 +6,13 @@ #pragma once #include +#include #include "decoder/intel_decoder.h" -void read_xe_data_file(FILE *file, enum intel_batch_decode_flags batch_flags); +void +read_xe_data_file(FILE *file, + enum intel_batch_decode_flags batch_flags, + const char *spec_xml_path, + bool option_dump_kernels, + bool option_print_all_bb);