1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3 * Copyright 2022 Collabora Ltd.
4 */
5
6 #include "trace.h"
7 #include <dlfcn.h>
8 #include <stdarg.h>
9
10 extern struct trace_context ctx_trace;
11
12 const std::list<unsigned long> ioctls = {
13 VIDIOC_QUERYCAP,
14 VIDIOC_STREAMON,
15 VIDIOC_STREAMOFF,
16 VIDIOC_ENUM_FMT,
17 VIDIOC_G_FMT,
18 VIDIOC_S_FMT,
19 VIDIOC_REQBUFS,
20 VIDIOC_QUERYBUF,
21 VIDIOC_QBUF,
22 VIDIOC_EXPBUF,
23 VIDIOC_DQBUF,
24 VIDIOC_G_PARM,
25 VIDIOC_S_PARM,
26 VIDIOC_ENUMINPUT,
27 VIDIOC_G_CTRL,
28 VIDIOC_S_CTRL,
29 VIDIOC_G_TUNER,
30 VIDIOC_S_TUNER,
31 VIDIOC_QUERYCTRL,
32 VIDIOC_G_INPUT,
33 VIDIOC_S_INPUT,
34 VIDIOC_G_OUTPUT,
35 VIDIOC_S_OUTPUT,
36 VIDIOC_ENUMOUTPUT,
37 VIDIOC_G_CROP,
38 VIDIOC_S_CROP,
39 VIDIOC_TRY_FMT,
40 VIDIOC_G_EXT_CTRLS,
41 VIDIOC_S_EXT_CTRLS,
42 VIDIOC_TRY_EXT_CTRLS,
43 VIDIOC_ENUM_FRAMESIZES,
44 VIDIOC_ENUM_FRAMEINTERVALS,
45 VIDIOC_ENCODER_CMD,
46 VIDIOC_TRY_ENCODER_CMD,
47 VIDIOC_DQEVENT,
48 VIDIOC_SUBSCRIBE_EVENT,
49 VIDIOC_UNSUBSCRIBE_EVENT,
50 VIDIOC_CREATE_BUFS,
51 VIDIOC_PREPARE_BUF,
52 VIDIOC_G_SELECTION,
53 VIDIOC_S_SELECTION,
54 VIDIOC_DECODER_CMD,
55 VIDIOC_TRY_DECODER_CMD,
56 VIDIOC_QUERY_EXT_CTRL,
57 MEDIA_IOC_REQUEST_ALLOC,
58 MEDIA_REQUEST_IOC_QUEUE,
59 MEDIA_REQUEST_IOC_REINIT,
60 };
61
open(const char * path,int oflag,...)62 int open(const char *path, int oflag, ...)
63 {
64 errno = 0;
65 mode_t mode = 0;
66
67 if ((oflag & O_CREAT) != 0) {
68 va_list argp;
69 va_start(argp, oflag);
70 mode = va_arg(argp, PROMOTED_MODE_T);
71 va_end(argp);
72 }
73
74 int (*original_open)(const char *path, int oflag, ...) = nullptr;
75 original_open = (int (*)(const char*, int, ...)) dlsym(RTLD_NEXT, "open");
76 int fd = (*original_open)(path, oflag, mode);
77 debug_line_info("\n\tfd: %d, path: %s", fd, path);
78
79 if (getenv("V4L2_TRACER_PAUSE_TRACE") != nullptr)
80 return fd;
81
82 if (is_video_or_media_device(path)) {
83 trace_open(fd, path, oflag, mode, false);
84 add_device(fd, path);
85 }
86 print_devices();
87
88 return fd;
89 }
90
write(int fd,const void * buf,size_t count)91 ssize_t write(int fd, const void *buf, size_t count)
92 {
93 ssize_t (*original_write)(int fd, const void *buf, size_t count) = nullptr;
94 original_write = (ssize_t (*)(int, const void *, size_t)) dlsym(RTLD_NEXT, "write");
95 ssize_t ret = (*original_write)(fd, buf, count);
96
97 /*
98 * If the write message starts with "v4l2-tracer", then assume it came from the
99 * v4l2_tracer_info macro and trace it.
100 */
101 std::string buf_string(static_cast<const char*>(buf), count);
102 if (buf_string.find("v4l2-tracer") == 0) {
103 json_object *write_obj = json_object_new_object();
104 json_object_object_add(write_obj, "write", json_object_new_string((const char*)buf));
105 write_json_object_to_json_file(write_obj);
106 json_object_put(write_obj);
107 }
108
109 return ret;
110 }
111
112 #if defined(linux) && defined(__GLIBC__)
open64(const char * path,int oflag,...)113 int open64(const char *path, int oflag, ...)
114 {
115 errno = 0;
116 mode_t mode = 0;
117 if ((oflag & O_CREAT) != 0) {
118 va_list argp;
119 va_start(argp, oflag);
120 mode = va_arg(argp, PROMOTED_MODE_T);
121 va_end(argp);
122 }
123
124 int (*original_open64)(const char *path, int oflag, ...) = nullptr;
125 original_open64 = (int (*)(const char*, int, ...)) dlsym(RTLD_NEXT, "open64");
126 int fd = (*original_open64)(path, oflag, mode);
127 debug_line_info("\n\tfd: %d, path: %s", fd, path);
128
129 if (getenv("V4L2_TRACER_PAUSE_TRACE") != nullptr)
130 return fd;
131
132 if (is_video_or_media_device(path)) {
133 add_device(fd, path);
134 trace_open(fd, path, oflag, mode, true);
135 }
136 print_devices();
137
138 return fd;
139 }
140 #endif
141
close(int fd)142 int close(int fd)
143 {
144 errno = 0;
145 int (*original_close)(int fd) = nullptr;
146 original_close = (int (*)(int)) dlsym(RTLD_NEXT, "close");
147
148 if (getenv("V4L2_TRACER_PAUSE_TRACE") != nullptr)
149 return (*original_close)(fd);
150
151 std::string path = get_device(fd);
152 debug_line_info("\n\tfd: %d, path: %s", fd, path.c_str());
153
154 /* Only trace the close if a corresponding open was also traced. */
155 if (!path.empty()) {
156 json_object *close_obj = json_object_new_object();
157 json_object_object_add(close_obj, "fd", json_object_new_int(fd));
158 json_object_object_add(close_obj, "close", json_object_new_string(path.c_str()));
159 write_json_object_to_json_file(close_obj);
160 json_object_put(close_obj);
161 ctx_trace.devices.erase(fd);
162
163 /* If we removed the last device, close the json trace file. */
164 if (!ctx_trace.devices.size())
165 close_json_file();
166 }
167 print_devices();
168
169 return (*original_close)(fd);
170 }
171
mmap(void * addr,size_t len,int prot,int flags,int fildes,off_t off)172 void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
173 {
174 errno = 0;
175 void *(*original_mmap)(void *addr, size_t len, int prot, int flags, int fildes, off_t off) = nullptr;
176 original_mmap = (void*(*)(void*, size_t, int, int, int, off_t)) dlsym(RTLD_NEXT, "mmap");
177 void *buf_address_pointer = (*original_mmap)(addr, len, prot, flags, fildes, off);
178
179 set_buffer_address_trace(fildes, off, (unsigned long) buf_address_pointer);
180
181 if (buffer_in_trace_context(fildes, off))
182 trace_mmap(addr, len, prot, flags, fildes, off, (unsigned long) buf_address_pointer, false);
183
184 return buf_address_pointer;
185 }
186
187 #if defined(linux) && defined(__GLIBC__)
mmap64(void * addr,size_t len,int prot,int flags,int fildes,off_t off)188 void *mmap64(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
189 {
190 errno = 0;
191 void *(*original_mmap64)(void *addr, size_t len, int prot, int flags, int fildes, off_t off) = nullptr;
192 original_mmap64 = (void*(*)(void*, size_t, int, int, int, off_t)) dlsym(RTLD_NEXT, "mmap64");
193 void *buf_address_pointer = (*original_mmap64)(addr, len, prot, flags, fildes, off);
194
195 set_buffer_address_trace(fildes, off, (unsigned long) buf_address_pointer);
196
197 if (buffer_in_trace_context(fildes, off))
198 trace_mmap(addr, len, prot, flags, fildes, off, (unsigned long) buf_address_pointer, true);
199
200 return buf_address_pointer;
201 }
202 #endif
203
munmap(void * start,size_t length)204 int munmap(void *start, size_t length)
205 {
206 errno = 0;
207 int(*original_munmap)(void *start, size_t length) = nullptr;
208 original_munmap = (int(*)(void *, size_t)) dlsym(RTLD_NEXT, "munmap");
209 int ret = (*original_munmap)(start, length);
210
211 /* Only trace the unmapping if the original mapping was traced. */
212 if (!buffer_is_mapped((unsigned long) start))
213 return ret;
214
215 json_object *munmap_obj = json_object_new_object();
216
217 if (errno)
218 json_object_object_add(munmap_obj, "errno", json_object_new_string(STRERR(errno)));
219
220 json_object *munmap_args = json_object_new_object();
221 json_object_object_add(munmap_args, "start", json_object_new_int64((int64_t)start));
222 json_object_object_add(munmap_args, "length", json_object_new_uint64(length));
223 json_object_object_add(munmap_obj, "munmap", munmap_args);
224
225 write_json_object_to_json_file(munmap_obj);
226 json_object_put(munmap_obj);
227
228 return ret;
229 }
230
ioctl(int fd,unsigned long cmd,...)231 int ioctl(int fd, unsigned long cmd, ...)
232 {
233 errno = 0;
234 va_list argp;
235 va_start(argp, cmd);
236 void *arg = va_arg(argp, void *);
237 va_end(argp);
238
239 int (*original_ioctl)(int fd, unsigned long cmd, ...) = nullptr;
240 original_ioctl = (int (*)(int, long unsigned int, ...)) dlsym(RTLD_NEXT, "ioctl");
241
242 if (getenv("V4L2_TRACER_PAUSE_TRACE") != nullptr)
243 return (*original_ioctl)(fd, cmd, arg);
244
245 /* Don't trace ioctls that are not in the specified ioctls list. */
246 if (find(ioctls.begin(), ioctls.end(), cmd) == ioctls.end())
247 return (*original_ioctl)(fd, cmd, arg);
248
249 json_object *ioctl_obj = json_object_new_object();
250 json_object_object_add(ioctl_obj, "fd", json_object_new_int(fd));
251 json_object_object_add(ioctl_obj, "ioctl",
252 json_object_new_string(val2s(cmd, ioctl_val_def).c_str()));
253
254 /* Don't attempt to trace a nullptr. */
255 if (arg == nullptr) {
256 int ret = (*original_ioctl)(fd, cmd, arg);
257 if (errno)
258 json_object_object_add(ioctl_obj, "errno",
259 json_object_new_string(STRERR(errno)));
260 write_json_object_to_json_file(ioctl_obj);
261 json_object_put(ioctl_obj);
262 return ret;
263 }
264
265 /* Get info needed for writing the decoded video data to a yuv file. */
266 if (cmd == VIDIOC_S_EXT_CTRLS)
267 s_ext_ctrls_setup(static_cast<struct v4l2_ext_controls*>(arg));
268 if (cmd == VIDIOC_QBUF)
269 qbuf_setup(static_cast<struct v4l2_buffer*>(arg));
270 if (cmd == VIDIOC_STREAMOFF)
271 streamoff_cleanup(*(static_cast<v4l2_buf_type*>(arg)));
272
273 /*
274 * To avoid cluttering the trace file, only trace userspace arguments when necessary
275 * or if the option to trace them is selected.
276 */
277 if (((cmd & IOC_INOUT) == IOC_IN) ||
278 (getenv("V4L2_TRACER_OPTION_TRACE_USERSPACE_ARG") != nullptr) ||
279 (cmd == VIDIOC_QBUF)) {
280 json_object *ioctl_args_userspace = trace_ioctl_args(cmd, arg);
281 /* Some ioctls won't have arguments to trace e.g. MEDIA_REQUEST_IOC_QUEUE. */
282 if (json_object_object_length(ioctl_args_userspace))
283 json_object_object_add(ioctl_obj, "from_userspace", ioctl_args_userspace);
284 else
285 json_object_put(ioctl_args_userspace);
286 }
287
288 /* Make the original ioctl call. */
289 int ret = (*original_ioctl)(fd, cmd, arg);
290
291 if (errno)
292 json_object_object_add(ioctl_obj, "errno", json_object_new_string(STRERR(errno)));
293
294 /* Trace driver arguments if userspace will be reading them i.e. _IOR or _IOWR ioctls */
295 if ((cmd & IOC_OUT) != 0U) {
296 json_object *ioctl_args_driver = trace_ioctl_args(cmd, arg);
297 /* Some ioctls won't have arguments to trace e.g. MEDIA_REQUEST_IOC_QUEUE. */
298 if (json_object_object_length(ioctl_args_driver))
299 json_object_object_add(ioctl_obj, "from_driver", ioctl_args_driver);
300 else
301 json_object_put(ioctl_args_driver);
302 }
303
304 write_json_object_to_json_file(ioctl_obj);
305 json_object_put(ioctl_obj);
306
307 /* Get additional info from driver for writing the decoded video data to a yuv file. */
308 if (cmd == VIDIOC_G_FMT)
309 g_fmt_setup_trace(static_cast<struct v4l2_format*>(arg));
310 if (cmd == VIDIOC_S_FMT)
311 s_fmt_setup(static_cast<struct v4l2_format*>(arg));
312 if (cmd == VIDIOC_EXPBUF)
313 expbuf_setup(static_cast<struct v4l2_exportbuffer*>(arg));
314 if (cmd == VIDIOC_QUERYBUF)
315 querybuf_setup(fd, static_cast<struct v4l2_buffer*>(arg));
316 if (cmd == VIDIOC_DQBUF)
317 dqbuf_setup(static_cast<struct v4l2_buffer*>(arg));
318
319 /* Get info needed for tracing dynamic arrays */
320 if (cmd == VIDIOC_QUERY_EXT_CTRL)
321 query_ext_ctrl_setup(fd, static_cast<struct v4l2_query_ext_ctrl*>(arg));
322
323 return ret;
324 }
325