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