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
;
213 { "rcs", I915_ENGINE_CLASS_RENDER
, true },
214 { "vcs", I915_ENGINE_CLASS_VIDEO
, true },
215 { "vecs", I915_ENGINE_CLASS_VIDEO_ENHANCE
, true },
216 { "bcs", I915_ENGINE_CLASS_COPY
, true },
217 { "global", I915_ENGINE_CLASS_INVALID
, false },
218 { "render command stream", I915_ENGINE_CLASS_RENDER
, false },
219 { "blt command stream", I915_ENGINE_CLASS_COPY
, false },
220 { "bsd command stream", I915_ENGINE_CLASS_VIDEO
, false },
221 { "vebox command stream", I915_ENGINE_CLASS_VIDEO_ENHANCE
, false },
222 { NULL
, I915_ENGINE_CLASS_INVALID
},
225 for (r
= rings
; r
->match
; r
++) {
226 if (strncasecmp(engine_name
, r
->match
, strlen(r
->match
)) == 0) {
227 *engine_class
= r
->engine_class
;
228 if (r
->parse_instance
)
229 *engine_instance
= strtol(engine_name
+ strlen(r
->match
), NULL
, 10);
231 *engine_instance
= 0;
236 fail("Unknown engine %s\n", engine_name
);
240 main(int argc
, char *argv
[])
243 bool help
= false, verbose
;
244 char *out_filename
= NULL
, *in_filename
= NULL
;
245 const struct option aubinator_opts
[] = {
246 { "help", no_argument
, NULL
, 'h' },
247 { "output", required_argument
, NULL
, 'o' },
248 { "verbose", no_argument
, NULL
, 'v' },
253 while ((c
= getopt_long(argc
, argv
, "ho:v", aubinator_opts
, &i
)) != -1) {
259 out_filename
= strdup(optarg
);
270 in_filename
= argv
[optind
++];
272 if (help
|| argc
== 1 || !in_filename
) {
273 print_help(argv
[0], stderr
);
274 return in_filename
? EXIT_SUCCESS
: EXIT_FAILURE
;
277 if (out_filename
== NULL
) {
278 int out_filename_size
= strlen(in_filename
) + 5;
279 out_filename
= malloc(out_filename_size
);
280 snprintf(out_filename
, out_filename_size
, "%s.aub", in_filename
);
283 FILE *err_file
= fopen(in_filename
, "r");
284 fail_if(!err_file
, "Failed to open error file \"%s\": %m\n", in_filename
);
286 FILE *aub_file
= fopen(out_filename
, "w");
287 fail_if(!aub_file
, "Failed to open aub file \"%s\": %m\n", in_filename
);
289 struct aub_file aub
= {};
291 enum drm_i915_gem_engine_class active_engine_class
= I915_ENGINE_CLASS_INVALID
;
292 int active_engine_instance
= -1;
294 enum address_space active_gtt
= PPGTT
;
298 uint32_t ring_buffer_head
;
299 uint32_t ring_buffer_tail
;
301 } engines
[I915_ENGINE_CLASS_VIDEO_ENHANCE
+ 1];
302 memset(engines
, 0, sizeof(engines
));
304 int num_ring_bos
= 0;
306 struct list_head bo_list
;
307 list_inithead(&bo_list
);
309 struct bo
*last_bo
= NULL
;
313 while (getline(&line
, &line_size
, err_file
) > 0) {
314 const char *pci_id_start
= strstr(line
, "PCI ID");
317 int matched
= sscanf(line
, "PCI ID: 0x%04x\n", &pci_id
);
318 fail_if(!matched
, "Invalid error state file!\n");
320 aub_file_init(&aub
, aub_file
,
321 NULL
, pci_id
, "error_state");
323 aub
.verbose_log_file
= stdout
;
324 fail_if(!aub_use_execlists(&aub
),
325 "%s currently only works on gen8+\n", argv
[0]);
329 if (strstr(line
, " command stream:")) {
330 engine_from_name(line
, &active_engine_class
, &active_engine_instance
);
334 if (sscanf(line
, " ring->head: 0x%x\n",
336 active_engine_class
].instances
[
337 active_engine_instance
].ring_buffer_head
) == 1) {
341 if (sscanf(line
, " ring->tail: 0x%x\n",
343 active_engine_class
].instances
[
344 active_engine_instance
].ring_buffer_tail
) == 1) {
348 const char *active_start
= "Active (";
349 if (strncmp(line
, active_start
, strlen(active_start
)) == 0) {
350 char *ring
= line
+ strlen(active_start
);
352 engine_from_name(ring
, &active_engine_class
, &active_engine_instance
);
355 char *count
= strchr(ring
, '[');
356 fail_if(!count
|| sscanf(count
, "[%d]:", &num_ring_bos
) < 1,
357 "Failed to parse BO table header\n");
361 const char *global_start
= "Pinned (global) [";
362 if (strncmp(line
, global_start
, strlen(global_start
)) == 0) {
363 active_engine_class
= I915_ENGINE_CLASS_INVALID
;
364 active_engine_instance
= -1;
369 if (num_ring_bos
> 0) {
370 unsigned hi
, lo
, size
;
371 if (sscanf(line
, " %x_%x %d", &hi
, &lo
, &size
) == 3) {
372 assert(aub_use_execlists(&aub
));
373 struct bo
*bo_entry
= find_or_create(&bo_list
, ((uint64_t)hi
) << 32 | lo
,
376 active_engine_instance
);
377 bo_entry
->size
= size
;
380 fail("Not enough BO entries in the active table\n");
385 if (line
[0] == ':' || line
[0] == '~') {
386 if (!last_bo
|| last_bo
->type
== BO_TYPE_UNKNOWN
)
389 int count
= ascii85_decode(line
+1, (uint32_t **) &last_bo
->data
, line
[0] == ':');
390 fail_if(count
== 0, "ASCII85 decode failed.\n");
391 last_bo
->size
= count
* 4;
395 char *dashes
= strstr(line
, " --- ");
399 engine_from_name(line
, &active_engine_class
, &active_engine_instance
);
402 char *bo_address_str
= strchr(dashes
, '=');
403 if (!bo_address_str
|| sscanf(bo_address_str
, "= 0x%08x %08x\n", &hi
, &lo
) != 2)
409 enum address_space gtt
;
411 { "gtt_offset", BO_TYPE_BATCH
, PPGTT
},
412 { "user", BO_TYPE_USER
, PPGTT
},
413 { "HW context", BO_TYPE_CONTEXT
, GGTT
},
414 { "ringbuffer", BO_TYPE_RINGBUFFER
, GGTT
},
415 { "HW Status", BO_TYPE_STATUS
, GGTT
},
416 { "WA context", BO_TYPE_CONTEXT_WA
, GGTT
},
417 { "unknown", BO_TYPE_UNKNOWN
, GGTT
},
420 for (b
= bo_types
; b
->type
!= BO_TYPE_UNKNOWN
; b
++) {
421 if (strncasecmp(dashes
, b
->match
, strlen(b
->match
)) == 0)
425 last_bo
= find_or_create(&bo_list
, ((uint64_t) hi
) << 32 | lo
,
427 active_engine_class
, active_engine_instance
);
429 /* The batch buffer will appear twice as gtt_offset and user. Only
430 * keep the batch type.
432 if (last_bo
->type
== BO_TYPE_UNKNOWN
) {
433 last_bo
->type
= b
->type
;
434 last_bo
->name
= b
->match
;
442 fprintf(stdout
, "BOs found:\n");
443 list_for_each_entry(struct bo
, bo_entry
, &bo_list
, link
) {
444 fprintf(stdout
, "\t type=%i addr=0x%016" PRIx64
" size=%" PRIu64
"\n",
445 bo_entry
->type
, bo_entry
->addr
, bo_entry
->size
);
449 /* Find the batch that trigger the hang */
450 struct bo
*batch_bo
= NULL
;
451 list_for_each_entry(struct bo
, bo_entry
, &bo_list
, link
) {
452 if (bo_entry
->type
== BO_TYPE_BATCH
) {
457 fail_if(!batch_bo
, "Failed to find batch buffer.\n");
459 /* Add all the BOs to the aub file */
460 struct bo
*hwsp_bo
= NULL
;
461 list_for_each_entry(struct bo
, bo_entry
, &bo_list
, link
) {
462 switch (bo_entry
->type
) {
464 aub_map_ppgtt(&aub
, bo_entry
->addr
, bo_entry
->size
);
465 aub_write_trace_block(&aub
, AUB_TRACE_TYPE_BATCH
,
466 bo_entry
->data
, bo_entry
->size
, bo_entry
->addr
);
469 aub_map_ppgtt(&aub
, bo_entry
->addr
, bo_entry
->size
);
470 aub_write_trace_block(&aub
, AUB_TRACE_TYPE_NOTYPE
,
471 bo_entry
->data
, bo_entry
->size
, bo_entry
->addr
);
473 case BO_TYPE_CONTEXT
:
474 if (bo_entry
->engine_class
== batch_bo
->engine_class
&&
475 bo_entry
->engine_instance
== batch_bo
->engine_instance
) {
478 uint32_t *context
= (uint32_t *) (bo_entry
->data
+ 4096 /* GuC */ + 4096 /* HWSP */);
480 if (context
[1] == 0) {
482 "Invalid context image data.\n"
483 "This is likely a kernel issue : https://bugs.freedesktop.org/show_bug.cgi?id=107691\n");
486 /* Update the ring buffer at the last known location. */
487 context
[5] = engines
[bo_entry
->engine_class
].instances
[bo_entry
->engine_instance
].ring_buffer_head
;
488 context
[7] = engines
[bo_entry
->engine_class
].instances
[bo_entry
->engine_instance
].ring_buffer_tail
;
489 fprintf(stdout
, "engine start=0x%x head/tail=0x%x/0x%x\n",
490 context
[9], context
[5], context
[7]);
492 /* The error state doesn't provide a dump of the page tables, so
493 * we have to provide our own, that's easy enough.
495 context
[49] = aub
.pml4
.phys_addr
>> 32;
496 context
[51] = aub
.pml4
.phys_addr
& 0xffffffff;
498 fprintf(stdout
, "context dump:\n");
499 for (int i
= 0; i
< 60; i
++) {
501 fprintf(stdout
, "\n 0x%08lx: ", bo_entry
->addr
+ 8192 + i
* 4);
502 fprintf(stdout
, "0x%08x ", context
[i
]);
504 fprintf(stdout
, "\n");
507 aub_write_ggtt(&aub
, bo_entry
->addr
, bo_entry
->size
, bo_entry
->data
);
509 case BO_TYPE_RINGBUFFER
:
511 case BO_TYPE_CONTEXT_WA
:
512 aub_write_ggtt(&aub
, bo_entry
->addr
, bo_entry
->size
, bo_entry
->data
);
514 case BO_TYPE_UNKNOWN
:
515 if (bo_entry
->gtt
== PPGTT
) {
516 aub_map_ppgtt(&aub
, bo_entry
->addr
, bo_entry
->size
);
517 if (bo_entry
->data
) {
518 aub_write_trace_block(&aub
, AUB_TRACE_TYPE_NOTYPE
,
519 bo_entry
->data
, bo_entry
->size
, bo_entry
->addr
);
522 if (bo_entry
->size
> 0) {
523 void *zero_data
= calloc(1, bo_entry
->size
);
524 aub_write_ggtt(&aub
, bo_entry
->addr
, bo_entry
->size
, zero_data
);
534 fail_if(!hwsp_bo
, "Failed to find Context buffer.\n");
535 aub_write_context_execlists(&aub
, hwsp_bo
->addr
+ 4096 /* skip GuC page */, hwsp_bo
->engine_class
);
538 list_for_each_entry_safe(struct bo
, bo_entry
, &bo_list
, link
) {
539 list_del(&bo_entry
->link
);
540 free(bo_entry
->data
);
550 aub_file_finish(&aub
);
551 } else if(aub_file
) {
557 /* vim: set ts=8 sw=8 tw=0 cino=:0,(0 noet :*/