• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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