gem_exec_tracer.c 8.76 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/*
 * Copyright © 2015 Intel Corporation
 *
 * 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.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <i915_drm.h>
39
#include <pthread.h>
40 41 42 43 44 45 46

#include "intel_aub.h"
#include "intel_chipset.h"

static int (*libc_close)(int fd);
static int (*libc_ioctl)(int fd, unsigned long request, void *argp);

47 48
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

49 50 51 52 53
struct trace {
	int fd;
	FILE *file;
	struct trace *next;
} *traces;
54 55 56 57 58 59

#define DRM_MAJOR 226

enum {
	ADD_BO = 0,
	DEL_BO,
60 61
	ADD_CTX,
	DEL_CTX,
62
	EXEC,
63
	WAIT,
64 65
};

66 67 68 69 70 71 72 73
static struct trace_verion {
	uint32_t magic;
	uint32_t version;
} version = {
	.magic = 0xdeadbeef,
	.version = 1
};

74 75 76 77 78 79 80 81 82 83
struct trace_add_bo {
	uint8_t cmd;
	uint32_t handle;
	uint64_t size;
} __attribute__((packed));
struct trace_del_bo {
	uint8_t cmd;
	uint32_t handle;
}__attribute__((packed));

84 85 86 87 88 89 90 91 92
struct trace_add_ctx {
	uint8_t cmd;
	uint32_t handle;
} __attribute__((packed));
struct trace_del_ctx {
	uint8_t cmd;
	uint32_t handle;
}__attribute__((packed));

93 94 95 96
struct trace_exec {
	uint8_t cmd;
	uint32_t object_count;
	uint64_t flags;
97
	uint32_t context;
98 99 100 101 102 103
}__attribute__((packed));

struct trace_exec_object {
	uint32_t handle;
	uint32_t relocation_count;
	uint64_t alignment;
104
	uint64_t offset;
105 106 107 108 109 110 111 112 113
	uint64_t flags;
	uint64_t rsvd1;
	uint64_t rsvd2;
}__attribute__((packed));

struct trace_exec_relocation {
	uint32_t target_handle;
	uint32_t delta;
	uint64_t offset;
114
	uint64_t presumed_offset;
115 116 117 118
	uint32_t read_domains;
	uint32_t write_domain;
}__attribute__((packed));

119 120 121 122 123
struct trace_wait {
	uint8_t cmd;
	uint32_t handle;
} __attribute__((packed));

124 125 126 127 128 129 130 131 132 133 134 135
static void __attribute__ ((format(__printf__, 2, 3)))
fail_if(int cond, const char *format, ...)
{
	va_list args;

	if (!cond)
		return;

	va_start(args, format);
	vfprintf(stderr, format, args);
	va_end(args);

136
	abort();
137 138
}

139 140 141
#define LOCAL_I915_EXEC_FENCE_IN              (1<<16)
#define LOCAL_I915_EXEC_FENCE_OUT             (1<<17)

142
static void
143 144
trace_exec(struct trace *trace,
	   const struct drm_i915_gem_execbuffer2 *execbuffer2)
145
{
146
#define to_ptr(T, x) ((T *)(uintptr_t)(x))
147
	const struct drm_i915_gem_exec_object2 *exec_objects =
148 149
		to_ptr(typeof(*exec_objects), execbuffer2->buffers_ptr);

150
	fail_if(execbuffer2->flags & (LOCAL_I915_EXEC_FENCE_IN | LOCAL_I915_EXEC_FENCE_OUT),
151
		"fences not supported yet\n");
152

153
	flockfile(trace->file);
154 155
	{
		struct trace_exec t = {
156 157 158 159
			EXEC,
			execbuffer2->buffer_count,
			execbuffer2->flags,
			execbuffer2->rsvd1,
160
		};
161
		fwrite(&t, sizeof(t), 1, trace->file);
162 163 164 165 166
	}

	for (uint32_t i = 0; i < execbuffer2->buffer_count; i++) {
		const struct drm_i915_gem_exec_object2 *obj = &exec_objects[i];
		const struct drm_i915_gem_relocation_entry *relocs =
167
			to_ptr(typeof(*relocs), obj->relocs_ptr);
168 169 170 171 172
		{
			struct trace_exec_object t = {
				obj->handle,
				obj->relocation_count,
				obj->alignment,
173
				obj->offset,
174 175 176 177
				obj->flags,
				obj->rsvd1,
				obj->rsvd2
			};
178
			fwrite(&t, sizeof(t), 1, trace->file);
179
		}
180 181
		fwrite(relocs, sizeof(*relocs), obj->relocation_count,
		       trace->file);
182 183
	}

184
	fflush(trace->file);
185
	funlockfile(trace->file);
186
#undef to_ptr
187 188
}

189 190 191 192 193 194 195
static void
trace_wait(struct trace *trace, uint32_t handle)
{
	struct trace_wait t = { WAIT, handle };
	fwrite(&t, sizeof(t), 1, trace->file);
}

196
static void
197
trace_add(struct trace *trace, uint32_t handle, uint64_t size)
198 199
{
	struct trace_add_bo t = { ADD_BO, handle, size };
200
	fwrite(&t, sizeof(t), 1, trace->file);
201 202 203
}

static void
204
trace_del(struct trace *trace, uint32_t handle)
205 206
{
	struct trace_del_bo t = { DEL_BO, handle };
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
	fwrite(&t, sizeof(t), 1, trace->file);
}

static void
trace_add_context(struct trace *trace, uint32_t handle)
{
	struct trace_add_ctx t = { ADD_CTX, handle };
	fwrite(&t, sizeof(t), 1, trace->file);
}

static void
trace_del_context(struct trace *trace, uint32_t handle)
{
	struct trace_del_ctx t = { DEL_CTX, handle };
	fwrite(&t, sizeof(t), 1, trace->file);
222 223 224 225 226
}

int
close(int fd)
{
227 228
	struct trace *t, **p;

229
	pthread_mutex_lock(&mutex);
230 231 232 233 234 235 236 237
	for (p = &traces; (t = *p); p = &t->next) {
		if (t->fd == fd) {
			*p = t->next;
			fclose(t->file);
			free(t);
			break;
		}
	}
238
	pthread_mutex_unlock(&mutex);
239 240 241 242

	return libc_close(fd);
}

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
static unsigned long
size_for_fb(const struct drm_mode_fb_cmd *cmd)
{
	unsigned long size;

#ifndef ALIGN
#define ALIGN(x, y) (((x) + (y) - 1) & -(y))
#endif

	size = ALIGN(cmd->width * cmd->bpp, 64);
	size *= cmd->height;
	return ALIGN(size, 4096);
}

static int is_i915(int fd)
{
259
	drm_version_t v;
260 261
	char name[5] = "";

262 263 264
	memset(&v, 0, sizeof(v));
	v.name_len = 4;
	v.name = name;
265

266
	if (libc_ioctl(fd, DRM_IOCTL_VERSION, &v))
267 268 269 270 271
		return 0;

	return strcmp(name, "i915") == 0;
}

272 273 274
#define LOCAL_IOCTL_I915_GEM_EXECBUFFER2_WR \
    DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2)

275 276 277
int
ioctl(int fd, unsigned long request, ...)
{
278
	struct trace *t, **p;
279 280 281 282 283 284 285 286
	va_list args;
	void *argp;
	int ret;

	va_start(args, request);
	argp = va_arg(args, void *);
	va_end(args);

287
	if (_IOC_TYPE(request) != DRM_IOCTL_BASE)
288 289
		goto untraced;

290
	pthread_mutex_lock(&mutex);
291 292 293 294 295 296 297 298 299 300 301
	for (p = &traces; (t = *p); p = &t->next) {
		if (fd == t->fd) {
			if (traces != t) {
				*p = t->next;
				t->next = traces;
				traces = t;
			}
			break;
		}
	}
	if (!t) {
302
		char filename[80];
303

304 305
		if (!is_i915(fd)) {
			pthread_mutex_unlock(&mutex);
306
			goto untraced;
307
		}
308

309
		t = malloc(sizeof(*t));
310 311
		if (!t) {
			pthread_mutex_unlock(&mutex);
312
			return -ENOMEM;
313
		}
314

315 316 317 318 319
		sprintf(filename, "/tmp/trace-%d.%d", getpid(), fd);
		t->file = fopen(filename, "w+");
		t->fd = fd;

		if (!fwrite(&version, sizeof(version), 1, t->file)) {
320
			pthread_mutex_unlock(&mutex);
321 322 323 324 325 326 327
			fclose(t->file);
			free(t);
			return -ENOMEM;
		}

		t->next = traces;
		traces = t;
328
	}
329
	pthread_mutex_unlock(&mutex);
330

331 332
	switch (request) {
	case DRM_IOCTL_I915_GEM_EXECBUFFER2:
333
	case LOCAL_IOCTL_I915_GEM_EXECBUFFER2_WR:
334 335 336 337 338 339
		trace_exec(t, argp);
		break;

	case DRM_IOCTL_GEM_CLOSE: {
		struct drm_gem_close *close = argp;
		trace_del(t, close->handle);
340
		break;
341 342 343 344 345 346 347
	}

	case DRM_IOCTL_I915_GEM_CONTEXT_DESTROY: {
		struct drm_i915_gem_context_destroy *close = argp;
		trace_del_context(t, close->ctx_id);
		break;
	}
348 349 350 351 352 353 354 355 356 357 358 359

	case DRM_IOCTL_I915_GEM_WAIT: {
		struct drm_i915_gem_wait *w = argp;
		trace_wait(t, w->bo_handle);
		break;
	}

	case DRM_IOCTL_I915_GEM_SET_DOMAIN: {
		struct drm_i915_gem_set_domain *w = argp;
		trace_wait(t, w->handle);
		break;
	}
360
	}
361

362 363 364 365 366
	ret = libc_ioctl(fd, request, argp);
	if (ret)
		return ret;

	switch (request) {
367 368
	case DRM_IOCTL_I915_GEM_CREATE: {
		struct drm_i915_gem_create *create = argp;
369
		trace_add(t, create->handle, create->size);
370 371
		break;
	}
372

373 374
	case DRM_IOCTL_I915_GEM_USERPTR: {
		struct drm_i915_gem_userptr *userptr = argp;
375
		trace_add(t, userptr->handle, userptr->user_size);
376 377
		break;
	}
378

379 380
	case DRM_IOCTL_GEM_OPEN: {
		struct drm_gem_open *open = argp;
381
		trace_add(t, open->handle, open->size);
382 383
		break;
	}
384

385 386 387 388
	case DRM_IOCTL_PRIME_FD_TO_HANDLE: {
		struct drm_prime_handle *prime = argp;
		off_t size = lseek(prime->fd, 0, SEEK_END);
		fail_if(size == -1, "failed to get prime bo size\n");
389
		trace_add(t, prime->handle, size);
390 391
		break;
	}
392

393 394
	case DRM_IOCTL_MODE_GETFB: {
		struct drm_mode_fb_cmd *cmd = argp;
395 396 397 398 399 400 401
		trace_add(t, cmd->handle, size_for_fb(cmd));
		break;
	}

	case DRM_IOCTL_I915_GEM_CONTEXT_CREATE: {
		struct drm_i915_gem_context_create *create = argp;
		trace_add_context(t, create->ctx_id);
402
		break;
403
	}
404 405 406
	}

	return 0;
407 408 409

untraced:
	return libc_ioctl(fd, request, argp);
410 411 412 413 414 415 416 417 418 419
}

static void __attribute__ ((constructor))
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");
}