• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2024 Intel Corporation
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include "aubinator_error_decode_xe.h"
7 
8 #include <stdint.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include "aubinator_error_decode_lib.h"
13 #include "intel/compiler/brw_isa_info.h"
14 #include "intel/dev/intel_device_info.h"
15 
16 enum xe_vm_topic_type {
17    XE_VM_TOPIC_TYPE_UNKNOWN = 0,
18    XE_VM_TOPIC_TYPE_LENGTH,
19    XE_VM_TOPIC_TYPE_DATA,
20    XE_VM_TOPIC_TYPE_ERROR,
21 };
22 
23 struct xe_vm_entry {
24    uint64_t address;
25    uint32_t length;
26    const uint32_t *data;
27 };
28 
29 struct xe_vm {
30    /* TODO: entries could be appended sorted or a hash could be used to
31     * optimize performance
32     */
33    struct xe_vm_entry *entries;
34    uint32_t entries_len;
35 };
36 
37 static const char *
read_parameter_helper(const char * line,const char * parameter)38 read_parameter_helper(const char *line, const char *parameter)
39 {
40    if (!strstr(line, parameter))
41       return NULL;
42 
43    while (*line != ':')
44       line++;
45    /* skip ':' and ' ' */
46    line += 2;
47 
48    return line;
49 }
50 
51 /* parse lines like 'batch_addr[0]: 0x0000effeffff5000 */
52 static bool
read_u64_hexacimal_parameter(const char * line,const char * parameter,uint64_t * value)53 read_u64_hexacimal_parameter(const char *line, const char *parameter, uint64_t *value)
54 {
55    line = read_parameter_helper(line, parameter);
56    if (!line)
57       return false;
58 
59    *value = (uint64_t)strtoull(line, NULL, 0);
60    return true;
61 }
62 
63 /* parse lines like 'PCI ID: 0x9a49' */
64 static bool
read_hexacimal_parameter(const char * line,const char * parameter,int * value)65 read_hexacimal_parameter(const char *line, const char *parameter, int *value)
66 {
67    line = read_parameter_helper(line, parameter);
68    if (!line)
69       return false;
70 
71    *value = (int)strtol(line, NULL, 0);
72    return true;
73 }
74 
75 /* parse lines like 'rcs0 (physical), logical instance=0' */
76 static bool
read_xe_engine_name(const char * line,char * ring_name)77 read_xe_engine_name(const char *line, char *ring_name)
78 {
79    int i;
80 
81    if (!strstr(line, " (physical), logical instance="))
82       return false;
83 
84    i = 0;
85    for (i = 0; *line != ' '; i++, line++)
86       ring_name[i] = *line;
87 
88    ring_name[i] = 0;
89    return true;
90 }
91 
92 /* return type of VM topic lines like '[200000].data: x...' and points
93  * value_ptr to first char of data of topic type
94  */
95 static enum xe_vm_topic_type
read_xe_vm_line(const char * line,uint64_t * address,const char ** value_ptr)96 read_xe_vm_line(const char *line, uint64_t *address, const char **value_ptr)
97 {
98    enum xe_vm_topic_type type;
99    char text_addr[64];
100    int i;
101 
102    if (*line != '[')
103       return XE_VM_TOPIC_TYPE_UNKNOWN;
104 
105    for (i = 0, line++; *line != ']'; i++, line++)
106       text_addr[i] = *line;
107 
108    text_addr[i] = 0;
109    *address = (uint64_t)strtoull(text_addr, NULL, 16);
110 
111    /* at this point line points to last address digit so +3 to point to type */
112    line += 2;
113    switch (*line) {
114    case 'd':
115       type = XE_VM_TOPIC_TYPE_DATA;
116       break;
117    case 'l':
118       type = XE_VM_TOPIC_TYPE_LENGTH;
119       break;
120    case 'e':
121       type = XE_VM_TOPIC_TYPE_ERROR;
122       break;
123    default:
124       printf("type char: %c\n", *line);
125       return XE_VM_TOPIC_TYPE_UNKNOWN;
126    }
127 
128    for (; *line != ':'; line++);
129 
130    *value_ptr = line + 2;
131    return type;
132 }
133 
xe_vm_init(struct xe_vm * xe_vm)134 static void xe_vm_init(struct xe_vm *xe_vm)
135 {
136    xe_vm->entries = NULL;
137    xe_vm->entries_len = 0;
138 }
139 
xe_vm_fini(struct xe_vm * xe_vm)140 static void xe_vm_fini(struct xe_vm *xe_vm)
141 {
142    uint32_t i;
143 
144    for (i = 0; i < xe_vm->entries_len; i++)
145       free((uint32_t *)xe_vm->entries[i].data);
146 
147    free(xe_vm->entries);
148 }
149 
150 /*
151  * xe_vm_fini() will take care to free data
152  */
153 static bool
xe_vm_append(struct xe_vm * xe_vm,const uint64_t address,const uint32_t length,const uint32_t * data)154 xe_vm_append(struct xe_vm *xe_vm, const uint64_t address, const uint32_t length, const uint32_t *data)
155 {
156    size_t len = sizeof(*xe_vm->entries) * (xe_vm->entries_len + 1);
157 
158    xe_vm->entries = realloc(xe_vm->entries, len);
159    if (!xe_vm->entries)
160       return false;
161 
162    xe_vm->entries[xe_vm->entries_len].address = address;
163    xe_vm->entries[xe_vm->entries_len].length = length;
164    xe_vm->entries[xe_vm->entries_len].data = data;
165    xe_vm->entries_len++;
166    return true;
167 }
168 
169 static const struct xe_vm_entry *
xe_vm_entry_get(struct xe_vm * xe_vm,const uint64_t address)170 xe_vm_entry_get(struct xe_vm *xe_vm, const uint64_t address)
171 {
172    uint32_t i;
173 
174    for (i = 0; i < xe_vm->entries_len; i++) {
175       struct xe_vm_entry *entry = &xe_vm->entries[i];
176 
177       if (entry->address == address)
178          return entry;
179 
180       if (address > entry->address &&
181           address < (entry->address + entry->length))
182          return entry;
183    }
184 
185    return NULL;
186 }
187 
188 static uint32_t *
xe_vm_entry_address_get_data(const struct xe_vm_entry * entry,const uint64_t address)189 xe_vm_entry_address_get_data(const struct xe_vm_entry *entry, const uint64_t address)
190 {
191    uint32_t offset = (address - entry->address) / sizeof(uint32_t);
192    return (uint32_t *)&entry->data[offset];
193 }
194 
195 static uint32_t
xe_vm_entry_address_get_len(const struct xe_vm_entry * entry,const uint64_t address)196 xe_vm_entry_address_get_len(const struct xe_vm_entry *entry, const uint64_t address)
197 {
198    return entry->length - (address - entry->address);
199 }
200 
201 static bool
ascii85_decode_allocated(const char * in,uint32_t * out,uint32_t vm_entry_bytes_len)202 ascii85_decode_allocated(const char *in, uint32_t *out, uint32_t vm_entry_bytes_len)
203 {
204    const uint32_t dword_len = vm_entry_bytes_len / sizeof(uint32_t);
205    uint32_t i;
206 
207    for (i = 0; (*in >= '!') && (*in <= 'z') && (i < dword_len); i++)
208       in = ascii85_decode_char(in, &out[i]);
209 
210    if (dword_len != i)
211       printf("mismatch dword_len=%u i=%u\n", dword_len, i);
212 
213    return dword_len == i && (*in < '!' || *in > 'z');
214 }
215 
216 static struct intel_batch_decode_bo
get_bo(void * user_data,bool ppgtt,uint64_t bo_addr)217 get_bo(void *user_data, bool ppgtt, uint64_t bo_addr)
218 {
219    struct intel_batch_decode_bo ret = {};
220    const struct xe_vm_entry *vm_entry;
221    struct xe_vm *xe_vm = user_data;
222 
223    if (!ppgtt)
224       return ret;
225 
226    vm_entry = xe_vm_entry_get(xe_vm, bo_addr);
227    if (!vm_entry)
228       return ret;
229 
230    ret.addr = bo_addr;
231    ret.map = xe_vm_entry_address_get_data(vm_entry, bo_addr);
232    ret.size = xe_vm_entry_address_get_len(vm_entry, bo_addr);
233 
234    return ret;
235 }
236 
237 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)238 read_xe_data_file(FILE *file,
239                   enum intel_batch_decode_flags batch_flags,
240                   const char *spec_xml_path,
241                   bool option_dump_kernels,
242                   bool option_print_all_bb)
243 {
244    struct intel_batch_decode_ctx batch_ctx;
245    struct intel_device_info devinfo;
246    struct intel_spec *spec = NULL;
247    struct brw_isa_info isa;
248    struct {
249       uint64_t *addrs;
250       uint8_t len;
251    } batch_buffers = { .addrs = NULL, .len = 0 };
252    enum intel_engine_class engine_class = INTEL_ENGINE_CLASS_INVALID;
253    uint32_t *vm_entry_data = NULL;
254    uint32_t vm_entry_len = 0;
255    bool ring_wraps = false;
256    uint64_t acthd = 0;
257    struct xe_vm xe_vm;
258    char *line = NULL;
259    size_t line_size;
260 
261    xe_vm_init(&xe_vm);
262 
263    while (getline(&line, &line_size, file) > 0) {
264       static const char *xe_topic_strings[] = {
265          "**** Xe Device Coredump ****",
266          "**** GuC CT ****",
267          "**** Job ****",
268          "**** HW Engines ****",
269          "**** VM state ****",
270       };
271       enum  {
272          TOPIC_DEVICE = 0,
273          TOPIC_GUC_CT,
274          TOPIC_JOB,
275          TOPIC_HW_ENGINES,
276          TOPIC_VM,
277          TOPIC_INVALID,
278       } xe_topic = TOPIC_INVALID;
279       bool topic_changed = false;
280       bool print_line = true;
281 
282       /* handle Xe dump topics */
283       for (int i = 0; i < ARRAY_SIZE(xe_topic_strings); i++) {
284          if (strncmp(xe_topic_strings[i], line, strlen(xe_topic_strings[i])) == 0) {
285             topic_changed = true;
286             xe_topic = i;
287             print_line = (xe_topic != TOPIC_VM);
288             break;
289          }
290       }
291       if (topic_changed) {
292          if (print_line)
293             fputs(line, stdout);
294          continue;
295       }
296 
297       switch (xe_topic) {
298       case TOPIC_DEVICE: {
299          int int_value;
300 
301          if (read_hexacimal_parameter(line, "PCI ID", &int_value)) {
302             if (intel_get_device_info_from_pci_id(int_value, &devinfo)) {
303                printf("Detected GFX ver %i\n", devinfo.verx10);
304                brw_init_isa_info(&isa, &devinfo);
305 
306                if (spec_xml_path == NULL)
307                   spec = intel_spec_load(&devinfo);
308                else
309                   spec = intel_spec_load_from_path(&devinfo, spec_xml_path);
310             } else {
311                printf("Unable to identify devid: 0x%x\n", int_value);
312             }
313          }
314 
315          break;
316       }
317       case TOPIC_HW_ENGINES: {
318          char engine_name[64];
319          uint64_t u64_reg;
320 
321          if (read_xe_engine_name(line, engine_name))
322             ring_name_to_class(engine_name, &engine_class);
323 
324          if (read_u64_hexacimal_parameter(line, "ACTHD", &u64_reg))
325             acthd = u64_reg;
326 
327          /* TODO: parse other engine registers */
328          break;
329       }
330       case TOPIC_JOB: {
331          uint64_t u64_value;
332 
333          if (read_u64_hexacimal_parameter(line, "batch_addr[", &u64_value)) {
334             batch_buffers.addrs = realloc(batch_buffers.addrs, sizeof(uint64_t) * (batch_buffers.len + 1));
335             batch_buffers.addrs[batch_buffers.len] = u64_value;
336             batch_buffers.len++;
337          }
338 
339          break;
340       }
341       case TOPIC_VM: {
342          enum xe_vm_topic_type type;
343          const char *value_ptr;
344          uint64_t address;
345 
346          print_line = false;
347          type = read_xe_vm_line(line, &address, &value_ptr);
348          switch (type) {
349          case XE_VM_TOPIC_TYPE_DATA: {
350             if (!ascii85_decode_allocated(value_ptr, vm_entry_data, vm_entry_len))
351                printf("Failed to parse VMA 0x%lx data\n", address);
352             break;
353          }
354          case XE_VM_TOPIC_TYPE_LENGTH: {
355             vm_entry_len = strtoul(value_ptr, NULL, 0);
356             vm_entry_data = calloc(1, vm_entry_len);
357             if (!vm_entry_data) {
358                printf("Out of memory to allocate a buffer to store content of VMA 0x%lx\n", address);
359                break;
360             }
361             if (!xe_vm_append(&xe_vm, address, vm_entry_len, vm_entry_data)) {
362                printf("xe_vm_append() failed for VMA 0x%lx\n", address);
363                break;
364             }
365             break;
366          }
367          case XE_VM_TOPIC_TYPE_ERROR:
368             printf("VMA 0x%lx not present in dump, content will be zeroed. %s\n", address, line);
369             break;
370          default:
371             printf("Not expected line in VM state: %s", line);
372          }
373          break;
374       }
375       default:
376             break;
377       }
378 
379       if (print_line)
380          fputs(line, stdout);
381    }
382 
383    printf("**** Batch buffers ****\n");
384    intel_batch_decode_ctx_init_brw(&batch_ctx, &isa, &devinfo, stdout,
385                                    batch_flags, spec_xml_path, get_bo,
386                                    NULL, &xe_vm);
387    batch_ctx.acthd = acthd;
388 
389    if (option_dump_kernels)
390       batch_ctx.shader_binary = dump_shader_binary;
391 
392    for (int i = 0; i < batch_buffers.len; i++) {
393       const uint64_t bb_addr = batch_buffers.addrs[i];
394       const struct xe_vm_entry *vm_entry = xe_vm_entry_get(&xe_vm, bb_addr);
395       const char *engine_name = intel_engines_class_to_string(engine_class);
396       const char *buffer_name = "batch buffer";
397       const uint32_t *bb_data;
398       bool is_ring_buffer;
399       uint32_t bb_len;
400 
401       if (!vm_entry)
402          continue;
403 
404       bb_data = xe_vm_entry_address_get_data(vm_entry, bb_addr);
405       bb_len = xe_vm_entry_address_get_len(vm_entry, bb_addr);
406 
407       printf("--- %s (%s) at 0x%016"PRIx64"\n",
408              buffer_name, engine_name, batch_buffers.addrs[i]);
409 
410       /* TODO: checks around buffer_name are copied from i915, if Xe KMD
411        * starts to dump HW context or ring buffer this might become
412        * useful.
413        */
414       is_ring_buffer = strcmp(buffer_name, "ring buffer") == 0;
415       if (option_print_all_bb || is_ring_buffer ||
416           strcmp(buffer_name, "batch buffer") == 0 ||
417           strcmp(buffer_name, "HW Context") == 0) {
418          if (is_ring_buffer && ring_wraps)
419             batch_ctx.flags &= ~INTEL_BATCH_DECODE_OFFSETS;
420          batch_ctx.engine = engine_class;
421          intel_print_batch(&batch_ctx, bb_data, bb_len, bb_addr, is_ring_buffer);
422          batch_ctx.flags = batch_flags;
423       }
424    }
425 
426    intel_batch_decode_ctx_finish(&batch_ctx);
427    intel_spec_destroy(spec);
428    free(batch_buffers.addrs);
429    free(line);
430    xe_vm_fini(&xe_vm);
431 }
432