• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2022 Intel Corporation
3  *
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:
10  *
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
13  * Software.
14  *
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
21  * IN THE SOFTWARE.
22  *
23  */
24 
25 #include <assert.h>
26 #include <getopt.h>
27 #include <inttypes.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include <zlib.h>
34 
35 #include "util/list.h"
36 
37 #include "common/intel_hang_dump.h"
38 
39 #include "drm-uapi/i915_drm.h"
40 
41 static inline void
_fail(const char * prefix,const char * format,...)42 _fail(const char *prefix, const char *format, ...)
43 {
44    va_list args;
45 
46    va_start(args, format);
47    if (prefix)
48       fprintf(stderr, "%s: ", prefix);
49    vfprintf(stderr, format, args);
50    va_end(args);
51 
52    abort();
53 }
54 
55 #define _fail_if(cond, prefix, ...) do { \
56    if (cond) \
57       _fail(prefix, __VA_ARGS__); \
58 } while (0)
59 
60 #define fail_if(cond, ...) _fail_if(cond, NULL, __VA_ARGS__)
61 
62 #define fail(...) fail_if(true, __VA_ARGS__)
63 
zlib_inflate(uint32_t ** ptr,int len)64 static int zlib_inflate(uint32_t **ptr, int len)
65 {
66    struct z_stream_s zstream;
67    void *out;
68    const uint32_t out_size = 128*4096;  /* approximate obj size */
69 
70    memset(&zstream, 0, sizeof(zstream));
71 
72    zstream.next_in = (unsigned char *)*ptr;
73    zstream.avail_in = 4*len;
74 
75    if (inflateInit(&zstream) != Z_OK)
76       return 0;
77 
78    out = malloc(out_size);
79    zstream.next_out = out;
80    zstream.avail_out = out_size;
81 
82    do {
83       switch (inflate(&zstream, Z_SYNC_FLUSH)) {
84       case Z_STREAM_END:
85          goto end;
86       case Z_OK:
87          break;
88       default:
89          inflateEnd(&zstream);
90          return 0;
91       }
92 
93       if (zstream.avail_out)
94          break;
95 
96       out = realloc(out, 2*zstream.total_out);
97       if (out == NULL) {
98          inflateEnd(&zstream);
99          return 0;
100       }
101 
102       zstream.next_out = (unsigned char *)out + zstream.total_out;
103       zstream.avail_out = zstream.total_out;
104    } while (1);
105  end:
106    inflateEnd(&zstream);
107    free(*ptr);
108    *ptr = out;
109    return zstream.total_out / 4;
110 }
111 
ascii85_decode(const char * in,uint32_t ** out,bool inflate)112 static int ascii85_decode(const char *in, uint32_t **out, bool inflate)
113 {
114    int len = 0, size = 1024;
115 
116    *out = realloc(*out, sizeof(uint32_t)*size);
117    if (*out == NULL)
118       return 0;
119 
120    while (*in >= '!' && *in <= 'z') {
121       uint32_t v = 0;
122 
123       if (len == size) {
124          size *= 2;
125          *out = realloc(*out, sizeof(uint32_t)*size);
126          if (*out == NULL)
127             return 0;
128       }
129 
130       if (*in == 'z') {
131          in++;
132       } else {
133          v += in[0] - 33; v *= 85;
134          v += in[1] - 33; v *= 85;
135          v += in[2] - 33; v *= 85;
136          v += in[3] - 33; v *= 85;
137          v += in[4] - 33;
138          in += 5;
139       }
140       (*out)[len++] = v;
141    }
142 
143    if (!inflate)
144       return len;
145 
146    return zlib_inflate(out, len);
147 }
148 
149 static void
print_help(const char * progname,FILE * file)150 print_help(const char *progname, FILE *file)
151 {
152    fprintf(file,
153            "Usage: %s [OPTION]... [FILE]\n"
154            "Convert an Intel GPU i915 error state to a hang dump file, replayable with intel_hang_replay.\n"
155            "  -h, --help          display this help and exit\n"
156            "  -o, --output=FILE   the output dump file (default FILE.dmp)\n",
157            progname);
158 }
159 
160 struct bo {
161    enum address_space {
162       PPGTT,
163       GGTT,
164    } gtt;
165    enum bo_type {
166       BO_TYPE_UNKNOWN = 0,
167       BO_TYPE_BATCH,
168       BO_TYPE_USER,
169       BO_TYPE_CONTEXT,
170       BO_TYPE_RINGBUFFER,
171       BO_TYPE_STATUS,
172       BO_TYPE_CONTEXT_WA,
173    } type;
174    const char *name;
175    uint64_t addr;
176    uint8_t *data;
177    uint64_t size;
178 
179    enum drm_i915_gem_engine_class engine_class;
180    int engine_instance;
181 
182    struct list_head link;
183 };
184 
185 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)186 find_or_create(struct list_head *bo_list, uint64_t addr,
187                enum address_space gtt,
188                enum drm_i915_gem_engine_class engine_class,
189                int engine_instance)
190 {
191    list_for_each_entry(struct bo, bo_entry, bo_list, link) {
192       if (bo_entry->addr == addr &&
193           bo_entry->gtt == gtt &&
194           bo_entry->engine_class == engine_class &&
195           bo_entry->engine_instance == engine_instance)
196          return bo_entry;
197    }
198 
199    struct bo *new_bo = calloc(1, sizeof(*new_bo));
200    new_bo->addr = addr;
201    new_bo->gtt = gtt;
202    new_bo->engine_class = engine_class;
203    new_bo->engine_instance = engine_instance;
204    list_addtail(&new_bo->link, bo_list);
205 
206    return new_bo;
207 }
208 
209 static void
engine_from_name(const char * engine_name,enum drm_i915_gem_engine_class * engine_class,int * engine_instance)210 engine_from_name(const char *engine_name,
211                  enum drm_i915_gem_engine_class *engine_class,
212                  int *engine_instance)
213 {
214    const struct {
215       const char *match;
216       enum drm_i915_gem_engine_class engine_class;
217       bool parse_instance;
218    } rings[] = {
219       { "rcs", I915_ENGINE_CLASS_RENDER, true },
220       { "vcs", I915_ENGINE_CLASS_VIDEO, true },
221       { "vecs", I915_ENGINE_CLASS_VIDEO_ENHANCE, true },
222       { "bcs", I915_ENGINE_CLASS_COPY, true },
223       { "global", I915_ENGINE_CLASS_INVALID, false },
224       { "render command stream", I915_ENGINE_CLASS_RENDER, false },
225       { "blt command stream", I915_ENGINE_CLASS_COPY, false },
226       { "bsd command stream", I915_ENGINE_CLASS_VIDEO, false },
227       { "vebox command stream", I915_ENGINE_CLASS_VIDEO_ENHANCE, false },
228       { NULL, I915_ENGINE_CLASS_INVALID },
229    }, *r;
230 
231    for (r = rings; r->match; r++) {
232       if (strncasecmp(engine_name, r->match, strlen(r->match)) == 0) {
233          *engine_class = r->engine_class;
234          if (r->parse_instance)
235             *engine_instance = strtol(engine_name + strlen(r->match), NULL, 10);
236          else
237             *engine_instance = 0;
238          return;
239       }
240    }
241 
242    fail("Unknown engine %s\n", engine_name);
243 }
244 
245 static void
write_header(FILE * f)246 write_header(FILE *f)
247 {
248    struct intel_hang_dump_block_header header = {
249       .base = {
250          .type = INTEL_HANG_DUMP_BLOCK_TYPE_HEADER,
251       },
252       .magic   = INTEL_HANG_DUMP_MAGIC,
253       .version = INTEL_HANG_DUMP_VERSION,
254    };
255 
256    fwrite(&header, sizeof(header), 1, f);
257 }
258 
259 static void
write_buffer(FILE * f,uint64_t offset,const void * data,uint64_t size,const char * name)260 write_buffer(FILE *f,
261              uint64_t offset,
262              const void *data,
263              uint64_t size,
264              const char *name)
265 {
266    struct intel_hang_dump_block_bo header = {
267       .base = {
268          .type = INTEL_HANG_DUMP_BLOCK_TYPE_BO,
269       },
270       .offset  = offset,
271       .size    = size,
272    };
273    snprintf(header.name, sizeof(header.name), "%s", name);
274 
275    fwrite(&header, sizeof(header), 1, f);
276    fwrite(data, size, 1, f);
277 }
278 
279 static void
write_hw_image_buffer(FILE * f,const void * data,uint64_t size)280 write_hw_image_buffer(FILE *f, const void *data, uint64_t size)
281 {
282    struct intel_hang_dump_block_hw_image header = {
283       .base = {
284          .type = INTEL_HANG_DUMP_BLOCK_TYPE_HW_IMAGE,
285       },
286       .size    = size,
287    };
288 
289    fwrite(&header, sizeof(header), 1, f);
290    fwrite(data, size, 1, f);
291 }
292 
293 static void
write_exec(FILE * f,uint64_t offset)294 write_exec(FILE *f, uint64_t offset)
295 {
296    struct intel_hang_dump_block_exec header = {
297       .base = {
298          .type = INTEL_HANG_DUMP_BLOCK_TYPE_EXEC,
299       },
300       .offset  = offset,
301    };
302 
303    fwrite(&header, sizeof(header), 1, f);
304 }
305 
306 int
main(int argc,char * argv[])307 main(int argc, char *argv[])
308 {
309    int i, c;
310    bool help = false, verbose = false;
311    char *out_filename = NULL, *in_filename = NULL, *capture_engine_name = "rcs";
312    const struct option aubinator_opts[] = {
313       { "help",       no_argument,       NULL,     'h' },
314       { "output",     required_argument, NULL,     'o' },
315       { "verbose",    no_argument,       NULL,     'v' },
316       { "engine",     required_argument, NULL,     'e' },
317       { NULL,         0,                 NULL,     0 }
318    };
319 
320    i = 0;
321    while ((c = getopt_long(argc, argv, "ho:v", aubinator_opts, &i)) != -1) {
322       switch (c) {
323       case 'h':
324          help = true;
325          break;
326       case 'o':
327          out_filename = strdup(optarg);
328          break;
329       case 'v':
330          verbose = true;
331          break;
332       case 'e':
333          capture_engine_name = optarg;
334          break;
335       default:
336          break;
337       }
338    }
339 
340    if (optind < argc)
341       in_filename = argv[optind++];
342 
343    if (help || argc == 1 || !in_filename) {
344       print_help(argv[0], stderr);
345       return in_filename ? EXIT_SUCCESS : EXIT_FAILURE;
346    }
347 
348    enum drm_i915_gem_engine_class capture_engine;
349    engine_from_name(capture_engine_name, &capture_engine, &c);
350 
351    if (out_filename == NULL) {
352       int out_filename_size = strlen(in_filename) + 5;
353       out_filename = malloc(out_filename_size);
354       snprintf(out_filename, out_filename_size, "%s.dmp", in_filename);
355    }
356 
357    FILE *err_file = fopen(in_filename, "r");
358    fail_if(!err_file, "Failed to open error file \"%s\": %m\n", in_filename);
359 
360    FILE *hang_file = fopen(out_filename, "w");
361    fail_if(!hang_file, "Failed to open aub file \"%s\": %m\n", in_filename);
362 
363    enum address_space active_gtt = PPGTT;
364    enum address_space default_gtt = PPGTT;
365 
366    int num_ring_bos = 0;
367 
368    struct list_head bo_list;
369    list_inithead(&bo_list);
370 
371    struct bo *last_bo = NULL;
372 
373    enum drm_i915_gem_engine_class active_engine_class = I915_ENGINE_CLASS_INVALID;
374    int active_engine_instance = -1;
375 
376    char *line = NULL;
377    size_t line_size;
378    while (getline(&line, &line_size, err_file) > 0) {
379       if (strstr(line, " command stream:")) {
380          engine_from_name(line, &active_engine_class, &active_engine_instance);
381          continue;
382       }
383 
384       if (num_ring_bos > 0) {
385          unsigned hi, lo, size;
386          if (sscanf(line, " %x_%x %d", &hi, &lo, &size) == 3) {
387             struct bo *bo_entry = find_or_create(&bo_list, ((uint64_t)hi) << 32 | lo,
388                                                  active_gtt,
389                                                  active_engine_class,
390                                                  active_engine_instance);
391             bo_entry->size = size;
392             num_ring_bos--;
393          } else {
394             fail("Not enough BO entries in the active table\n");
395          }
396          continue;
397       }
398 
399       if (line[0] == ':' || line[0] == '~') {
400          if (!last_bo || last_bo->type == BO_TYPE_UNKNOWN)
401             continue;
402 
403          int count = ascii85_decode(line+1, (uint32_t **) &last_bo->data, line[0] == ':');
404          fail_if(count == 0, "ASCII85 decode failed.\n");
405          last_bo->size = count * 4;
406          continue;
407       }
408 
409       char *dashes = strstr(line, " --- ");
410       if (dashes) {
411          dashes += 5;
412 
413          engine_from_name(line, &active_engine_class, &active_engine_instance);
414 
415          uint32_t hi, lo;
416          char *bo_address_str = strchr(dashes, '=');
417          if (!bo_address_str || sscanf(bo_address_str, "= 0x%08x %08x\n", &hi, &lo) != 2)
418             continue;
419 
420          const struct {
421             const char *match;
422             enum bo_type type;
423             enum address_space gtt;
424          } bo_types[] = {
425             { "gtt_offset", BO_TYPE_BATCH,      default_gtt },
426             { "batch",      BO_TYPE_BATCH,      default_gtt },
427             { "user",       BO_TYPE_USER,       default_gtt },
428             { "HW context", BO_TYPE_CONTEXT,    GGTT },
429             { "ringbuffer", BO_TYPE_RINGBUFFER, GGTT },
430             { "HW Status",  BO_TYPE_STATUS,     GGTT },
431             { "WA context", BO_TYPE_CONTEXT_WA, GGTT },
432             { "unknown",    BO_TYPE_UNKNOWN,    GGTT },
433          }, *b;
434 
435          for (b = bo_types; b->type != BO_TYPE_UNKNOWN; b++) {
436             if (strncasecmp(dashes, b->match, strlen(b->match)) == 0)
437                break;
438          }
439 
440          last_bo = find_or_create(&bo_list, ((uint64_t) hi) << 32 | lo,
441                                   b->gtt,
442                                   active_engine_class, active_engine_instance);
443 
444          /* The batch buffer will appear twice as gtt_offset and user. Only
445           * keep the batch type.
446           */
447          if (last_bo->type == BO_TYPE_UNKNOWN) {
448             last_bo->type = b->type;
449             last_bo->name = b->match;
450          }
451 
452          continue;
453       }
454    }
455 
456    if (verbose) {
457       fprintf(stdout, "BOs found:\n");
458       list_for_each_entry(struct bo, bo_entry, &bo_list, link) {
459          fprintf(stdout, "\t type=%i addr=0x%016" PRIx64 " size=%" PRIu64 "\n",
460                  bo_entry->type, bo_entry->addr, bo_entry->size);
461       }
462    }
463 
464    /* Find the batch that trigger the hang */
465    struct bo *batch_bo = NULL, *hw_image_bo = NULL;
466    list_for_each_entry(struct bo, bo_entry, &bo_list, link) {
467       if (batch_bo != NULL && hw_image_bo != NULL)
468          break;
469 
470       if (bo_entry->engine_class != capture_engine)
471          continue;
472 
473       switch (bo_entry->type) {
474       case BO_TYPE_BATCH:
475          batch_bo = bo_entry;
476          break;
477       case BO_TYPE_CONTEXT:
478          hw_image_bo = bo_entry;
479          break;
480       default:
481          break;
482       }
483    }
484    fail_if(!batch_bo, "Failed to find batch buffer.\n");
485    fail_if(!hw_image_bo, "Failed to find HW image buffer.\n");
486 
487    /* Add all the user BOs to the aub file */
488    list_for_each_entry(struct bo, bo_entry, &bo_list, link) {
489       if (bo_entry->type == BO_TYPE_USER && bo_entry->gtt == PPGTT)
490          write_buffer(hang_file, bo_entry->addr, bo_entry->data, bo_entry->size, "user");
491    }
492 
493    write_buffer(hang_file, batch_bo->addr, batch_bo->data, batch_bo->size, "batch");
494    fprintf(stderr, "writing image buffer 0x%016"PRIx64" size=0x%016"PRIx64"\n",
495            hw_image_bo->addr, hw_image_bo->size);
496    write_hw_image_buffer(hang_file, hw_image_bo->data, hw_image_bo->size);
497    write_exec(hang_file, batch_bo->addr);
498 
499    /* Cleanup */
500    list_for_each_entry_safe(struct bo, bo_entry, &bo_list, link) {
501       list_del(&bo_entry->link);
502       free(bo_entry->data);
503       free(bo_entry);
504    }
505 
506    free(out_filename);
507    free(line);
508    if (err_file)
509       fclose(err_file);
510    fclose(hang_file);
511 
512    return EXIT_SUCCESS;
513 }
514 
515 /* vim: set ts=8 sw=8 tw=0 cino=:0,(0 noet :*/
516