2 * Copyright © 2018 Intel Corporation
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
35 #include "util/list.h"
37 #include "aub_write.h"
38 #include "drm-uapi/i915_drm.h"
39 #include "intel_aub.h"
41 static void __attribute__ ((format(__printf__
, 2, 3)))
42 fail_if(int cond
, const char *format
, ...)
49 va_start(args
, format
);
50 vfprintf(stderr
, format
, args
);
56 #define fail(...) fail_if(true, __VA_ARGS__)
58 static int zlib_inflate(uint32_t **ptr
, int len
)
60 struct z_stream_s zstream
;
62 const uint32_t out_size
= 128*4096; /* approximate obj size */
64 memset(&zstream
, 0, sizeof(zstream
));
66 zstream
.next_in
= (unsigned char *)*ptr
;
67 zstream
.avail_in
= 4*len
;
69 if (inflateInit(&zstream
) != Z_OK
)
72 out
= malloc(out_size
);
73 zstream
.next_out
= out
;
74 zstream
.avail_out
= out_size
;
77 switch (inflate(&zstream
, Z_SYNC_FLUSH
)) {
87 if (zstream
.avail_out
)
90 out
= realloc(out
, 2*zstream
.total_out
);
96 zstream
.next_out
= (unsigned char *)out
+ zstream
.total_out
;
97 zstream
.avail_out
= zstream
.total_out
;
100 inflateEnd(&zstream
);
103 return zstream
.total_out
/ 4;
106 static int ascii85_decode(const char *in
, uint32_t **out
, bool inflate
)
108 int len
= 0, size
= 1024;
110 *out
= realloc(*out
, sizeof(uint32_t)*size
);
114 while (*in
>= '!' && *in
<= 'z') {
119 *out
= realloc(*out
, sizeof(uint32_t)*size
);
127 v
+= in
[0] - 33; v
*= 85;
128 v
+= in
[1] - 33; v
*= 85;
129 v
+= in
[2] - 33; v
*= 85;
130 v
+= in
[3] - 33; v
*= 85;
140 return zlib_inflate(out
, len
);
144 print_help(const char *progname
, FILE *file
)
147 "Usage: %s [OPTION]... [FILE]\n"
148 "Convert an Intel GPU i915 error state to an aub file.\n"
149 " -h, --help display this help and exit\n"
150 " -o, --output=FILE the output aub file (default FILE.aub)\n",
173 enum drm_i915_gem_engine_class engine_class
;
176 struct list_head link
;
180 find_or_create(struct list_head
*bo_list
, uint64_t addr
,
181 enum address_space gtt
,
182 enum drm_i915_gem_engine_class engine_class
,
185 list_for_each_entry(struct bo
, bo_entry
, bo_list
, link
) {
186 if (bo_entry
->addr
== addr
&&
187 bo_entry
->gtt
== gtt
&&
188 bo_entry
->engine_class
== engine_class
&&
189 bo_entry
->engine_instance
== engine_instance
)
193 struct bo
*new_bo
= calloc(1, sizeof(*new_bo
));
196 new_bo
->engine_class
= engine_class
;
197 new_bo
->engine_instance
= engine_instance
;
198 list_addtail(&new_bo
->link
, bo_list
);
204 engine_from_name(const char *engine_name
,
205 enum drm_i915_gem_engine_class
*engine_class
,
206 int *engine_instance
)
210 enum drm_i915_gem_engine_class engine_class
;
212 { "rcs", I915_ENGINE_CLASS_RENDER
},
213 { "vcs", I915_ENGINE_CLASS_VIDEO
},
214 { "vecs", I915_ENGINE_CLASS_VIDEO_ENHANCE
},
215 { "bcs", I915_ENGINE_CLASS_COPY
},
216 { NULL
, I915_ENGINE_CLASS_INVALID
},
219 for (r
= rings
; r
->match
; r
++) {
220 if (strncasecmp(engine_name
, r
->match
, strlen(r
->match
)) == 0) {
221 *engine_class
= r
->engine_class
;
222 *engine_instance
= strtol(engine_name
+ strlen(r
->match
), NULL
, 10);
227 fail("Unknown engine %s\n", engine_name
);
231 main(int argc
, char *argv
[])
235 char *out_filename
= NULL
, *in_filename
= NULL
;
236 const struct option aubinator_opts
[] = {
237 { "help", no_argument
, NULL
, 'h' },
238 { "output", required_argument
, NULL
, 'o' },
243 while ((c
= getopt_long(argc
, argv
, "ho:", aubinator_opts
, &i
)) != -1) {
249 out_filename
= strdup(optarg
);
257 in_filename
= argv
[optind
++];
259 if (help
|| argc
== 1 || !in_filename
) {
260 print_help(argv
[0], stderr
);
261 return in_filename
? EXIT_SUCCESS
: EXIT_FAILURE
;
264 if (out_filename
== NULL
) {
265 int out_filename_size
= strlen(in_filename
) + 5;
266 out_filename
= malloc(out_filename_size
);
267 snprintf(out_filename
, out_filename_size
, "%s.aub", in_filename
);
270 FILE *err_file
= fopen(in_filename
, "r");
271 fail_if(!err_file
, "Failed to open error file \"%s\": %m\n", in_filename
);
273 FILE *aub_file
= fopen(out_filename
, "w");
274 fail_if(!aub_file
, "Failed to open aub file \"%s\": %m\n", in_filename
);
276 struct aub_file aub
= {};
278 enum drm_i915_gem_engine_class active_engine_class
= I915_ENGINE_CLASS_INVALID
;
279 int active_engine_instance
= -1;
281 enum address_space active_gtt
= PPGTT
;
285 uint32_t ring_buffer_head
;
286 uint32_t ring_buffer_tail
;
288 } engines
[I915_ENGINE_CLASS_VIDEO_ENHANCE
+ 1];
289 memset(engines
, 0, sizeof(engines
));
291 int num_ring_bos
= 0;
293 struct list_head bo_list
;
294 list_inithead(&bo_list
);
296 struct bo
*last_bo
= NULL
;
300 while (getline(&line
, &line_size
, err_file
) > 0) {
301 const char *pci_id_start
= strstr(line
, "PCI ID");
304 int matched
= sscanf(line
, "PCI ID: 0x%04x\n", &pci_id
);
305 fail_if(!matched
, "Invalid error state file!\n");
307 aub_file_init(&aub
, aub_file
,
308 NULL
, pci_id
, "error_state");
309 fail_if(!aub_use_execlists(&aub
),
310 "%s currently only works on gen8+\n", argv
[0]);
314 if (strstr(line
, " command stream:")) {
315 engine_from_name(line
, &active_engine_class
, &active_engine_instance
);
319 if (sscanf(line
, " ring->head: 0x%x\n",
321 active_engine_class
].instances
[
322 active_engine_instance
].ring_buffer_head
) == 1) {
326 if (sscanf(line
, " ring->tail: 0x%x\n",
328 active_engine_class
].instances
[
329 active_engine_instance
].ring_buffer_tail
) == 1) {
333 const char *active_start
= "Active (";
334 if (strncmp(line
, active_start
, strlen(active_start
)) == 0) {
335 char *ring
= line
+ strlen(active_start
);
337 engine_from_name(ring
, &active_engine_class
, &active_engine_instance
);
340 char *count
= strchr(ring
, '[');
341 fail_if(!count
|| sscanf(count
, "[%d]:", &num_ring_bos
) < 1,
342 "Failed to parse BO table header\n");
346 const char *global_start
= "Pinned (global) [";
347 if (strncmp(line
, global_start
, strlen(global_start
)) == 0) {
348 active_engine_class
= I915_ENGINE_CLASS_INVALID
;
349 active_engine_instance
= -1;
354 if (num_ring_bos
> 0) {
355 unsigned hi
, lo
, size
;
356 if (sscanf(line
, " %x_%x %d", &hi
, &lo
, &size
) == 3) {
357 assert(aub_use_execlists(&aub
));
358 struct bo
*bo_entry
= find_or_create(&bo_list
, ((uint64_t)hi
) << 32 | lo
,
361 active_engine_instance
);
362 bo_entry
->size
= size
;
365 fail("Not enough BO entries in the active table\n");
370 if (line
[0] == ':' || line
[0] == '~') {
371 if (!last_bo
|| last_bo
->type
== BO_TYPE_UNKNOWN
)
374 int count
= ascii85_decode(line
+1, (uint32_t **) &last_bo
->data
, line
[0] == ':');
375 fail_if(count
== 0, "ASCII85 decode failed.\n");
376 last_bo
->size
= count
* 4;
380 char *dashes
= strstr(line
, " --- ");
384 engine_from_name(line
, &active_engine_class
, &active_engine_instance
);
387 char *bo_address_str
= strchr(dashes
, '=');
388 if (!bo_address_str
|| sscanf(bo_address_str
, "= 0x%08x %08x\n", &hi
, &lo
) != 2)
394 enum address_space gtt
;
396 { "gtt_offset", BO_TYPE_BATCH
, PPGTT
},
397 { "user", BO_TYPE_USER
, PPGTT
},
398 { "HW context", BO_TYPE_CONTEXT
, GGTT
},
399 { "ringbuffer", BO_TYPE_RINGBUFFER
, GGTT
},
400 { "HW Status", BO_TYPE_STATUS
, GGTT
},
401 { "WA context", BO_TYPE_CONTEXT_WA
, GGTT
},
402 { "unknown", BO_TYPE_UNKNOWN
, GGTT
},
405 for (b
= bo_types
; b
->type
!= BO_TYPE_UNKNOWN
; b
++) {
406 if (strncasecmp(dashes
, b
->match
, strlen(b
->match
)) == 0)
410 last_bo
= find_or_create(&bo_list
, ((uint64_t) hi
) << 32 | lo
,
412 active_engine_class
, active_engine_instance
);
414 /* The batch buffer will appear twice as gtt_offset and user. Only
415 * keep the batch type.
417 if (last_bo
->type
== BO_TYPE_UNKNOWN
) {
418 last_bo
->type
= b
->type
;
419 last_bo
->name
= b
->match
;
426 /* Find the batch that trigger the hang */
427 struct bo
*batch_bo
= NULL
;
428 list_for_each_entry(struct bo
, bo_entry
, &bo_list
, link
) {
429 if (bo_entry
->type
== BO_TYPE_BATCH
) {
434 fail_if(!batch_bo
, "Failed to find batch buffer.\n");
436 /* Add all the BOs to the aub file */
437 struct bo
*hwsp_bo
= NULL
;
438 list_for_each_entry(struct bo
, bo_entry
, &bo_list
, link
) {
439 switch (bo_entry
->type
) {
441 aub_map_ppgtt(&aub
, bo_entry
->addr
, bo_entry
->size
);
442 aub_write_trace_block(&aub
, AUB_TRACE_TYPE_BATCH
,
443 bo_entry
->data
, bo_entry
->size
, bo_entry
->addr
);
446 aub_map_ppgtt(&aub
, bo_entry
->addr
, bo_entry
->size
);
447 aub_write_trace_block(&aub
, AUB_TRACE_TYPE_NOTYPE
,
448 bo_entry
->data
, bo_entry
->size
, bo_entry
->addr
);
450 case BO_TYPE_CONTEXT
:
451 if (bo_entry
->engine_class
== batch_bo
->engine_class
&&
452 bo_entry
->engine_instance
== batch_bo
->engine_instance
) {
455 uint32_t *context
= (uint32_t *) (bo_entry
->data
+ 4096 /* GuC */ + 4096 /* HWSP */);
457 if (context
[1] == 0) {
459 "Invalid context image data.\n"
460 "This is likely a kernel issue : https://bugs.freedesktop.org/show_bug.cgi?id=107691\n");
463 /* Update the ring buffer at the last known location. */
464 context
[5] = engines
[bo_entry
->engine_class
].instances
[bo_entry
->engine_instance
].ring_buffer_head
;
465 context
[7] = engines
[bo_entry
->engine_class
].instances
[bo_entry
->engine_instance
].ring_buffer_tail
;
466 fprintf(stdout
, "engine start=0x%x head/tail=0x%x/0x%x\n",
467 context
[9], context
[5], context
[7]);
469 /* The error state doesn't provide a dump of the page tables, so
470 * we have to provide our own, that's easy enough.
472 context
[49] = aub
.pml4
.phys_addr
>> 32;
473 context
[51] = aub
.pml4
.phys_addr
& 0xffffffff;
475 fprintf(stdout
, "context dump:\n");
476 for (int i
= 0; i
< 60; i
++) {
478 fprintf(stdout
, "\n 0x%08lx: ", bo_entry
->addr
+ 8192 + i
* 4);
479 fprintf(stdout
, "0x%08x ", context
[i
]);
481 fprintf(stdout
, "\n");
484 aub_write_ggtt(&aub
, bo_entry
->addr
, bo_entry
->size
, bo_entry
->data
);
486 case BO_TYPE_RINGBUFFER
:
488 case BO_TYPE_CONTEXT_WA
:
489 aub_write_ggtt(&aub
, bo_entry
->addr
, bo_entry
->size
, bo_entry
->data
);
491 case BO_TYPE_UNKNOWN
:
492 if (bo_entry
->gtt
== PPGTT
) {
493 aub_map_ppgtt(&aub
, bo_entry
->addr
, bo_entry
->size
);
494 if (bo_entry
->data
) {
495 aub_write_trace_block(&aub
, AUB_TRACE_TYPE_NOTYPE
,
496 bo_entry
->data
, bo_entry
->size
, bo_entry
->addr
);
499 if (bo_entry
->size
> 0) {
500 void *zero_data
= calloc(1, bo_entry
->size
);
501 aub_write_ggtt(&aub
, bo_entry
->addr
, bo_entry
->size
, zero_data
);
511 fail_if(!hwsp_bo
, "Failed to find Context buffer.\n");
512 aub_write_context_execlists(&aub
, hwsp_bo
->addr
+ 4096 /* skip GuC page */, hwsp_bo
->engine_class
);
515 list_for_each_entry_safe(struct bo
, bo_entry
, &bo_list
, link
) {
516 list_del(&bo_entry
->link
);
517 free(bo_entry
->data
);
527 aub_file_finish(&aub
);
528 } else if(aub_file
) {
534 /* vim: set ts=8 sw=8 tw=0 cino=:0,(0 noet :*/