Path: blob/21.2-virgl/src/intel/tools/error2aub.c
4547 views
/*1* Copyright © 2018 Intel Corporation2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS20* IN THE SOFTWARE.21*22*/2324#include <assert.h>25#include <getopt.h>26#include <inttypes.h>27#include <signal.h>28#include <stdio.h>29#include <stdlib.h>30#include <string.h>31#include <stdarg.h>32#include <zlib.h>3334#include "util/list.h"3536#include "aub_write.h"37#include "drm-uapi/i915_drm.h"38#include "intel_aub.h"3940#define fail_if(cond, ...) _fail_if(cond, NULL, __VA_ARGS__)4142#define fail(...) fail_if(true, __VA_ARGS__)4344static int zlib_inflate(uint32_t **ptr, int len)45{46struct z_stream_s zstream;47void *out;48const uint32_t out_size = 128*4096; /* approximate obj size */4950memset(&zstream, 0, sizeof(zstream));5152zstream.next_in = (unsigned char *)*ptr;53zstream.avail_in = 4*len;5455if (inflateInit(&zstream) != Z_OK)56return 0;5758out = malloc(out_size);59zstream.next_out = out;60zstream.avail_out = out_size;6162do {63switch (inflate(&zstream, Z_SYNC_FLUSH)) {64case Z_STREAM_END:65goto end;66case Z_OK:67break;68default:69inflateEnd(&zstream);70return 0;71}7273if (zstream.avail_out)74break;7576out = realloc(out, 2*zstream.total_out);77if (out == NULL) {78inflateEnd(&zstream);79return 0;80}8182zstream.next_out = (unsigned char *)out + zstream.total_out;83zstream.avail_out = zstream.total_out;84} while (1);85end:86inflateEnd(&zstream);87free(*ptr);88*ptr = out;89return zstream.total_out / 4;90}9192static int ascii85_decode(const char *in, uint32_t **out, bool inflate)93{94int len = 0, size = 1024;9596*out = realloc(*out, sizeof(uint32_t)*size);97if (*out == NULL)98return 0;99100while (*in >= '!' && *in <= 'z') {101uint32_t v = 0;102103if (len == size) {104size *= 2;105*out = realloc(*out, sizeof(uint32_t)*size);106if (*out == NULL)107return 0;108}109110if (*in == 'z') {111in++;112} else {113v += in[0] - 33; v *= 85;114v += in[1] - 33; v *= 85;115v += in[2] - 33; v *= 85;116v += in[3] - 33; v *= 85;117v += in[4] - 33;118in += 5;119}120(*out)[len++] = v;121}122123if (!inflate)124return len;125126return zlib_inflate(out, len);127}128129static void130print_help(const char *progname, FILE *file)131{132fprintf(file,133"Usage: %s [OPTION]... [FILE]\n"134"Convert an Intel GPU i915 error state to an aub file.\n"135" -h, --help display this help and exit\n"136" -o, --output=FILE the output aub file (default FILE.aub)\n",137progname);138}139140struct bo {141enum address_space {142PPGTT,143GGTT,144} gtt;145enum bo_type {146BO_TYPE_UNKNOWN = 0,147BO_TYPE_BATCH,148BO_TYPE_USER,149BO_TYPE_CONTEXT,150BO_TYPE_RINGBUFFER,151BO_TYPE_STATUS,152BO_TYPE_CONTEXT_WA,153} type;154const char *name;155uint64_t addr;156uint8_t *data;157uint64_t size;158159enum drm_i915_gem_engine_class engine_class;160int engine_instance;161162struct list_head link;163};164165static struct bo *166find_or_create(struct list_head *bo_list, uint64_t addr,167enum address_space gtt,168enum drm_i915_gem_engine_class engine_class,169int engine_instance)170{171list_for_each_entry(struct bo, bo_entry, bo_list, link) {172if (bo_entry->addr == addr &&173bo_entry->gtt == gtt &&174bo_entry->engine_class == engine_class &&175bo_entry->engine_instance == engine_instance)176return bo_entry;177}178179struct bo *new_bo = calloc(1, sizeof(*new_bo));180new_bo->addr = addr;181new_bo->gtt = gtt;182new_bo->engine_class = engine_class;183new_bo->engine_instance = engine_instance;184list_addtail(&new_bo->link, bo_list);185186return new_bo;187}188189static void190engine_from_name(const char *engine_name,191enum drm_i915_gem_engine_class *engine_class,192int *engine_instance)193{194const struct {195const char *match;196enum drm_i915_gem_engine_class engine_class;197bool parse_instance;198} rings[] = {199{ "rcs", I915_ENGINE_CLASS_RENDER, true },200{ "vcs", I915_ENGINE_CLASS_VIDEO, true },201{ "vecs", I915_ENGINE_CLASS_VIDEO_ENHANCE, true },202{ "bcs", I915_ENGINE_CLASS_COPY, true },203{ "global", I915_ENGINE_CLASS_INVALID, false },204{ "render command stream", I915_ENGINE_CLASS_RENDER, false },205{ "blt command stream", I915_ENGINE_CLASS_COPY, false },206{ "bsd command stream", I915_ENGINE_CLASS_VIDEO, false },207{ "vebox command stream", I915_ENGINE_CLASS_VIDEO_ENHANCE, false },208{ NULL, I915_ENGINE_CLASS_INVALID },209}, *r;210211for (r = rings; r->match; r++) {212if (strncasecmp(engine_name, r->match, strlen(r->match)) == 0) {213*engine_class = r->engine_class;214if (r->parse_instance)215*engine_instance = strtol(engine_name + strlen(r->match), NULL, 10);216else217*engine_instance = 0;218return;219}220}221222fail("Unknown engine %s\n", engine_name);223}224225int226main(int argc, char *argv[])227{228int i, c;229bool help = false, verbose = false;230char *out_filename = NULL, *in_filename = NULL;231const struct option aubinator_opts[] = {232{ "help", no_argument, NULL, 'h' },233{ "output", required_argument, NULL, 'o' },234{ "verbose", no_argument, NULL, 'v' },235{ NULL, 0, NULL, 0 }236};237238i = 0;239while ((c = getopt_long(argc, argv, "ho:v", aubinator_opts, &i)) != -1) {240switch (c) {241case 'h':242help = true;243break;244case 'o':245out_filename = strdup(optarg);246break;247case 'v':248verbose = true;249break;250default:251break;252}253}254255if (optind < argc)256in_filename = argv[optind++];257258if (help || argc == 1 || !in_filename) {259print_help(argv[0], stderr);260return in_filename ? EXIT_SUCCESS : EXIT_FAILURE;261}262263if (out_filename == NULL) {264int out_filename_size = strlen(in_filename) + 5;265out_filename = malloc(out_filename_size);266snprintf(out_filename, out_filename_size, "%s.aub", in_filename);267}268269FILE *err_file = fopen(in_filename, "r");270fail_if(!err_file, "Failed to open error file \"%s\": %m\n", in_filename);271272FILE *aub_file = fopen(out_filename, "w");273fail_if(!aub_file, "Failed to open aub file \"%s\": %m\n", in_filename);274275struct aub_file aub = {};276277enum drm_i915_gem_engine_class active_engine_class = I915_ENGINE_CLASS_INVALID;278int active_engine_instance = -1;279280enum address_space active_gtt = PPGTT;281enum address_space default_gtt = PPGTT;282283struct {284struct {285uint32_t ring_buffer_head;286uint32_t ring_buffer_tail;287} instances[3];288} engines[I915_ENGINE_CLASS_VIDEO_ENHANCE + 1];289memset(engines, 0, sizeof(engines));290291int num_ring_bos = 0;292293struct list_head bo_list;294list_inithead(&bo_list);295296struct bo *last_bo = NULL;297298char *line = NULL;299size_t line_size;300while (getline(&line, &line_size, err_file) > 0) {301const char *pci_id_start = strstr(line, "PCI ID");302if (pci_id_start) {303int pci_id;304int matched = sscanf(line, "PCI ID: 0x%04x\n", &pci_id);305fail_if(!matched, "Invalid error state file!\n");306307aub_file_init(&aub, aub_file,308NULL, pci_id, "error_state");309if (verbose)310aub.verbose_log_file = stdout;311default_gtt = active_gtt = aub_use_execlists(&aub) ? PPGTT : GGTT;312continue;313}314315if (strstr(line, " command stream:")) {316engine_from_name(line, &active_engine_class, &active_engine_instance);317continue;318}319320if (sscanf(line, " ring->head: 0x%x\n",321&engines[322active_engine_class].instances[323active_engine_instance].ring_buffer_head) == 1) {324continue;325}326327if (sscanf(line, " ring->tail: 0x%x\n",328&engines[329active_engine_class].instances[330active_engine_instance].ring_buffer_tail) == 1) {331continue;332}333334const char *active_start = "Active (";335if (strncmp(line, active_start, strlen(active_start)) == 0) {336char *ring = line + strlen(active_start);337338engine_from_name(ring, &active_engine_class, &active_engine_instance);339active_gtt = default_gtt;340341char *count = strchr(ring, '[');342fail_if(!count || sscanf(count, "[%d]:", &num_ring_bos) < 1,343"Failed to parse BO table header\n");344continue;345}346347const char *global_start = "Pinned (global) [";348if (strncmp(line, global_start, strlen(global_start)) == 0) {349active_engine_class = I915_ENGINE_CLASS_INVALID;350active_engine_instance = -1;351active_gtt = GGTT;352continue;353}354355if (num_ring_bos > 0) {356unsigned hi, lo, size;357if (sscanf(line, " %x_%x %d", &hi, &lo, &size) == 3) {358struct bo *bo_entry = find_or_create(&bo_list, ((uint64_t)hi) << 32 | lo,359active_gtt,360active_engine_class,361active_engine_instance);362bo_entry->size = size;363num_ring_bos--;364} else {365fail("Not enough BO entries in the active table\n");366}367continue;368}369370if (line[0] == ':' || line[0] == '~') {371if (!last_bo || last_bo->type == BO_TYPE_UNKNOWN)372continue;373374int count = ascii85_decode(line+1, (uint32_t **) &last_bo->data, line[0] == ':');375fail_if(count == 0, "ASCII85 decode failed.\n");376last_bo->size = count * 4;377continue;378}379380char *dashes = strstr(line, " --- ");381if (dashes) {382dashes += 5;383384engine_from_name(line, &active_engine_class, &active_engine_instance);385386uint32_t hi, lo;387char *bo_address_str = strchr(dashes, '=');388if (!bo_address_str || sscanf(bo_address_str, "= 0x%08x %08x\n", &hi, &lo) != 2)389continue;390391const struct {392const char *match;393enum bo_type type;394enum address_space gtt;395} bo_types[] = {396{ "gtt_offset", BO_TYPE_BATCH, default_gtt },397{ "user", BO_TYPE_USER, default_gtt },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 },403}, *b;404405for (b = bo_types; b->type != BO_TYPE_UNKNOWN; b++) {406if (strncasecmp(dashes, b->match, strlen(b->match)) == 0)407break;408}409410last_bo = find_or_create(&bo_list, ((uint64_t) hi) << 32 | lo,411b->gtt,412active_engine_class, active_engine_instance);413414/* The batch buffer will appear twice as gtt_offset and user. Only415* keep the batch type.416*/417if (last_bo->type == BO_TYPE_UNKNOWN) {418last_bo->type = b->type;419last_bo->name = b->match;420}421422continue;423}424}425426if (verbose) {427fprintf(stdout, "BOs found:\n");428list_for_each_entry(struct bo, bo_entry, &bo_list, link) {429fprintf(stdout, "\t type=%i addr=0x%016" PRIx64 " size=%" PRIu64 "\n",430bo_entry->type, bo_entry->addr, bo_entry->size);431}432}433434/* Find the batch that trigger the hang */435struct bo *batch_bo = NULL;436list_for_each_entry(struct bo, bo_entry, &bo_list, link) {437if (bo_entry->type == BO_TYPE_BATCH) {438batch_bo = bo_entry;439break;440}441}442fail_if(!batch_bo, "Failed to find batch buffer.\n");443444/* Add all the BOs to the aub file */445struct bo *hwsp_bo = NULL;446list_for_each_entry(struct bo, bo_entry, &bo_list, link) {447switch (bo_entry->type) {448case BO_TYPE_BATCH:449if (bo_entry->gtt == PPGTT) {450aub_map_ppgtt(&aub, bo_entry->addr, bo_entry->size);451aub_write_trace_block(&aub, AUB_TRACE_TYPE_BATCH,452bo_entry->data, bo_entry->size, bo_entry->addr);453} else454aub_write_ggtt(&aub, bo_entry->addr, bo_entry->size, bo_entry->data);455break;456case BO_TYPE_USER:457if (bo_entry->gtt == PPGTT) {458aub_map_ppgtt(&aub, bo_entry->addr, bo_entry->size);459aub_write_trace_block(&aub, AUB_TRACE_TYPE_NOTYPE,460bo_entry->data, bo_entry->size, bo_entry->addr);461} else462aub_write_ggtt(&aub, bo_entry->addr, bo_entry->size, bo_entry->data);463break;464case BO_TYPE_CONTEXT:465if (bo_entry->engine_class == batch_bo->engine_class &&466bo_entry->engine_instance == batch_bo->engine_instance &&467aub_use_execlists(&aub)) {468hwsp_bo = bo_entry;469470uint32_t *context = (uint32_t *) (bo_entry->data + 4096 /* GuC */ + 4096 /* HWSP */);471472if (context[1] == 0) {473fprintf(stderr,474"Invalid context image data.\n"475"This is likely a kernel issue : https://bugs.freedesktop.org/show_bug.cgi?id=107691\n");476}477478/* Update the ring buffer at the last known location. */479context[5] = engines[bo_entry->engine_class].instances[bo_entry->engine_instance].ring_buffer_head;480context[7] = engines[bo_entry->engine_class].instances[bo_entry->engine_instance].ring_buffer_tail;481fprintf(stdout, "engine start=0x%x head/tail=0x%x/0x%x\n",482context[9], context[5], context[7]);483484/* The error state doesn't provide a dump of the page tables, so485* we have to provide our own, that's easy enough.486*/487context[49] = aub.pml4.phys_addr >> 32;488context[51] = aub.pml4.phys_addr & 0xffffffff;489490fprintf(stdout, "context dump:\n");491for (int i = 0; i < 60; i++) {492if (i % 4 == 0)493fprintf(stdout, "\n 0x%08" PRIx64 ": ", bo_entry->addr + 8192 + i * 4);494fprintf(stdout, "0x%08x ", context[i]);495}496fprintf(stdout, "\n");497498}499aub_write_ggtt(&aub, bo_entry->addr, bo_entry->size, bo_entry->data);500break;501case BO_TYPE_RINGBUFFER:502case BO_TYPE_STATUS:503case BO_TYPE_CONTEXT_WA:504aub_write_ggtt(&aub, bo_entry->addr, bo_entry->size, bo_entry->data);505break;506case BO_TYPE_UNKNOWN:507if (bo_entry->gtt == PPGTT) {508aub_map_ppgtt(&aub, bo_entry->addr, bo_entry->size);509if (bo_entry->data) {510aub_write_trace_block(&aub, AUB_TRACE_TYPE_NOTYPE,511bo_entry->data, bo_entry->size, bo_entry->addr);512}513} else {514if (bo_entry->size > 0) {515void *zero_data = calloc(1, bo_entry->size);516aub_write_ggtt(&aub, bo_entry->addr, bo_entry->size, zero_data);517free(zero_data);518}519}520break;521default:522break;523}524}525526if (aub_use_execlists(&aub)) {527fail_if(!hwsp_bo, "Failed to find Context buffer.\n");528aub_write_context_execlists(&aub, hwsp_bo->addr + 4096 /* skip GuC page */, hwsp_bo->engine_class);529} else {530/* Use context id 0 -- if we are not using execlists it doesn't matter531* anyway532*/533aub_write_exec(&aub, 0, batch_bo->addr, 0, I915_ENGINE_CLASS_RENDER);534}535536/* Cleanup */537list_for_each_entry_safe(struct bo, bo_entry, &bo_list, link) {538list_del(&bo_entry->link);539free(bo_entry->data);540free(bo_entry);541}542543free(out_filename);544free(line);545if(err_file) {546fclose(err_file);547}548if(aub.file) {549aub_file_finish(&aub);550} else if(aub_file) {551fclose(aub_file);552}553return EXIT_SUCCESS;554}555556/* vim: set ts=8 sw=8 tw=0 cino=:0,(0 noet :*/557558559