Path: blob/21.2-virgl/src/intel/tools/intel_dump_gpu.c
4547 views
/*1* Copyright © 2015 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*/2223#include <stdlib.h>24#include <stdio.h>25#include <string.h>26#include <stdint.h>27#include <stdbool.h>28#include <signal.h>29#include <stdarg.h>30#include <fcntl.h>31#include <sys/types.h>32#include <sys/sysmacros.h>33#include <sys/stat.h>34#include <sys/ioctl.h>35#include <unistd.h>36#include <errno.h>37#include <sys/mman.h>38#include <dlfcn.h>39#include "drm-uapi/i915_drm.h"40#include <inttypes.h>4142#include "intel_aub.h"43#include "aub_write.h"4445#include "dev/intel_debug.h"46#include "dev/intel_device_info.h"47#include "util/macros.h"4849static int close_init_helper(int fd);50static int ioctl_init_helper(int fd, unsigned long request, ...);51static int munmap_init_helper(void *addr, size_t length);5253static int (*libc_close)(int fd) = close_init_helper;54static int (*libc_ioctl)(int fd, unsigned long request, ...) = ioctl_init_helper;55static int (*libc_munmap)(void *addr, size_t length) = munmap_init_helper;5657static int drm_fd = -1;58static char *output_filename = NULL;59static FILE *output_file = NULL;60static int verbose = 0;61static bool device_override = false;62static bool capture_only = false;63static int64_t frame_id = -1;64static bool capture_finished = false;6566#define MAX_FD_COUNT 6467#define MAX_BO_COUNT 64 * 10246869struct bo {70uint32_t size;71uint64_t offset;72void *map;73/* Whether the buffer has been positionned in the GTT already. */74bool gtt_mapped : 1;75/* Tracks userspace mmapping of the buffer */76bool user_mapped : 1;77/* Using the i915-gem mmapping ioctl & execbuffer ioctl, track whether a78* buffer has been updated.79*/80bool dirty : 1;81};8283static struct bo *bos;8485#define DRM_MAJOR 2268687/* We set bit 0 in the map pointer for userptr BOs so we know not to88* munmap them on DRM_IOCTL_GEM_CLOSE.89*/90#define USERPTR_FLAG 191#define IS_USERPTR(p) ((uintptr_t) (p) & USERPTR_FLAG)92#define GET_PTR(p) ( (void *) ((uintptr_t) p & ~(uintptr_t) 1) )9394#define fail_if(cond, ...) _fail_if(cond, "intel_dump_gpu", __VA_ARGS__)9596static struct bo *97get_bo(unsigned fd, uint32_t handle)98{99struct bo *bo;100101fail_if(handle >= MAX_BO_COUNT, "bo handle too large\n");102fail_if(fd >= MAX_FD_COUNT, "bo fd too large\n");103bo = &bos[handle + fd * MAX_BO_COUNT];104105return bo;106}107108static inline uint32_t109align_u32(uint32_t v, uint32_t a)110{111return (v + a - 1) & ~(a - 1);112}113114static struct intel_device_info devinfo = {0};115static int device = 0;116static struct aub_file aub_file;117118static void119ensure_device_info(int fd)120{121/* We can't do this at open time as we're not yet authenticated. */122if (device == 0) {123fail_if(!intel_get_device_info_from_fd(fd, &devinfo),124"failed to identify chipset.\n");125device = devinfo.chipset_id;126} else if (devinfo.ver == 0) {127fail_if(!intel_get_device_info_from_pci_id(device, &devinfo),128"failed to identify chipset.\n");129}130}131132static void *133relocate_bo(int fd, struct bo *bo, const struct drm_i915_gem_execbuffer2 *execbuffer2,134const struct drm_i915_gem_exec_object2 *obj)135{136const struct drm_i915_gem_exec_object2 *exec_objects =137(struct drm_i915_gem_exec_object2 *) (uintptr_t) execbuffer2->buffers_ptr;138const struct drm_i915_gem_relocation_entry *relocs =139(const struct drm_i915_gem_relocation_entry *) (uintptr_t) obj->relocs_ptr;140void *relocated;141int handle;142143relocated = malloc(bo->size);144fail_if(relocated == NULL, "out of memory\n");145memcpy(relocated, GET_PTR(bo->map), bo->size);146for (size_t i = 0; i < obj->relocation_count; i++) {147fail_if(relocs[i].offset >= bo->size, "reloc outside bo\n");148149if (execbuffer2->flags & I915_EXEC_HANDLE_LUT)150handle = exec_objects[relocs[i].target_handle].handle;151else152handle = relocs[i].target_handle;153154aub_write_reloc(&devinfo, ((char *)relocated) + relocs[i].offset,155get_bo(fd, handle)->offset + relocs[i].delta);156}157158return relocated;159}160161static int162gem_ioctl(int fd, unsigned long request, void *argp)163{164int ret;165166do {167ret = libc_ioctl(fd, request, argp);168} while (ret == -1 && (errno == EINTR || errno == EAGAIN));169170return ret;171}172173static void *174gem_mmap(int fd, uint32_t handle, uint64_t offset, uint64_t size)175{176struct drm_i915_gem_mmap mmap = {177.handle = handle,178.offset = offset,179.size = size180};181182if (gem_ioctl(fd, DRM_IOCTL_I915_GEM_MMAP, &mmap) == -1)183return MAP_FAILED;184185return (void *)(uintptr_t) mmap.addr_ptr;186}187188static enum drm_i915_gem_engine_class189engine_class_from_ring_flag(uint32_t ring_flag)190{191switch (ring_flag) {192case I915_EXEC_DEFAULT:193case I915_EXEC_RENDER:194return I915_ENGINE_CLASS_RENDER;195case I915_EXEC_BSD:196return I915_ENGINE_CLASS_VIDEO;197case I915_EXEC_BLT:198return I915_ENGINE_CLASS_COPY;199case I915_EXEC_VEBOX:200return I915_ENGINE_CLASS_VIDEO_ENHANCE;201default:202return I915_ENGINE_CLASS_INVALID;203}204}205206static void207dump_execbuffer2(int fd, struct drm_i915_gem_execbuffer2 *execbuffer2)208{209struct drm_i915_gem_exec_object2 *exec_objects =210(struct drm_i915_gem_exec_object2 *) (uintptr_t) execbuffer2->buffers_ptr;211uint32_t ring_flag = execbuffer2->flags & I915_EXEC_RING_MASK;212uint32_t offset;213struct drm_i915_gem_exec_object2 *obj;214struct bo *bo, *batch_bo;215int batch_index;216void *data;217218ensure_device_info(fd);219220if (capture_finished)221return;222223if (!aub_file.file) {224aub_file_init(&aub_file, output_file,225verbose == 2 ? stdout : NULL,226device, program_invocation_short_name);227aub_write_default_setup(&aub_file);228229if (verbose)230printf("[running, output file %s, chipset id 0x%04x, gen %d]\n",231output_filename, device, devinfo.ver);232}233234if (aub_use_execlists(&aub_file))235offset = 0x1000;236else237offset = aub_gtt_size(&aub_file);238239for (uint32_t i = 0; i < execbuffer2->buffer_count; i++) {240obj = &exec_objects[i];241bo = get_bo(fd, obj->handle);242243/* If bo->size == 0, this means they passed us an invalid244* buffer. The kernel will reject it and so should we.245*/246if (bo->size == 0) {247if (verbose)248printf("BO #%d is invalid!\n", obj->handle);249return;250}251252if (obj->flags & EXEC_OBJECT_PINNED) {253if (bo->offset != obj->offset)254bo->gtt_mapped = false;255bo->offset = obj->offset;256} else {257if (obj->alignment != 0)258offset = align_u32(offset, obj->alignment);259bo->offset = offset;260offset = align_u32(offset + bo->size + 4095, 4096);261}262263if (bo->map == NULL && bo->size > 0)264bo->map = gem_mmap(fd, obj->handle, 0, bo->size);265fail_if(bo->map == MAP_FAILED, "bo mmap failed\n");266}267268uint64_t current_frame_id = 0;269if (frame_id >= 0) {270for (uint32_t i = 0; i < execbuffer2->buffer_count; i++) {271obj = &exec_objects[i];272bo = get_bo(fd, obj->handle);273274/* Check against frame_id requirements. */275if (memcmp(bo->map, intel_debug_identifier(),276intel_debug_identifier_size()) == 0) {277const struct intel_debug_block_frame *frame_desc =278intel_debug_get_identifier_block(bo->map, bo->size,279INTEL_DEBUG_BLOCK_TYPE_FRAME);280281current_frame_id = frame_desc ? frame_desc->frame_id : 0;282break;283}284}285}286287if (verbose)288printf("Dumping execbuffer2 (frame_id=%"PRIu64", buffers=%u):\n",289current_frame_id, execbuffer2->buffer_count);290291/* Check whether we can stop right now. */292if (frame_id >= 0) {293if (current_frame_id < frame_id)294return;295296if (current_frame_id > frame_id) {297aub_file_finish(&aub_file);298capture_finished = true;299return;300}301}302303304/* Map buffers into the PPGTT. */305for (uint32_t i = 0; i < execbuffer2->buffer_count; i++) {306obj = &exec_objects[i];307bo = get_bo(fd, obj->handle);308309if (verbose) {310printf("BO #%d (%dB) @ 0x%" PRIx64 "\n",311obj->handle, bo->size, bo->offset);312}313314if (aub_use_execlists(&aub_file) && !bo->gtt_mapped) {315aub_map_ppgtt(&aub_file, bo->offset, bo->size);316bo->gtt_mapped = true;317}318}319320/* Write the buffer content into the Aub. */321batch_index = (execbuffer2->flags & I915_EXEC_BATCH_FIRST) ? 0 :322execbuffer2->buffer_count - 1;323batch_bo = get_bo(fd, exec_objects[batch_index].handle);324for (uint32_t i = 0; i < execbuffer2->buffer_count; i++) {325obj = &exec_objects[i];326bo = get_bo(fd, obj->handle);327328if (obj->relocation_count > 0)329data = relocate_bo(fd, bo, execbuffer2, obj);330else331data = bo->map;332333bool write = !capture_only || (obj->flags & EXEC_OBJECT_CAPTURE);334335if (write && bo->dirty) {336if (bo == batch_bo) {337aub_write_trace_block(&aub_file, AUB_TRACE_TYPE_BATCH,338GET_PTR(data), bo->size, bo->offset);339} else {340aub_write_trace_block(&aub_file, AUB_TRACE_TYPE_NOTYPE,341GET_PTR(data), bo->size, bo->offset);342}343344if (!bo->user_mapped)345bo->dirty = false;346}347348if (data != bo->map)349free(data);350}351352uint32_t ctx_id = execbuffer2->rsvd1;353354aub_write_exec(&aub_file, ctx_id,355batch_bo->offset + execbuffer2->batch_start_offset,356offset, engine_class_from_ring_flag(ring_flag));357358if (device_override &&359(execbuffer2->flags & I915_EXEC_FENCE_ARRAY) != 0) {360struct drm_i915_gem_exec_fence *fences =361(void*)(uintptr_t)execbuffer2->cliprects_ptr;362for (uint32_t i = 0; i < execbuffer2->num_cliprects; i++) {363if ((fences[i].flags & I915_EXEC_FENCE_SIGNAL) != 0) {364struct drm_syncobj_array arg = {365.handles = (uintptr_t)&fences[i].handle,366.count_handles = 1,367.pad = 0,368};369libc_ioctl(fd, DRM_IOCTL_SYNCOBJ_SIGNAL, &arg);370}371}372}373}374375static void376add_new_bo(unsigned fd, int handle, uint64_t size, void *map)377{378struct bo *bo = &bos[handle + fd * MAX_BO_COUNT];379380fail_if(handle >= MAX_BO_COUNT, "bo handle out of range\n");381fail_if(fd >= MAX_FD_COUNT, "bo fd out of range\n");382fail_if(size == 0, "bo size is invalid\n");383384bo->size = size;385bo->map = map;386bo->user_mapped = false;387bo->gtt_mapped = false;388}389390static void391remove_bo(int fd, int handle)392{393struct bo *bo = get_bo(fd, handle);394395if (bo->map && !IS_USERPTR(bo->map))396munmap(bo->map, bo->size);397memset(bo, 0, sizeof(*bo));398}399400__attribute__ ((visibility ("default"))) int401close(int fd)402{403if (fd == drm_fd)404drm_fd = -1;405406return libc_close(fd);407}408409static int410get_pci_id(int fd, int *pci_id)411{412struct drm_i915_getparam gparam;413414if (device_override) {415*pci_id = device;416return 0;417}418419gparam.param = I915_PARAM_CHIPSET_ID;420gparam.value = pci_id;421return libc_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gparam);422}423424static void425maybe_init(int fd)426{427static bool initialized = false;428FILE *config;429char *key, *value;430431if (initialized)432return;433434initialized = true;435436const char *config_path = getenv("INTEL_DUMP_GPU_CONFIG");437fail_if(config_path == NULL, "INTEL_DUMP_GPU_CONFIG is not set\n");438439config = fopen(config_path, "r");440fail_if(config == NULL, "failed to open file %s\n", config_path);441442while (fscanf(config, "%m[^=]=%m[^\n]\n", &key, &value) != EOF) {443if (!strcmp(key, "verbose")) {444if (!strcmp(value, "1")) {445verbose = 1;446} else if (!strcmp(value, "2")) {447verbose = 2;448}449} else if (!strcmp(key, "device")) {450fail_if(device != 0, "Device/Platform override specified multiple times.\n");451fail_if(sscanf(value, "%i", &device) != 1,452"failed to parse device id '%s'\n",453value);454device_override = true;455} else if (!strcmp(key, "platform")) {456fail_if(device != 0, "Device/Platform override specified multiple times.\n");457device = intel_device_name_to_pci_device_id(value);458fail_if(device == -1, "Unknown platform '%s'\n", value);459device_override = true;460} else if (!strcmp(key, "file")) {461free(output_filename);462if (output_file)463fclose(output_file);464output_filename = strdup(value);465output_file = fopen(output_filename, "w+");466fail_if(output_file == NULL,467"failed to open file '%s'\n",468output_filename);469} else if (!strcmp(key, "capture_only")) {470capture_only = atoi(value);471} else if (!strcmp(key, "frame")) {472frame_id = atol(value);473} else {474fprintf(stderr, "unknown option '%s'\n", key);475}476477free(key);478free(value);479}480fclose(config);481482bos = calloc(MAX_FD_COUNT * MAX_BO_COUNT, sizeof(bos[0]));483fail_if(bos == NULL, "out of memory\n");484485ASSERTED int ret = get_pci_id(fd, &device);486assert(ret == 0);487488aub_file_init(&aub_file, output_file,489verbose == 2 ? stdout : NULL,490device, program_invocation_short_name);491aub_write_default_setup(&aub_file);492493if (verbose)494printf("[running, output file %s, chipset id 0x%04x, gen %d]\n",495output_filename, device, devinfo.ver);496}497498__attribute__ ((visibility ("default"))) int499ioctl(int fd, unsigned long request, ...)500{501va_list args;502void *argp;503int ret;504struct stat buf;505506va_start(args, request);507argp = va_arg(args, void *);508va_end(args);509510if (_IOC_TYPE(request) == DRM_IOCTL_BASE &&511drm_fd != fd && fstat(fd, &buf) == 0 &&512(buf.st_mode & S_IFMT) == S_IFCHR && major(buf.st_rdev) == DRM_MAJOR) {513drm_fd = fd;514if (verbose)515printf("[intercept drm ioctl on fd %d]\n", fd);516}517518if (fd == drm_fd) {519maybe_init(fd);520521switch (request) {522case DRM_IOCTL_SYNCOBJ_WAIT:523case DRM_IOCTL_I915_GEM_WAIT: {524if (device_override)525return 0;526return libc_ioctl(fd, request, argp);527}528529case DRM_IOCTL_I915_GET_RESET_STATS: {530if (device_override) {531struct drm_i915_reset_stats *stats = argp;532533stats->reset_count = 0;534stats->batch_active = 0;535stats->batch_pending = 0;536return 0;537}538return libc_ioctl(fd, request, argp);539}540541case DRM_IOCTL_I915_GETPARAM: {542struct drm_i915_getparam *getparam = argp;543544ensure_device_info(fd);545546if (getparam->param == I915_PARAM_CHIPSET_ID)547return get_pci_id(fd, getparam->value);548549if (device_override) {550switch (getparam->param) {551case I915_PARAM_CS_TIMESTAMP_FREQUENCY:552*getparam->value = devinfo.timestamp_frequency;553return 0;554555case I915_PARAM_HAS_WAIT_TIMEOUT:556case I915_PARAM_HAS_EXECBUF2:557case I915_PARAM_MMAP_VERSION:558case I915_PARAM_HAS_EXEC_ASYNC:559case I915_PARAM_HAS_EXEC_FENCE:560case I915_PARAM_HAS_EXEC_FENCE_ARRAY:561*getparam->value = 1;562return 0;563564case I915_PARAM_HAS_EXEC_SOFTPIN:565*getparam->value = devinfo.ver >= 8 && !devinfo.is_cherryview;566return 0;567568default:569return -1;570}571}572573return libc_ioctl(fd, request, argp);574}575576case DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM: {577struct drm_i915_gem_context_param *getparam = argp;578579ensure_device_info(fd);580581if (device_override) {582switch (getparam->param) {583case I915_CONTEXT_PARAM_GTT_SIZE:584if (devinfo.is_elkhartlake)585getparam->value = 1ull << 36;586else if (devinfo.ver >= 8 && !devinfo.is_cherryview)587getparam->value = 1ull << 48;588else589getparam->value = 1ull << 31;590return 0;591592default:593return -1;594}595}596597return libc_ioctl(fd, request, argp);598}599600case DRM_IOCTL_I915_GEM_EXECBUFFER: {601static bool once;602if (!once) {603fprintf(stderr,604"application uses DRM_IOCTL_I915_GEM_EXECBUFFER, not handled\n");605once = true;606}607return libc_ioctl(fd, request, argp);608}609610case DRM_IOCTL_I915_GEM_EXECBUFFER2:611case DRM_IOCTL_I915_GEM_EXECBUFFER2_WR: {612dump_execbuffer2(fd, argp);613if (device_override)614return 0;615616return libc_ioctl(fd, request, argp);617}618619case DRM_IOCTL_I915_GEM_CONTEXT_CREATE: {620uint32_t *ctx_id = NULL;621struct drm_i915_gem_context_create *create = argp;622ret = 0;623if (!device_override) {624ret = libc_ioctl(fd, request, argp);625ctx_id = &create->ctx_id;626}627628if (ret == 0)629create->ctx_id = aub_write_context_create(&aub_file, ctx_id);630631return ret;632}633634case DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT: {635uint32_t *ctx_id = NULL;636struct drm_i915_gem_context_create_ext *create = argp;637ret = 0;638if (!device_override) {639ret = libc_ioctl(fd, request, argp);640ctx_id = &create->ctx_id;641}642643if (ret == 0)644create->ctx_id = aub_write_context_create(&aub_file, ctx_id);645646return ret;647}648649case DRM_IOCTL_I915_GEM_CREATE: {650struct drm_i915_gem_create *create = argp;651652ret = libc_ioctl(fd, request, argp);653if (ret == 0)654add_new_bo(fd, create->handle, create->size, NULL);655656return ret;657}658659case DRM_IOCTL_I915_GEM_USERPTR: {660struct drm_i915_gem_userptr *userptr = argp;661662ret = libc_ioctl(fd, request, argp);663if (ret == 0)664add_new_bo(fd, userptr->handle, userptr->user_size,665(void *) (uintptr_t) (userptr->user_ptr | USERPTR_FLAG));666667return ret;668}669670case DRM_IOCTL_GEM_CLOSE: {671struct drm_gem_close *close = argp;672673remove_bo(fd, close->handle);674675return libc_ioctl(fd, request, argp);676}677678case DRM_IOCTL_GEM_OPEN: {679struct drm_gem_open *open = argp;680681ret = libc_ioctl(fd, request, argp);682if (ret == 0)683add_new_bo(fd, open->handle, open->size, NULL);684685return ret;686}687688case DRM_IOCTL_PRIME_FD_TO_HANDLE: {689struct drm_prime_handle *prime = argp;690691ret = libc_ioctl(fd, request, argp);692if (ret == 0) {693off_t size;694695size = lseek(prime->fd, 0, SEEK_END);696fail_if(size == -1, "failed to get prime bo size\n");697add_new_bo(fd, prime->handle, size, NULL);698699}700701return ret;702}703704case DRM_IOCTL_I915_GEM_MMAP: {705ret = libc_ioctl(fd, request, argp);706if (ret == 0) {707struct drm_i915_gem_mmap *mmap = argp;708struct bo *bo = get_bo(fd, mmap->handle);709bo->user_mapped = true;710bo->dirty = true;711}712return ret;713}714715case DRM_IOCTL_I915_GEM_MMAP_OFFSET: {716ret = libc_ioctl(fd, request, argp);717if (ret == 0) {718struct drm_i915_gem_mmap_offset *mmap = argp;719struct bo *bo = get_bo(fd, mmap->handle);720bo->user_mapped = true;721bo->dirty = true;722}723return ret;724}725726default:727return libc_ioctl(fd, request, argp);728}729} else {730return libc_ioctl(fd, request, argp);731}732}733734static void735init(void)736{737libc_close = dlsym(RTLD_NEXT, "close");738libc_ioctl = dlsym(RTLD_NEXT, "ioctl");739libc_munmap = dlsym(RTLD_NEXT, "munmap");740fail_if(libc_close == NULL || libc_ioctl == NULL,741"failed to get libc ioctl or close\n");742}743744static int745close_init_helper(int fd)746{747init();748return libc_close(fd);749}750751static int752ioctl_init_helper(int fd, unsigned long request, ...)753{754va_list args;755void *argp;756757va_start(args, request);758argp = va_arg(args, void *);759va_end(args);760761init();762return libc_ioctl(fd, request, argp);763}764765static int766munmap_init_helper(void *addr, size_t length)767{768init();769for (uint32_t i = 0; i < MAX_FD_COUNT * MAX_BO_COUNT; i++) {770struct bo *bo = &bos[i];771if (bo->map == addr) {772bo->user_mapped = false;773break;774}775}776return libc_munmap(addr, length);777}778779static void __attribute__ ((destructor))780fini(void)781{782if (devinfo.ver != 0) {783free(output_filename);784if (!capture_finished)785aub_file_finish(&aub_file);786free(bos);787}788}789790791