• 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  * Capture the hanging application with INTEL_DEBUG=capture-all
24  *
25  * Turn the error state into a replay file with :
26  *    $ intel_error2hangdump error_state
27  *
28  * Replay with :
29  *    $ intel_hang_replay -d error_state.dmp
30  */
31 
32 #include <fcntl.h>
33 #include <getopt.h>
34 #include <inttypes.h>
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <sys/mman.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <unistd.h>
42 
43 #include <xf86drm.h>
44 
45 #include "common/intel_gem.h"
46 #include "common/intel_hang_dump.h"
47 #include "compiler/elk/elk_disasm.h"
48 #include "compiler/elk/elk_isa_info.h"
49 #include "compiler/brw_disasm.h"
50 #include "compiler/brw_isa_info.h"
51 #include "dev/intel_device_info.h"
52 
53 #include "drm-uapi/i915_drm.h"
54 
55 #include "util/u_dynarray.h"
56 #include "util/u_math.h"
57 
58 static uint32_t
gem_create(int drm_fd,uint64_t size)59 gem_create(int drm_fd, uint64_t size)
60 {
61    struct drm_i915_gem_create gem_create = {
62       .size = size,
63    };
64 
65    int ret = intel_ioctl(drm_fd, DRM_IOCTL_I915_GEM_CREATE, &gem_create);
66    if (ret != 0) {
67       /* FIXME: What do we do if this fails? */
68       return 0;
69    }
70 
71    return gem_create.handle;
72 }
73 
74 static void*
gem_mmap_offset(int drm_fd,uint32_t gem_handle,uint64_t offset,uint64_t size,uint32_t flags)75 gem_mmap_offset(int drm_fd,
76                 uint32_t gem_handle,
77                 uint64_t offset,
78                 uint64_t size,
79                 uint32_t flags)
80 {
81    struct drm_i915_gem_mmap_offset gem_mmap = {
82       .handle = gem_handle,
83       .flags = I915_MMAP_OFFSET_WB,
84    };
85    assert(offset == 0);
86 
87    /* Get the fake offset back */
88    int ret = intel_ioctl(drm_fd, DRM_IOCTL_I915_GEM_MMAP_OFFSET, &gem_mmap);
89    if (ret != 0 && gem_mmap.flags == I915_MMAP_OFFSET_FIXED) {
90       gem_mmap.flags =
91          (flags & I915_MMAP_WC) ? I915_MMAP_OFFSET_WC : I915_MMAP_OFFSET_WB,
92       ret = intel_ioctl(drm_fd, DRM_IOCTL_I915_GEM_MMAP_OFFSET, &gem_mmap);
93    }
94 
95    if (ret != 0)
96       return MAP_FAILED;
97 
98    /* And map it */
99    void *map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
100                     drm_fd, gem_mmap.offset);
101    return map;
102 }
103 
104 static void
write_gem_bo_data(int drm_fd,uint32_t gem_handle,int file_fd,size_t size)105 write_gem_bo_data(int drm_fd,
106                   uint32_t gem_handle,
107                   int file_fd,
108                   size_t size)
109 {
110    void *map = gem_mmap_offset(drm_fd, gem_handle, 0, size, I915_MMAP_OFFSET_WB);
111    assert(map != MAP_FAILED);
112 
113    size_t total_read_len = 0;
114    ssize_t read_len;
115    while (total_read_len < size &&
116           (read_len = read(file_fd, map + total_read_len, size - total_read_len)) > 0) {
117       total_read_len += read_len;
118    }
119    munmap(map, size);
120 
121    assert(total_read_len == size);
122 }
123 
124 static void
skip_data(int file_fd,size_t size)125 skip_data(int file_fd, size_t size)
126 {
127    lseek(file_fd, size, SEEK_CUR);
128 }
129 
130 static int
get_drm_device(struct intel_device_info * devinfo)131 get_drm_device(struct intel_device_info *devinfo)
132 {
133    drmDevicePtr devices[8];
134    int max_devices = drmGetDevices2(0, devices, 8);
135 
136    int i, fd = -1;
137    for (i = 0; i < max_devices; i++) {
138       if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
139           devices[i]->bustype == DRM_BUS_PCI &&
140           devices[i]->deviceinfo.pci->vendor_id == 0x8086) {
141          fd = open(devices[i]->nodes[DRM_NODE_RENDER], O_RDWR | O_CLOEXEC);
142          if (fd < 0)
143             continue;
144 
145          if (!intel_get_device_info_from_fd(fd, devinfo, -1, -1) ||
146              devinfo->ver < 8) {
147             close(fd);
148             fd = -1;
149             continue;
150          }
151 
152          /* Found a device! */
153          break;
154       }
155    }
156 
157    return fd;
158 }
159 
160 struct gem_bo {
161    off_t    file_offset;
162    uint32_t gem_handle;
163    uint64_t offset;
164    uint64_t size;
165    bool     hw_img;
166 };
167 
168 static int
compare_bos(const void * b1,const void * b2)169 compare_bos(const void *b1, const void *b2)
170 {
171    const struct gem_bo *gem_b1 = b1, *gem_b2 = b2;
172 
173    return gem_b2->size > gem_b1->size;
174 }
175 
176 static void
print_help(const char * filename,FILE * f)177 print_help(const char *filename, FILE *f)
178 {
179    fprintf(f, "%s: %s [options]...\n", filename, filename);
180    fprintf(f, "    -d, --dump FILE    hang file to replay\n");
181    fprintf(f, "    -l, --list         list content of hang file (no replay)\n");
182    fprintf(f, "    -s, --shader ADDR  print shader at ADDR\n");
183    fprintf(f, "    -h, --help         print this screen\n");
184    fprintf(f, "    -a, --address ADDR Find BO containing ADDR\n");
185 }
186 
187 static int
execbuffer(int drm_fd,struct util_dynarray * execbuffer_bos,struct gem_bo * exec_bo,uint64_t exec_offset)188 execbuffer(int drm_fd, struct util_dynarray *execbuffer_bos, struct gem_bo *exec_bo, uint64_t exec_offset)
189 {
190    struct drm_i915_gem_execbuffer2 execbuf = {
191       .buffers_ptr        = (uintptr_t)(void *)util_dynarray_begin(execbuffer_bos),
192       .buffer_count       = util_dynarray_num_elements(execbuffer_bos,
193                                                        struct drm_i915_gem_exec_object2),
194       .batch_start_offset = exec_offset - exec_bo->offset,
195       .batch_len          = exec_bo->size,
196       .flags              = I915_EXEC_HANDLE_LUT | I915_EXEC_RENDER,
197       .rsvd1              = 0,
198    };
199 
200    int ret = intel_ioctl(drm_fd, DRM_IOCTL_I915_GEM_EXECBUFFER2_WR, &execbuf);
201    if (ret == 0) {
202       struct drm_i915_gem_wait gem_wait = {
203          .bo_handle  = exec_bo->gem_handle,
204          .timeout_ns = INT64_MAX,
205       };
206       ret = intel_ioctl(drm_fd, DRM_IOCTL_I915_GEM_WAIT, &gem_wait);
207       if (ret)
208          fprintf(stderr, "wait failed: %m\n");
209    } else {
210       fprintf(stderr, "execbuffer failed: %m\n");
211    }
212 
213    return ret;
214 }
215 
216 int
main(int argc,char * argv[])217 main(int argc, char *argv[])
218 {
219    bool help = false, list = false;
220    const struct option aubinator_opts[] = {
221       { "address",    required_argument, NULL, 'a' },
222       { "dump",       required_argument, NULL, 'd' },
223       { "shader",     required_argument, NULL, 's' },
224       { "list",       no_argument,       NULL, 'l' },
225       { "help",       no_argument,       NULL, 'h' },
226       { NULL,         0,                 NULL,   0 },
227    };
228 
229    void *mem_ctx = ralloc_context(NULL);
230 
231    struct util_dynarray shader_addresses;
232 
233    util_dynarray_init(&shader_addresses, mem_ctx);
234 
235    const char *file = NULL;
236    uint64_t check_addr = -1;
237    int c, i;
238    while ((c = getopt_long(argc, argv, "a:d:hls:", aubinator_opts, &i)) != -1) {
239       switch (c) {
240       case 'a':
241          check_addr = strtol(optarg, NULL, 0);
242          break;
243       case 'd':
244          file = optarg;
245          break;
246       case 's': {
247          uint64_t *addr = util_dynarray_grow(&shader_addresses, uint64_t, 1);
248          *addr = strtol(optarg, NULL, 0);
249          fprintf(stderr, "shader addr=0x%016"PRIx64"\n", *addr);
250          break;
251       }
252       case 'h':
253          help = true;
254          break;
255       case 'l':
256          list = true;
257          break;
258       default:
259          break;
260       }
261    }
262 
263    if (help) {
264       print_help(argv[0], stderr);
265       exit(EXIT_SUCCESS);
266    }
267 
268    int file_fd = open(file, O_RDONLY);
269    if (file_fd < 0)
270       exit(EXIT_FAILURE);
271 
272    struct stat file_stats;
273    if (fstat(file_fd, &file_stats) != 0)
274       exit(EXIT_FAILURE);
275 
276    struct intel_device_info devinfo;
277    int drm_fd = get_drm_device(&devinfo);
278    if (drm_fd < 0)
279       exit(EXIT_FAILURE);
280 
281    struct util_dynarray buffers;
282    uint64_t total_vma = 0;
283 
284    util_dynarray_init(&buffers, mem_ctx);
285 
286    union intel_hang_dump_block_all block_header;
287    struct intel_hang_dump_block_exec init = {
288       .offset = -1,
289    }, exec = {
290       .offset = -1,
291    };
292 
293    while (read(file_fd, &block_header.base, sizeof(block_header.base)) ==
294           sizeof(block_header.base)) {
295 
296       static const size_t block_size[] = {
297          [INTEL_HANG_DUMP_BLOCK_TYPE_HEADER]   = sizeof(struct intel_hang_dump_block_header),
298          [INTEL_HANG_DUMP_BLOCK_TYPE_BO]       = sizeof(struct intel_hang_dump_block_bo),
299          [INTEL_HANG_DUMP_BLOCK_TYPE_MAP]      = sizeof(struct intel_hang_dump_block_map),
300          [INTEL_HANG_DUMP_BLOCK_TYPE_EXEC]     = sizeof(struct intel_hang_dump_block_exec),
301          [INTEL_HANG_DUMP_BLOCK_TYPE_HW_IMAGE] = sizeof(struct intel_hang_dump_block_hw_image),
302       };
303 
304       assert(block_header.base.type < ARRAY_SIZE(block_size));
305 
306       size_t remaining_size = block_size[block_header.base.type] - sizeof(block_header.base);
307       ssize_t ret = read(file_fd, &block_header.base + 1, remaining_size);
308       bool has_hw_image = false;
309       assert(ret == remaining_size);
310 
311       switch (block_header.base.type) {
312       case INTEL_HANG_DUMP_BLOCK_TYPE_HEADER:
313          assert(block_header.header.magic == INTEL_HANG_DUMP_MAGIC);
314          assert(block_header.header.version == INTEL_HANG_DUMP_VERSION);
315          break;
316 
317       case INTEL_HANG_DUMP_BLOCK_TYPE_BO: {
318          struct gem_bo *bo = util_dynarray_grow(&buffers, struct gem_bo, 1);
319          *bo = (struct gem_bo) {
320             .file_offset = lseek(file_fd, 0, SEEK_CUR),
321             .offset = block_header.bo.offset,
322             .size = block_header.bo.size,
323          };
324          total_vma += bo->size;
325          skip_data(file_fd, bo->size);
326          if (list) {
327             fprintf(stderr, "buffer: offset=0x%016"PRIx64" size=0x%016"PRIx64" name=%s\n",
328                     bo->offset, bo->size, block_header.bo.name);
329          }
330          break;
331       }
332 
333       case INTEL_HANG_DUMP_BLOCK_TYPE_HW_IMAGE: {
334          struct gem_bo *bo = util_dynarray_grow(&buffers, struct gem_bo, 1);
335          *bo = (struct gem_bo) {
336             .file_offset = lseek(file_fd, 0, SEEK_CUR),
337             .offset = 0,
338             .size = block_header.hw_img.size,
339             .hw_img = true,
340          };
341          total_vma += bo->size;
342          skip_data(file_fd, bo->size);
343          if (list) {
344             fprintf(stderr, "buffer: offset=0x%016"PRIx64" size=0x%016"PRIx64" name=hw_img\n",
345                     bo->offset, bo->size);
346          }
347          has_hw_image = true;
348          break;
349       }
350 
351       case INTEL_HANG_DUMP_BLOCK_TYPE_MAP: {
352          struct gem_bo *bo = util_dynarray_grow(&buffers, struct gem_bo, 1);
353          *bo = (struct gem_bo) {
354             .file_offset = 0,
355             .offset = block_header.map.offset,
356             .size = block_header.map.size,
357          };
358          total_vma += bo->size;
359          if (list) {
360             fprintf(stderr, "map   : offset=0x%016"PRIx64" size=0x%016"PRIx64" name=%s\n",
361                     bo->offset, bo->size, block_header.map.name);
362          }
363          break;
364       }
365 
366       case INTEL_HANG_DUMP_BLOCK_TYPE_EXEC: {
367          if (init.offset == 0 && !has_hw_image) {
368             if (list)
369                fprintf(stderr, "init  : offset=0x%016"PRIx64"\n", block_header.exec.offset);
370             init = block_header.exec;
371          } else {
372             if (list)
373                fprintf(stderr, "exec  : offset=0x%016"PRIx64"\n", block_header.exec.offset);
374             exec = block_header.exec;
375          }
376          break;
377       }
378 
379       default:
380          unreachable("Invalid block type");
381       }
382    }
383 
384    fprintf(stderr, "total_vma: 0x%016"PRIx64"\n", total_vma);
385 
386    if (check_addr != -1) {
387       struct gem_bo *check_bo = NULL;
388       util_dynarray_foreach(&buffers, struct gem_bo, bo) {
389          if (check_addr >= bo->offset && check_addr < (bo->offset + bo->size)) {
390             check_bo = bo;
391             break;
392          }
393       }
394 
395       if (check_bo) {
396          fprintf(stderr, "address=0x%016"PRIx64" found in buffer 0x%016"PRIx64" size=0x%016"PRIx64"\n",
397                  check_addr, check_bo->offset, check_bo->size);
398       } else {
399          fprintf(stderr, "address=0x%016"PRIx64" not found in buffer list\n", check_addr);
400       }
401    }
402 
403    util_dynarray_foreach(&shader_addresses, uint64_t, addr) {
404       bool found = false;
405       util_dynarray_foreach(&buffers, struct gem_bo, bo) {
406          if (*addr < bo->offset || *addr >= (bo->offset + bo->size))
407             continue;
408          if (!bo->file_offset)
409             break;
410 
411          uint64_t aligned_offset = ROUND_DOWN_TO(bo->file_offset, 4096);
412          uint64_t remaining_length = file_stats.st_size - aligned_offset;
413          void *map = mmap(NULL, remaining_length, PROT_READ, MAP_PRIVATE,
414                           file_fd, aligned_offset);
415          if (map == MAP_FAILED)
416             break;
417 
418          found = true;
419          fprintf(stderr, "shader at 0x%016"PRIx64" file_offset=0%016"PRIx64" addr_offset=%016"PRIx64":\n", *addr,
420                  (bo->file_offset - aligned_offset), (*addr - bo->offset));
421          if (devinfo.ver >= 9) {
422             struct brw_isa_info _isa, *isa = &_isa;
423             brw_init_isa_info(isa, &devinfo);
424             brw_disassemble_with_errors(isa,
425                                         map + (bo->file_offset - aligned_offset) + (*addr - bo->offset),
426                                         0, stderr);
427          } else {
428             struct elk_isa_info _isa, *isa = &_isa;
429             elk_init_isa_info(isa, &devinfo);
430             elk_disassemble_with_errors(isa,
431                                         map + (bo->file_offset - aligned_offset) + (*addr - bo->offset),
432                                         0, stderr);
433          }
434 
435          munmap(map, remaining_length);
436       }
437 
438       if (!found)
439          fprintf(stderr, "shader at 0x%016"PRIx64" not found\n", *addr);
440    }
441 
442    if (!list && util_dynarray_num_elements(&shader_addresses, uint64_t) == 0) {
443       /* Sort buffers by size */
444       qsort(util_dynarray_begin(&buffers),
445             util_dynarray_num_elements(&buffers, struct gem_bo),
446             sizeof(struct gem_bo),
447             compare_bos);
448 
449       /* Allocate BOs populate them */
450       uint64_t gem_allocated = 0;
451       util_dynarray_foreach(&buffers, struct gem_bo, bo) {
452          bo->gem_handle = gem_create(drm_fd, bo->size);
453          if (bo->file_offset != 0) {
454             lseek(file_fd, bo->file_offset, SEEK_SET);
455             write_gem_bo_data(drm_fd, bo->gem_handle, file_fd, bo->size);
456          }
457 
458          gem_allocated += bo->size;
459       }
460 
461       struct util_dynarray execbuffer_bos;
462       util_dynarray_init(&execbuffer_bos, mem_ctx);
463 
464       struct gem_bo *init_bo = NULL, *batch_bo = NULL;
465       util_dynarray_foreach(&buffers, struct gem_bo, bo) {
466          if (bo->offset <= init.offset &&
467              (bo->offset + bo->size) > init.offset) {
468                init_bo = bo;
469                continue;
470          }
471 
472          if (bo->offset <= exec.offset &&
473              (bo->offset + bo->size) > exec.offset) {
474                batch_bo = bo;
475                continue;
476          }
477 
478          struct drm_i915_gem_exec_object2 *execbuf_bo =
479             util_dynarray_grow(&execbuffer_bos, struct drm_i915_gem_exec_object2, 1);
480          *execbuf_bo = (struct drm_i915_gem_exec_object2) {
481             .handle           = bo->gem_handle,
482             .relocation_count = 0,
483             .relocs_ptr       = 0,
484             .flags            = EXEC_OBJECT_SUPPORTS_48B_ADDRESS |
485                                 EXEC_OBJECT_PINNED,
486             .offset           = bo->offset,
487          };
488 
489          if (bo->hw_img)
490             execbuf_bo->flags |= EXEC_OBJECT_NEEDS_GTT;
491       }
492 
493       assert(batch_bo != NULL);
494 
495       struct drm_i915_gem_exec_object2 *execbuf_bo =
496          util_dynarray_grow(&execbuffer_bos, struct drm_i915_gem_exec_object2, 1);
497 
498       int ret;
499 
500       if (init_bo) {
501          fprintf(stderr, "init: 0x%016"PRIx64"\n", init_bo->offset);
502          *execbuf_bo = (struct drm_i915_gem_exec_object2) {
503             .handle           = init_bo->gem_handle,
504             .relocation_count = 0,
505             .relocs_ptr       = 0,
506             .flags            = EXEC_OBJECT_SUPPORTS_48B_ADDRESS |
507                                 EXEC_OBJECT_PINNED |
508                                 EXEC_OBJECT_WRITE /* to be able to wait on the BO */,
509             .offset           = init_bo->offset,
510          };
511          ret = execbuffer(drm_fd, &execbuffer_bos, init_bo, init.offset);
512          if (ret != 0) {
513             fprintf(stderr, "initialization buffer failed to execute errno=%i\n", errno);
514             exit(-1);
515          }
516       } else {
517          fprintf(stderr, "no init BO\n");
518       }
519 
520       if (batch_bo) {
521          fprintf(stderr, "exec: 0x%016"PRIx64" aperture=%.2fMb\n", batch_bo->offset,
522                  gem_allocated / 1024.0 / 1024.0);
523          *execbuf_bo = (struct drm_i915_gem_exec_object2) {
524             .handle           = batch_bo->gem_handle,
525             .relocation_count = 0,
526             .relocs_ptr       = 0,
527             .flags            = EXEC_OBJECT_SUPPORTS_48B_ADDRESS |
528                                 EXEC_OBJECT_PINNED |
529                                 EXEC_OBJECT_WRITE /* to be able to wait on the BO */,
530             .offset           = batch_bo->offset,
531          };
532          ret = execbuffer(drm_fd, &execbuffer_bos, batch_bo, exec.offset);
533          if (ret != 0) {
534             fprintf(stderr, "replayed buffer failed to execute errno=%i\n", errno);
535             exit(-1);
536          } else {
537             fprintf(stderr, "exec completed successfully\n");
538          }
539       } else {
540          fprintf(stderr, "no exec BO\n");
541       }
542    }
543 
544    close(drm_fd);
545    close(file_fd);
546 
547    ralloc_free(mem_ctx);
548 
549    return EXIT_SUCCESS;
550 }
551