#include <stdarg.h>
#include <zlib.h>
+#include "util/list.h"
+
#include "aub_write.h"
#include "drm-uapi/i915_drm.h"
#include "intel_aub.h"
progname);
}
+struct bo {
+ enum address_space {
+ PPGTT,
+ GGTT,
+ } gtt;
+ enum bo_type {
+ BO_TYPE_UNKNOWN = 0,
+ BO_TYPE_BATCH,
+ BO_TYPE_USER,
+ BO_TYPE_CONTEXT,
+ BO_TYPE_RINGBUFFER,
+ BO_TYPE_STATUS,
+ BO_TYPE_CONTEXT_WA,
+ } type;
+ const char *name;
+ uint64_t addr;
+ uint8_t *data;
+ uint64_t size;
+
+ enum drm_i915_gem_engine_class engine_class;
+ int engine_instance;
+
+ struct list_head link;
+};
+
+static struct bo *
+find_or_create(struct list_head *bo_list, uint64_t addr,
+ enum address_space gtt,
+ enum drm_i915_gem_engine_class engine_class,
+ int engine_instance)
+{
+ list_for_each_entry(struct bo, bo_entry, bo_list, link) {
+ if (bo_entry->addr == addr &&
+ bo_entry->gtt == gtt &&
+ bo_entry->engine_class == engine_class &&
+ bo_entry->engine_instance == engine_instance)
+ return bo_entry;
+ }
+
+ struct bo *new_bo = calloc(1, sizeof(*new_bo));
+ new_bo->addr = addr;
+ new_bo->gtt = gtt;
+ new_bo->engine_class = engine_class;
+ new_bo->engine_instance = engine_instance;
+ list_addtail(&new_bo->link, bo_list);
+
+ return new_bo;
+}
+
+static void
+engine_from_name(const char *engine_name,
+ enum drm_i915_gem_engine_class *engine_class,
+ int *engine_instance)
+{
+ const struct {
+ const char *match;
+ enum drm_i915_gem_engine_class engine_class;
+ bool parse_instance;
+ } rings[] = {
+ { "rcs", I915_ENGINE_CLASS_RENDER, true },
+ { "vcs", I915_ENGINE_CLASS_VIDEO, true },
+ { "vecs", I915_ENGINE_CLASS_VIDEO_ENHANCE, true },
+ { "bcs", I915_ENGINE_CLASS_COPY, true },
+ { "global", I915_ENGINE_CLASS_INVALID, false },
+ { "render command stream", I915_ENGINE_CLASS_RENDER, false },
+ { "blt command stream", I915_ENGINE_CLASS_COPY, false },
+ { "bsd command stream", I915_ENGINE_CLASS_VIDEO, false },
+ { "vebox command stream", I915_ENGINE_CLASS_VIDEO_ENHANCE, false },
+ { NULL, I915_ENGINE_CLASS_INVALID },
+ }, *r;
+
+ for (r = rings; r->match; r++) {
+ if (strncasecmp(engine_name, r->match, strlen(r->match)) == 0) {
+ *engine_class = r->engine_class;
+ if (r->parse_instance)
+ *engine_instance = strtol(engine_name + strlen(r->match), NULL, 10);
+ else
+ *engine_instance = 0;
+ return;
+ }
+ }
+
+ fail("Unknown engine %s\n", engine_name);
+}
+
int
main(int argc, char *argv[])
{
int i, c;
- bool help = false;
+ bool help = false, verbose;
char *out_filename = NULL, *in_filename = NULL;
const struct option aubinator_opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "output", required_argument, NULL, 'o' },
+ { "verbose", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0 }
};
i = 0;
- while ((c = getopt_long(argc, argv, "ho:", aubinator_opts, &i)) != -1) {
+ while ((c = getopt_long(argc, argv, "ho:v", aubinator_opts, &i)) != -1) {
switch (c) {
case 'h':
help = true;
case 'o':
out_filename = strdup(optarg);
break;
+ case 'v':
+ verbose = true;
+ break;
default:
break;
}
struct aub_file aub = {};
- uint32_t active_ring = 0;
+ enum drm_i915_gem_engine_class active_engine_class = I915_ENGINE_CLASS_INVALID;
+ int active_engine_instance = -1;
+
+ enum address_space active_gtt = PPGTT;
+ enum address_space default_gtt = PPGTT;
+
+ struct {
+ struct {
+ uint32_t ring_buffer_head;
+ uint32_t ring_buffer_tail;
+ } instances[3];
+ } engines[I915_ENGINE_CLASS_VIDEO_ENHANCE + 1];
+ memset(engines, 0, sizeof(engines));
+
int num_ring_bos = 0;
- uint64_t batch_addr = 0;
+ struct list_head bo_list;
+ list_inithead(&bo_list);
- enum bo_type {
- BO_TYPE_UNKNOWN = 0,
- BO_TYPE_BATCH,
- BO_TYPE_USER,
- } bo_type = BO_TYPE_UNKNOWN;
- uint64_t bo_addr = 0;
+ struct bo *last_bo = NULL;
char *line = NULL;
size_t line_size;
aub_file_init(&aub, aub_file,
NULL, pci_id, "error_state");
- fail_if(!aub_use_execlists(&aub),
- "%s currently only works on gen8+\n", argv[0]);
+ if (verbose)
+ aub.verbose_log_file = stdout;
+ default_gtt = active_gtt = aub_use_execlists(&aub) ? PPGTT : GGTT;
+ continue;
+ }
- aub_write_default_setup(&aub);
+ if (strstr(line, " command stream:")) {
+ engine_from_name(line, &active_engine_class, &active_engine_instance);
+ continue;
+ }
+
+ if (sscanf(line, " ring->head: 0x%x\n",
+ &engines[
+ active_engine_class].instances[
+ active_engine_instance].ring_buffer_head) == 1) {
+ continue;
+ }
+
+ if (sscanf(line, " ring->tail: 0x%x\n",
+ &engines[
+ active_engine_class].instances[
+ active_engine_instance].ring_buffer_tail) == 1) {
continue;
}
const char *active_start = "Active (";
if (strncmp(line, active_start, strlen(active_start)) == 0) {
- fail_if(active_ring != 0, "TODO: Handle multiple active rings\n");
-
char *ring = line + strlen(active_start);
- const struct {
- const char *match;
- uint32_t ring;
- } rings[] = {
- { "rcs", I915_EXEC_RENDER },
- { "vcs", I915_EXEC_VEBOX },
- { "bcs", I915_EXEC_BLT },
- { NULL, BO_TYPE_UNKNOWN },
- }, *r;
-
- for (r = rings; r->match; r++) {
- if (strncasecmp(ring, r->match, strlen(r->match)) == 0) {
- active_ring = r->ring;
- break;
- }
- }
+ engine_from_name(ring, &active_engine_class, &active_engine_instance);
+ active_gtt = default_gtt;
char *count = strchr(ring, '[');
fail_if(!count || sscanf(count, "[%d]:", &num_ring_bos) < 1,
continue;
}
+ const char *global_start = "Pinned (global) [";
+ if (strncmp(line, global_start, strlen(global_start)) == 0) {
+ active_engine_class = I915_ENGINE_CLASS_INVALID;
+ active_engine_instance = -1;
+ active_gtt = GGTT;
+ continue;
+ }
+
if (num_ring_bos > 0) {
unsigned hi, lo, size;
if (sscanf(line, " %x_%x %d", &hi, &lo, &size) == 3) {
- assert(aub_use_execlists(&aub));
- aub_map_ppgtt(&aub, ((uint64_t)hi) << 32 | lo, size);
+ struct bo *bo_entry = find_or_create(&bo_list, ((uint64_t)hi) << 32 | lo,
+ active_gtt,
+ active_engine_class,
+ active_engine_instance);
+ bo_entry->size = size;
num_ring_bos--;
} else {
fail("Not enough BO entries in the active table\n");
}
if (line[0] == ':' || line[0] == '~') {
- if (bo_type == BO_TYPE_UNKNOWN)
+ if (!last_bo || last_bo->type == BO_TYPE_UNKNOWN)
continue;
- uint32_t *data = NULL;
- int count = ascii85_decode(line+1, &data, line[0] == ':');
+ int count = ascii85_decode(line+1, (uint32_t **) &last_bo->data, line[0] == ':');
fail_if(count == 0, "ASCII85 decode failed.\n");
- uint64_t bo_size = count * 4;
-
- if (bo_type == BO_TYPE_BATCH) {
- aub_write_trace_block(&aub, AUB_TRACE_TYPE_BATCH,
- data, bo_size, bo_addr);
- batch_addr = bo_addr;
- } else {
- assert(bo_type == BO_TYPE_USER);
- aub_write_trace_block(&aub, AUB_TRACE_TYPE_NOTYPE,
- data, bo_size, bo_addr);
- }
-
+ last_bo->size = count * 4;
continue;
}
- char *dashes = strstr(line, "---");
+ char *dashes = strstr(line, " --- ");
if (dashes) {
- dashes += 4;
+ dashes += 5;
+
+ engine_from_name(line, &active_engine_class, &active_engine_instance);
+
+ uint32_t hi, lo;
+ char *bo_address_str = strchr(dashes, '=');
+ if (!bo_address_str || sscanf(bo_address_str, "= 0x%08x %08x\n", &hi, &lo) != 2)
+ continue;
const struct {
const char *match;
enum bo_type type;
+ enum address_space gtt;
} bo_types[] = {
- { "gtt_offset", BO_TYPE_BATCH },
- { "user", BO_TYPE_USER },
- { NULL, BO_TYPE_UNKNOWN },
+ { "gtt_offset", BO_TYPE_BATCH, default_gtt },
+ { "user", BO_TYPE_USER, default_gtt },
+ { "HW context", BO_TYPE_CONTEXT, GGTT },
+ { "ringbuffer", BO_TYPE_RINGBUFFER, GGTT },
+ { "HW Status", BO_TYPE_STATUS, GGTT },
+ { "WA context", BO_TYPE_CONTEXT_WA, GGTT },
+ { "unknown", BO_TYPE_UNKNOWN, GGTT },
}, *b;
- bo_type = BO_TYPE_UNKNOWN;
- for (b = bo_types; b->match; b++) {
- if (strncasecmp(dashes, b->match, strlen(b->match)) == 0) {
- bo_type = b->type;
+ for (b = bo_types; b->type != BO_TYPE_UNKNOWN; b++) {
+ if (strncasecmp(dashes, b->match, strlen(b->match)) == 0)
break;
- }
}
- if (bo_type != BO_TYPE_UNKNOWN) {
- uint32_t hi, lo;
- dashes = strchr(dashes, '=');
- if (dashes && sscanf(dashes, "= 0x%08x %08x\n", &hi, &lo) == 2) {
- bo_addr = ((uint64_t) hi) << 32 | lo;
- } else {
- fail("User BO does not have an address\n");
- }
+ last_bo = find_or_create(&bo_list, ((uint64_t) hi) << 32 | lo,
+ b->gtt,
+ active_engine_class, active_engine_instance);
+
+ /* The batch buffer will appear twice as gtt_offset and user. Only
+ * keep the batch type.
+ */
+ if (last_bo->type == BO_TYPE_UNKNOWN) {
+ last_bo->type = b->type;
+ last_bo->name = b->match;
}
+
continue;
}
}
- fail_if(!batch_addr, "Failed to find batch buffer.\n");
+ if (verbose) {
+ fprintf(stdout, "BOs found:\n");
+ list_for_each_entry(struct bo, bo_entry, &bo_list, link) {
+ fprintf(stdout, "\t type=%i addr=0x%016" PRIx64 " size=%" PRIu64 "\n",
+ bo_entry->type, bo_entry->addr, bo_entry->size);
+ }
+ }
+
+ /* Find the batch that trigger the hang */
+ struct bo *batch_bo = NULL;
+ list_for_each_entry(struct bo, bo_entry, &bo_list, link) {
+ if (bo_entry->type == BO_TYPE_BATCH) {
+ batch_bo = bo_entry;
+ break;
+ }
+ }
+ fail_if(!batch_bo, "Failed to find batch buffer.\n");
+
+ /* Add all the BOs to the aub file */
+ struct bo *hwsp_bo = NULL;
+ list_for_each_entry(struct bo, bo_entry, &bo_list, link) {
+ switch (bo_entry->type) {
+ case BO_TYPE_BATCH:
+ if (bo_entry->gtt == PPGTT) {
+ aub_map_ppgtt(&aub, bo_entry->addr, bo_entry->size);
+ aub_write_trace_block(&aub, AUB_TRACE_TYPE_BATCH,
+ bo_entry->data, bo_entry->size, bo_entry->addr);
+ } else
+ aub_write_ggtt(&aub, bo_entry->addr, bo_entry->size, bo_entry->data);
+ break;
+ case BO_TYPE_USER:
+ if (bo_entry->gtt == PPGTT) {
+ aub_map_ppgtt(&aub, bo_entry->addr, bo_entry->size);
+ aub_write_trace_block(&aub, AUB_TRACE_TYPE_NOTYPE,
+ bo_entry->data, bo_entry->size, bo_entry->addr);
+ } else
+ aub_write_ggtt(&aub, bo_entry->addr, bo_entry->size, bo_entry->data);
+ break;
+ case BO_TYPE_CONTEXT:
+ if (bo_entry->engine_class == batch_bo->engine_class &&
+ bo_entry->engine_instance == batch_bo->engine_instance &&
+ aub_use_execlists(&aub)) {
+ hwsp_bo = bo_entry;
+
+ uint32_t *context = (uint32_t *) (bo_entry->data + 4096 /* GuC */ + 4096 /* HWSP */);
+
+ if (context[1] == 0) {
+ fprintf(stderr,
+ "Invalid context image data.\n"
+ "This is likely a kernel issue : https://bugs.freedesktop.org/show_bug.cgi?id=107691\n");
+ }
+
+ /* Update the ring buffer at the last known location. */
+ context[5] = engines[bo_entry->engine_class].instances[bo_entry->engine_instance].ring_buffer_head;
+ context[7] = engines[bo_entry->engine_class].instances[bo_entry->engine_instance].ring_buffer_tail;
+ fprintf(stdout, "engine start=0x%x head/tail=0x%x/0x%x\n",
+ context[9], context[5], context[7]);
+
+ /* The error state doesn't provide a dump of the page tables, so
+ * we have to provide our own, that's easy enough.
+ */
+ context[49] = aub.pml4.phys_addr >> 32;
+ context[51] = aub.pml4.phys_addr & 0xffffffff;
+
+ fprintf(stdout, "context dump:\n");
+ for (int i = 0; i < 60; i++) {
+ if (i % 4 == 0)
+ fprintf(stdout, "\n 0x%08" PRIx64 ": ", bo_entry->addr + 8192 + i * 4);
+ fprintf(stdout, "0x%08x ", context[i]);
+ }
+ fprintf(stdout, "\n");
+
+ }
+ aub_write_ggtt(&aub, bo_entry->addr, bo_entry->size, bo_entry->data);
+ break;
+ case BO_TYPE_RINGBUFFER:
+ case BO_TYPE_STATUS:
+ case BO_TYPE_CONTEXT_WA:
+ aub_write_ggtt(&aub, bo_entry->addr, bo_entry->size, bo_entry->data);
+ break;
+ case BO_TYPE_UNKNOWN:
+ if (bo_entry->gtt == PPGTT) {
+ aub_map_ppgtt(&aub, bo_entry->addr, bo_entry->size);
+ if (bo_entry->data) {
+ aub_write_trace_block(&aub, AUB_TRACE_TYPE_NOTYPE,
+ bo_entry->data, bo_entry->size, bo_entry->addr);
+ }
+ } else {
+ if (bo_entry->size > 0) {
+ void *zero_data = calloc(1, bo_entry->size);
+ aub_write_ggtt(&aub, bo_entry->addr, bo_entry->size, zero_data);
+ free(zero_data);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (aub_use_execlists(&aub)) {
+ fail_if(!hwsp_bo, "Failed to find Context buffer.\n");
+ aub_write_context_execlists(&aub, hwsp_bo->addr + 4096 /* skip GuC page */, hwsp_bo->engine_class);
+ } else {
+ /* Use context id 0 -- if we are not using execlists it doesn't matter
+ * anyway
+ */
+ aub_write_exec(&aub, 0, batch_bo->addr, 0, I915_ENGINE_CLASS_RENDER);
+ }
- aub_write_exec(&aub, batch_addr, aub_gtt_size(&aub), I915_ENGINE_CLASS_RENDER);
+ /* Cleanup */
+ list_for_each_entry_safe(struct bo, bo_entry, &bo_list, link) {
+ list_del(&bo_entry->link);
+ free(bo_entry->data);
+ free(bo_entry);
+ }
free(out_filename);
free(line);