• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2015 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 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdint.h>
28 #include <stdbool.h>
29 #include <stdarg.h>
30 #include <fcntl.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/ioctl.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <sys/mman.h>
37 #include <dlfcn.h>
38 #include <i915_drm.h>
39 #include <pthread.h>
40 
41 #include "intel_aub.h"
42 #include "intel_chipset.h"
43 
44 static int (*libc_close)(int fd);
45 static int (*libc_ioctl)(int fd, unsigned long request, void *argp);
46 
47 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
48 
49 struct trace {
50 	int fd;
51 	FILE *file;
52 	struct trace *next;
53 } *traces;
54 
55 #define DRM_MAJOR 226
56 
57 enum {
58 	ADD_BO = 0,
59 	DEL_BO,
60 	ADD_CTX,
61 	DEL_CTX,
62 	EXEC,
63 	WAIT,
64 };
65 
66 static struct trace_verion {
67 	uint32_t magic;
68 	uint32_t version;
69 } version = {
70 	.magic = 0xdeadbeef,
71 	.version = 1
72 };
73 
74 struct trace_add_bo {
75 	uint8_t cmd;
76 	uint32_t handle;
77 	uint64_t size;
78 } __attribute__((packed));
79 struct trace_del_bo {
80 	uint8_t cmd;
81 	uint32_t handle;
82 }__attribute__((packed));
83 
84 struct trace_add_ctx {
85 	uint8_t cmd;
86 	uint32_t handle;
87 } __attribute__((packed));
88 struct trace_del_ctx {
89 	uint8_t cmd;
90 	uint32_t handle;
91 }__attribute__((packed));
92 
93 struct trace_exec {
94 	uint8_t cmd;
95 	uint32_t object_count;
96 	uint64_t flags;
97 	uint32_t context;
98 }__attribute__((packed));
99 
100 struct trace_exec_object {
101 	uint32_t handle;
102 	uint32_t relocation_count;
103 	uint64_t alignment;
104 	uint64_t offset;
105 	uint64_t flags;
106 	uint64_t rsvd1;
107 	uint64_t rsvd2;
108 }__attribute__((packed));
109 
110 struct trace_exec_relocation {
111 	uint32_t target_handle;
112 	uint32_t delta;
113 	uint64_t offset;
114 	uint64_t presumed_offset;
115 	uint32_t read_domains;
116 	uint32_t write_domain;
117 }__attribute__((packed));
118 
119 struct trace_wait {
120 	uint8_t cmd;
121 	uint32_t handle;
122 } __attribute__((packed));
123 
124 static void __attribute__ ((format(__printf__, 2, 3)))
fail_if(int cond,const char * format,...)125 fail_if(int cond, const char *format, ...)
126 {
127 	va_list args;
128 
129 	if (!cond)
130 		return;
131 
132 	va_start(args, format);
133 	vfprintf(stderr, format, args);
134 	va_end(args);
135 
136 	abort();
137 }
138 
139 #define LOCAL_I915_EXEC_FENCE_IN              (1<<16)
140 #define LOCAL_I915_EXEC_FENCE_OUT             (1<<17)
141 
142 static void
trace_exec(struct trace * trace,const struct drm_i915_gem_execbuffer2 * execbuffer2)143 trace_exec(struct trace *trace,
144 	   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 		to_ptr(typeof(*exec_objects), execbuffer2->buffers_ptr);
149 
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 			EXEC,
157 			execbuffer2->buffer_count,
158 			execbuffer2->flags,
159 			execbuffer2->rsvd1,
160 		};
161 		fwrite(&t, sizeof(t), 1, trace->file);
162 	}
163 
164 	for (uint32_t i = 0; i < execbuffer2->buffer_count; i++) {
165 		const struct drm_i915_gem_exec_object2 *obj = &exec_objects[i];
166 		const struct drm_i915_gem_relocation_entry *relocs =
167 			to_ptr(typeof(*relocs), obj->relocs_ptr);
168 		{
169 			struct trace_exec_object t = {
170 				obj->handle,
171 				obj->relocation_count,
172 				obj->alignment,
173 				obj->offset,
174 				obj->flags,
175 				obj->rsvd1,
176 				obj->rsvd2
177 			};
178 			fwrite(&t, sizeof(t), 1, trace->file);
179 		}
180 		fwrite(relocs, sizeof(*relocs), obj->relocation_count,
181 		       trace->file);
182 	}
183 
184 	fflush(trace->file);
185 	funlockfile(trace->file);
186 #undef to_ptr
187 }
188 
189 static void
trace_wait(struct trace * trace,uint32_t handle)190 trace_wait(struct trace *trace, uint32_t handle)
191 {
192 	struct trace_wait t = { WAIT, handle };
193 	fwrite(&t, sizeof(t), 1, trace->file);
194 }
195 
196 static void
trace_add(struct trace * trace,uint32_t handle,uint64_t size)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
trace_del(struct trace * trace,uint32_t handle)204 trace_del(struct trace *trace, uint32_t handle)
205 {
206 	struct trace_del_bo t = { DEL_BO, handle };
207 	fwrite(&t, sizeof(t), 1, trace->file);
208 }
209 
210 static void
trace_add_context(struct trace * trace,uint32_t handle)211 trace_add_context(struct trace *trace, uint32_t handle)
212 {
213 	struct trace_add_ctx t = { ADD_CTX, handle };
214 	fwrite(&t, sizeof(t), 1, trace->file);
215 }
216 
217 static void
trace_del_context(struct trace * trace,uint32_t handle)218 trace_del_context(struct trace *trace, uint32_t handle)
219 {
220 	struct trace_del_ctx t = { DEL_CTX, handle };
221 	fwrite(&t, sizeof(t), 1, trace->file);
222 }
223 
224 int
close(int fd)225 close(int fd)
226 {
227 	struct trace *t, **p;
228 
229 	pthread_mutex_lock(&mutex);
230 	for (p = &traces; (t = *p); p = &t->next) {
231 		if (t->fd == fd) {
232 			*p = t->next;
233 			fclose(t->file);
234 			free(t);
235 			break;
236 		}
237 	}
238 	pthread_mutex_unlock(&mutex);
239 
240 	return libc_close(fd);
241 }
242 
243 static unsigned long
size_for_fb(const struct drm_mode_fb_cmd * cmd)244 size_for_fb(const struct drm_mode_fb_cmd *cmd)
245 {
246 	unsigned long size;
247 
248 #ifndef ALIGN
249 #define ALIGN(x, y) (((x) + (y) - 1) & -(y))
250 #endif
251 
252 	size = ALIGN(cmd->width * cmd->bpp, 64);
253 	size *= cmd->height;
254 	return ALIGN(size, 4096);
255 }
256 
is_i915(int fd)257 static int is_i915(int fd)
258 {
259 	drm_version_t v;
260 	char name[5] = "";
261 
262 	memset(&v, 0, sizeof(v));
263 	v.name_len = 4;
264 	v.name = name;
265 
266 	if (libc_ioctl(fd, DRM_IOCTL_VERSION, &v))
267 		return 0;
268 
269 	return strcmp(name, "i915") == 0;
270 }
271 
272 #define LOCAL_IOCTL_I915_GEM_EXECBUFFER2_WR \
273     DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2)
274 
275 int
ioctl(int fd,unsigned long request,...)276 ioctl(int fd, unsigned long request, ...)
277 {
278 	struct trace *t, **p;
279 	va_list args;
280 	void *argp;
281 	int ret;
282 
283 	va_start(args, request);
284 	argp = va_arg(args, void *);
285 	va_end(args);
286 
287 	if (_IOC_TYPE(request) != DRM_IOCTL_BASE)
288 		goto untraced;
289 
290 	pthread_mutex_lock(&mutex);
291 	for (p = &traces; (t = *p); p = &t->next) {
292 		if (fd == t->fd) {
293 			if (traces != t) {
294 				*p = t->next;
295 				t->next = traces;
296 				traces = t;
297 			}
298 			break;
299 		}
300 	}
301 	if (!t) {
302 		char filename[80];
303 
304 		if (!is_i915(fd)) {
305 			pthread_mutex_unlock(&mutex);
306 			goto untraced;
307 		}
308 
309 		t = malloc(sizeof(*t));
310 		if (!t) {
311 			pthread_mutex_unlock(&mutex);
312 			return -ENOMEM;
313 		}
314 
315 		sprintf(filename, "/tmp/trace-%d.%d", getpid(), fd);
316 		t->file = fopen(filename, "w+");
317 		t->fd = fd;
318 
319 		if (!fwrite(&version, sizeof(version), 1, t->file)) {
320 			pthread_mutex_unlock(&mutex);
321 			fclose(t->file);
322 			free(t);
323 			return -ENOMEM;
324 		}
325 
326 		t->next = traces;
327 		traces = t;
328 	}
329 	pthread_mutex_unlock(&mutex);
330 
331 	switch (request) {
332 	case DRM_IOCTL_I915_GEM_EXECBUFFER2:
333 	case LOCAL_IOCTL_I915_GEM_EXECBUFFER2_WR:
334 		trace_exec(t, argp);
335 		break;
336 
337 	case DRM_IOCTL_GEM_CLOSE: {
338 		struct drm_gem_close *close = argp;
339 		trace_del(t, close->handle);
340 		break;
341 	}
342 
343 	case DRM_IOCTL_I915_GEM_CONTEXT_DESTROY: {
344 		struct drm_i915_gem_context_destroy *close = argp;
345 		trace_del_context(t, close->ctx_id);
346 		break;
347 	}
348 
349 	case DRM_IOCTL_I915_GEM_WAIT: {
350 		struct drm_i915_gem_wait *w = argp;
351 		trace_wait(t, w->bo_handle);
352 		break;
353 	}
354 
355 	case DRM_IOCTL_I915_GEM_SET_DOMAIN: {
356 		struct drm_i915_gem_set_domain *w = argp;
357 		trace_wait(t, w->handle);
358 		break;
359 	}
360 	}
361 
362 	ret = libc_ioctl(fd, request, argp);
363 	if (ret)
364 		return ret;
365 
366 	switch (request) {
367 	case DRM_IOCTL_I915_GEM_CREATE: {
368 		struct drm_i915_gem_create *create = argp;
369 		trace_add(t, create->handle, create->size);
370 		break;
371 	}
372 
373 	case DRM_IOCTL_I915_GEM_USERPTR: {
374 		struct drm_i915_gem_userptr *userptr = argp;
375 		trace_add(t, userptr->handle, userptr->user_size);
376 		break;
377 	}
378 
379 	case DRM_IOCTL_GEM_OPEN: {
380 		struct drm_gem_open *open = argp;
381 		trace_add(t, open->handle, open->size);
382 		break;
383 	}
384 
385 	case DRM_IOCTL_PRIME_FD_TO_HANDLE: {
386 		struct drm_prime_handle *prime = argp;
387 		off_t size = lseek(prime->fd, 0, SEEK_END);
388 		fail_if(size == -1, "failed to get prime bo size\n");
389 		trace_add(t, prime->handle, size);
390 		break;
391 	}
392 
393 	case DRM_IOCTL_MODE_GETFB: {
394 		struct drm_mode_fb_cmd *cmd = argp;
395 		trace_add(t, cmd->handle, size_for_fb(cmd));
396 		break;
397 	}
398 
399 	case DRM_IOCTL_I915_GEM_CONTEXT_CREATE: {
400 		struct drm_i915_gem_context_create *create = argp;
401 		trace_add_context(t, create->ctx_id);
402 		break;
403 	}
404 	}
405 
406 	return 0;
407 
408 untraced:
409 	return libc_ioctl(fd, request, argp);
410 }
411 
412 static void __attribute__ ((constructor))
init(void)413 init(void)
414 {
415 	libc_close = dlsym(RTLD_NEXT, "close");
416 	libc_ioctl = dlsym(RTLD_NEXT, "ioctl");
417 	fail_if(libc_close == NULL || libc_ioctl == NULL,
418 		"failed to get libc ioctl or close\n");
419 }
420