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