Commit 3ff7cd59 authored by Christian Gmeiner's avatar Christian Gmeiner
Browse files
parent 4c212a11
Pipeline #147293 waiting for manual action with stages
/*
* Copyright (c) 2020 Etnaviv Project
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sub license,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Authors:
* Christian Gmeiner <christian.gmeiner@gmail.com>
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <signal.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <inttypes.h>
#include "drm-uapi/drm.h"
#include "drm-uapi/etnaviv_drm.h"
#include "util/macros.h"
static int close_init_helper(int fd);
static int ioctl_init_helper(int fd, unsigned long request, ...);
static int (*libc_close)(int fd) = close_init_helper;
static int (*libc_ioctl)(int fd, unsigned long request, ...) = ioctl_init_helper;
static int drm_fd = -1;
static int verbose = 1;
static __u64 identity[ETNAVIV_PARAM_SOFTPIN_START_ADDR + 1];
#define MAX_FD_COUNT 64
#define MAX_BO_COUNT 64 * 1024
struct bo {
uint32_t size;
uint64_t offset;
void *map;
};
static struct bo *bos;
#define DRM_MAJOR 226
#define GET_PTR(p) ( (void *) ((uintptr_t) p & ~(uintptr_t) 1) )
static unsigned submit_num;
static const char *base = "/home/debian/shared/dump/";
static void __attribute__ ((format(__printf__, 2, 3)))
fail_if(int cond, const char *format, ...)
{
va_list args;
if (!cond)
return;
va_start(args, format);
fprintf(stderr, "etnaviv_dump_gpu: ");
vfprintf(stderr, format, args);
va_end(args);
raise(SIGTRAP);
}
static void dump_cmdstream(const char *base, unsigned submit, void *data, unsigned size)
{
char buffer[255];
snprintf(buffer, sizeof(buffer), "%s/submit_%08u_cmdstream", base, submit);
FILE *f = fopen(buffer,"wb");
fail_if(f == NULL, "failed to open %s", buffer);
fwrite(data, size, sizeof(uint32_t), f);
fclose(f);
}
static void dump_bo(const char *base, unsigned submit, void *data, unsigned size, unsigned flags)
{
char buffer[255];
const char *str;
fail_if(flags & ~(ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE), "silly flags");
if (flags == ETNA_SUBMIT_BO_READ)
str = "read";
else if (flags == ETNA_SUBMIT_BO_WRITE)
str = "write";
else
str = "read_write";
snprintf(buffer, sizeof(buffer), "%s/submit_%08u_bo_%08x_%s", base, submit, (uintptr_t) data, str);
FILE *f = fopen(buffer,"wb");
fail_if(f == NULL, "failed to open %s", buffer);
fwrite(data, size, sizeof(uint32_t), f);
fclose(f);
}
static struct bo *
get_bo(unsigned fd, uint32_t handle)
{
struct bo *bo;
fail_if(handle >= MAX_BO_COUNT, "bo handle too large\n");
fail_if(fd >= MAX_FD_COUNT, "bo fd too large\n");
bo = &bos[handle + fd * MAX_BO_COUNT];
return bo;
}
static void
add_new_bo(unsigned fd, int handle, uint64_t size, void *map)
{
struct bo *bo = &bos[handle + fd * MAX_BO_COUNT];
fail_if(handle >= MAX_BO_COUNT, "bo handle out of range\n");
fail_if(fd >= MAX_FD_COUNT, "bo fd out of range\n");
fail_if(size == 0, "bo size is invalid\n");
bo->size = size;
bo->map = map;
}
static void
remove_bo(int fd, int handle)
{
struct bo *bo = get_bo(fd, handle);
if (bo->map)
munmap(bo->map, bo->size);
bo->size = 0;
bo->map = NULL;
}
static int
gem_ioctl(int fd, unsigned long request, void *argp)
{
int ret;
do {
ret = libc_ioctl(fd, request, argp);
} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
return ret;
}
__attribute__ ((visibility ("default"))) int
close(int fd)
{
if (fd == drm_fd)
drm_fd = -1;
return libc_close(fd);
}
static void
maybe_init(void)
{
static bool initialized = false;
if (initialized)
return;
initialized = true;
bos = calloc(MAX_FD_COUNT * MAX_BO_COUNT, sizeof(bos[0]));
fail_if(bos == NULL, "out of memory\n");
}
static void
get_buffer_info(int fd, int handle)
{
int ret;
struct bo *bo = get_bo(fd, handle);
struct drm_etnaviv_gem_info req = {
.handle = handle,
};
ret = gem_ioctl(fd, DRM_IOCTL_ETNAVIV_GEM_INFO, &req);
fail_if(ret != 0, "gem info failed");
bo->offset = req.offset;
}
static void
dump_submit(int fd, struct drm_etnaviv_gem_submit *submit)
{
const struct drm_etnaviv_gem_submit_bo *bos = (struct drm_etnaviv_gem_submit_bo *) (uintptr_t) submit->bos;
const struct drm_etnaviv_gem_submit_reloc *relocs = (struct drm_etnaviv_gem_submit_reloc *) (uintptr_t) submit->relocs;
if (verbose)
printf("Dumping submit:\n");
for (__u32 i = 0; i < submit->nr_bos; i++) {
const struct drm_etnaviv_gem_submit_bo *obj = &bos[i];
struct bo *bo = get_bo(fd, obj->handle);
fail_if(bo->size == 0, "bo size is 0");
if (bo->map == NULL && bo->size > 0) {
get_buffer_info(fd, obj->handle);
bo->map = mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, bo->offset);
}
fail_if(bo->map == MAP_FAILED, "bo mmap failed\n");
dump_bo(base, submit_num, bo->map, bo->size, obj->flags);
}
/* create a copy of the cmd stream */
uint32_t *cmd = malloc(submit->stream_size);
memcpy(cmd, GET_PTR(submit->stream), submit->stream_size);
for (__u32 i = 0; i < submit->nr_relocs; i++) {
const struct drm_etnaviv_gem_submit_reloc *reloc = &relocs[i];
const struct drm_etnaviv_gem_submit_bo *obj = &bos[reloc->reloc_idx];
struct bo *bo = get_bo(fd, obj->handle);
fail_if(reloc->flags, "invalid reloc flags");
fail_if(reloc->submit_offset % 4, "non-aligned reloc offset");
cmd[reloc->submit_offset / 4] = (uintptr_t) bo->map + reloc->reloc_offset;
}
dump_cmdstream(base, submit_num, cmd, submit->stream_size / 4);
free(cmd);
submit_num++;
}
__attribute__ ((visibility ("default"))) int
ioctl(int fd, unsigned long request, ...)
{
va_list args;
void *argp;
int ret;
struct stat buf;
va_start(args, request);
argp = va_arg(args, void *);
va_end(args);
if (_IOC_TYPE(request) == DRM_IOCTL_BASE &&
drm_fd != fd && fstat(fd, &buf) == 0 &&
(buf.st_mode & S_IFMT) == S_IFCHR && major(buf.st_rdev) == DRM_MAJOR) {
drm_fd = fd;
if (verbose)
printf("[intercept drm ioctl on fd %d]\n", fd);
}
if (fd != drm_fd)
return libc_ioctl(fd, request, argp);
maybe_init();
switch (request) {
case DRM_IOCTL_GEM_CLOSE: {
struct drm_gem_close *close = argp;
remove_bo(fd, close->handle);
return libc_ioctl(fd, request, argp);
}
case DRM_IOCTL_GEM_OPEN: {
struct drm_gem_open *open = argp;
ret = libc_ioctl(fd, request, argp);
if (ret == 0)
add_new_bo(fd, open->handle, open->size, NULL);
return ret;
}
case DRM_IOCTL_PRIME_FD_TO_HANDLE: {
struct drm_prime_handle *prime = argp;
ret = libc_ioctl(fd, request, argp);
if (ret == 0) {
off_t size;
size = lseek(prime->fd, 0, SEEK_END);
fail_if(size == -1, "failed to get prime bo size\n");
add_new_bo(fd, prime->handle, size, NULL);
}
return ret;
}
case DRM_IOCTL_ETNAVIV_GET_PARAM: {
struct drm_etnaviv_param *param = argp;
ret = libc_ioctl(fd, request, argp);
if (ret == 0) {
fail_if(ARRAY_SIZE(identity) < param->param, "identity array is too small\n");
identity[param->param] = param->value;
}
return ret;
}
case DRM_IOCTL_ETNAVIV_GEM_NEW: {
struct drm_etnaviv_gem_new *open = argp;
ret = libc_ioctl(fd, request, argp);
if (ret == 0)
add_new_bo(fd, open->handle, open->size, NULL);
return ret;
}
case DRM_IOCTL_ETNAVIV_GEM_SUBMIT: {
dump_submit(fd, argp);
return libc_ioctl(fd, request, argp);
}
default:
return libc_ioctl(fd, request, argp);
}
}
static void
init(void)
{
libc_close = dlsym(RTLD_NEXT, "close");
libc_ioctl = dlsym(RTLD_NEXT, "ioctl");
fail_if(libc_close == NULL || libc_ioctl == NULL,
"failed to get libc ioctl or close\n");
}
static int
close_init_helper(int fd)
{
init();
return libc_close(fd);
}
static int
ioctl_init_helper(int fd, unsigned long request, ...)
{
va_list args;
void *argp;
va_start(args, request);
argp = va_arg(args, void *);
va_end(args);
init();
return libc_ioctl(fd, request, argp);
}
static void __attribute__ ((destructor))
fini(void)
{
if (bos)
free(bos);
}
# Copyright (c) 2020 Etnaviv Project
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
libetnaviv_dump_gpu = shared_library(
'etnaviv_dump_cmdstream',
'etnaviv_dump_cmdstream.c',
dependencies : dep_dl,
include_directories: [
inc_include,
inc_src,
],
c_args : [c_vis_args, no_override_init_args],
build_by_default : with_tools.contains('etnaviv'),
install : with_tools.contains('etnaviv')
)
......@@ -25,3 +25,7 @@ subdir('drm')
if with_tools.contains('drm-shim')
subdir('drm-shim')
endif
if with_tools.contains('etnaviv')
subdir('dump-cmdstream')
endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment