* [RFC 0/2] v4l2 stateless tracer/retracer utilities
@ 2022-08-20 0:50 Deborah Brouwer
2022-08-20 0:50 ` [RFC 1/2] utils: add stateless tracer utility Deborah Brouwer
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Deborah Brouwer @ 2022-08-20 0:50 UTC (permalink / raw)
To: linux-media
Cc: daniel.almeida, nfraprado, nicolas.dufresne, hverkuil-cisco,
Deborah Brouwer
This project helps to test v4l2 stateless decoder drivers by tracing,
recording and replaying (i.e. "retracing") userspace's interaction with
a stateless decoder driver.
The tracer utility attaches to a userspace application and generates a
json file with relevant system calls, parameters and encoded data.
The retracer utility reads the json-file and makes the same system calls
to a v4l2 stateless driver. Since the retracer is independent from the
original userspace application that was traced, testing can be decoupled
from extraneous factors in the userspace environment. The json-file can
also be edited to inject errors and test a driver's error-handling
abilities.
NOTE:
This project is work in progress and currently only traces VP8, but
H264 and FWHT will follow shortly.
EXAMPLE:
./tracer gst-launch-1.0 -- filesrc location=<some_vp8_file> ! parsebin !
v4l2slvp8dec ! videocodectestsink
./retracer 10284_trace.json
FURTHER INFO AND TEST FILES:
https://gitlab.collabora.com/dbrouwer/v4l2-stateless-tracer-utility/-/tree/main/
Deborah Brouwer (2):
utils: add stateless tracer utility
utils: add stateless retracer utility
configure.ac | 6 +
utils/Makefile.am | 5 +
utils/common/v4l2-info.cpp | 7 +-
utils/common/v4l2-info.h | 8 +
utils/tracer/.gitignore | 9 +
utils/tracer/Makefile.am | 23 +
utils/tracer/libtracer.cpp | 217 ++++++
utils/tracer/libtracer.h | 92 +++
utils/tracer/retrace-helper.cpp | 141 ++++
utils/tracer/retrace-helper.h | 18 +
utils/tracer/retrace-vp8.cpp | 288 ++++++++
utils/tracer/retrace-vp8.h | 11 +
utils/tracer/retracer.cpp | 1090 +++++++++++++++++++++++++++++++
utils/tracer/retracer.h | 24 +
utils/tracer/trace-helper.cpp | 218 +++++++
utils/tracer/trace-info.cpp | 358 ++++++++++
utils/tracer/trace-info.h | 72 ++
utils/tracer/trace-vp8.cpp | 183 ++++++
utils/tracer/trace.cpp | 520 +++++++++++++++
utils/tracer/tracer.cpp | 91 +++
20 files changed, 3375 insertions(+), 6 deletions(-)
create mode 100644 utils/tracer/.gitignore
create mode 100644 utils/tracer/Makefile.am
create mode 100644 utils/tracer/libtracer.cpp
create mode 100644 utils/tracer/libtracer.h
create mode 100755 utils/tracer/retrace-helper.cpp
create mode 100644 utils/tracer/retrace-helper.h
create mode 100755 utils/tracer/retrace-vp8.cpp
create mode 100644 utils/tracer/retrace-vp8.h
create mode 100755 utils/tracer/retracer.cpp
create mode 100644 utils/tracer/retracer.h
create mode 100644 utils/tracer/trace-helper.cpp
create mode 100644 utils/tracer/trace-info.cpp
create mode 100644 utils/tracer/trace-info.h
create mode 100644 utils/tracer/trace-vp8.cpp
create mode 100644 utils/tracer/trace.cpp
create mode 100644 utils/tracer/tracer.cpp
--
2.37.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC 1/2] utils: add stateless tracer utility
2022-08-20 0:50 [RFC 0/2] v4l2 stateless tracer/retracer utilities Deborah Brouwer
@ 2022-08-20 0:50 ` Deborah Brouwer
2022-08-26 19:18 ` Nicolas Dufresne
2022-08-20 0:50 ` [RFC 2/2] utils: add stateless retracer utility Deborah Brouwer
2022-08-26 19:05 ` [RFC 0/2] v4l2 stateless tracer/retracer utilities Nicolas Dufresne
2 siblings, 1 reply; 5+ messages in thread
From: Deborah Brouwer @ 2022-08-20 0:50 UTC (permalink / raw)
To: linux-media
Cc: daniel.almeida, nfraprado, nicolas.dufresne, hverkuil-cisco,
Deborah Brouwer
The tracer utility helps to test v4l2 stateless decoder drivers by tracing
and recording userspace's interaction with a stateless decoder driver.
Only system calls relevant to the v4l2 memory-to-memory stateless video
decoder interface are traced. The tracer traces encoded data from output
buffers and, optionally, may trace decoded data from capture buffers. The
tracer records its results in a json-formatted trace file.
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
---
configure.ac | 6 +
utils/Makefile.am | 5 +
utils/common/v4l2-info.cpp | 7 +-
utils/common/v4l2-info.h | 8 +
utils/tracer/.gitignore | 9 +
utils/tracer/Makefile.am | 19 ++
utils/tracer/libtracer.cpp | 217 ++++++++++++++
utils/tracer/libtracer.h | 92 ++++++
utils/tracer/trace-helper.cpp | 218 ++++++++++++++
utils/tracer/trace-info.cpp | 358 +++++++++++++++++++++++
utils/tracer/trace-info.h | 72 +++++
utils/tracer/trace-vp8.cpp | 183 ++++++++++++
utils/tracer/trace.cpp | 520 ++++++++++++++++++++++++++++++++++
utils/tracer/tracer.cpp | 91 ++++++
14 files changed, 1799 insertions(+), 6 deletions(-)
create mode 100644 utils/tracer/.gitignore
create mode 100644 utils/tracer/Makefile.am
create mode 100644 utils/tracer/libtracer.cpp
create mode 100644 utils/tracer/libtracer.h
create mode 100644 utils/tracer/trace-helper.cpp
create mode 100644 utils/tracer/trace-info.cpp
create mode 100644 utils/tracer/trace-info.h
create mode 100644 utils/tracer/trace-vp8.cpp
create mode 100644 utils/tracer/trace.cpp
create mode 100644 utils/tracer/tracer.cpp
diff --git a/configure.ac b/configure.ac
index 05298981..cc604cad 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,7 @@ AC_CONFIG_FILES([Makefile
utils/cec-follower/cec-follower.1
utils/qvidcap/Makefile
utils/rds-ctl/Makefile
+ utils/tracer/Makefile
contrib/Makefile
contrib/freebsd/Makefile
@@ -311,6 +312,11 @@ AS_IF([test "x$with_libudev" != xno -o "x$enable_libdvbv5" != xno],
AC_SUBST([JPEG_LIBS])
+PKG_CHECK_MODULES(JSONC, [json-c >= 0.16], [jsonc_pkgconfig=yes], [jsonc_pkgconfig=no])
+AC_SUBST([JSONC_CFLAGS])
+AC_SUBST([JSONC_LIBS])
+AM_CONDITIONAL([HAVE_JSONC], [test x$jsonc_pkgconfig = xyes])
+
# Check for pthread
AS_IF([test x$enable_shared != xno],
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 0e68a612..3ccc3d81 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -15,6 +15,11 @@ SUBDIRS = \
cec-follower \
rds-ctl
+if HAVE_JSONC
+SUBDIRS += \
+ tracer
+endif
+
if WITH_LIBDVBV5
SUBDIRS += \
dvb
diff --git a/utils/common/v4l2-info.cpp b/utils/common/v4l2-info.cpp
index 40d45471..74e77b7a 100644
--- a/utils/common/v4l2-info.cpp
+++ b/utils/common/v4l2-info.cpp
@@ -16,12 +16,7 @@ static std::string num2s(unsigned num, bool is_hex = true)
return buf;
}
-struct flag_def {
- unsigned flag;
- const char *str;
-};
-
-static std::string flags2s(unsigned val, const flag_def *def)
+std::string flags2s(unsigned int val, const flag_def *def)
{
std::string s;
diff --git a/utils/common/v4l2-info.h b/utils/common/v4l2-info.h
index 35237853..8eb14bc4 100644
--- a/utils/common/v4l2-info.h
+++ b/utils/common/v4l2-info.h
@@ -11,6 +11,14 @@
#include <linux/videodev2.h>
#include <linux/v4l2-subdev.h>
+struct flag_def {
+ unsigned int flag;
+ const char *str;
+};
+
+/* Return a comma-separated string of flags or hex value if unknown */
+std::string flags2s(unsigned int val, const flag_def *def);
+
/* Print capability information */
void v4l2_info_capability(const v4l2_capability &cap);
void v4l2_info_subdev_capability(const v4l2_subdev_capability &subdevcap);
diff --git a/utils/tracer/.gitignore b/utils/tracer/.gitignore
new file mode 100644
index 00000000..4098662d
--- /dev/null
+++ b/utils/tracer/.gitignore
@@ -0,0 +1,9 @@
+*.json
+*.yuv
+*.webm
+*.h264
+*.ivf
+*.vp8
+tracer
+retracer
+.clang-tidy
diff --git a/utils/tracer/Makefile.am b/utils/tracer/Makefile.am
new file mode 100644
index 00000000..f5579198
--- /dev/null
+++ b/utils/tracer/Makefile.am
@@ -0,0 +1,19 @@
+if HAVE_JSONC
+
+lib_LTLIBRARIES = libtracer.la
+include_HEADERS = libtracer.h ../../utils/common/v4l2-info.h ../../utils/common/media-info.h
+libtracer_la_SOURCES = libtracer.cpp trace.cpp trace-vp8.cpp trace-helper.cpp \
+trace-info.cpp ../../utils/common/v4l2-info.cpp ../../utils/common/media-info.cpp
+
+libtracer_la_CFLAGS = $(JSONC_CFLAGS)
+libtracer_la_CPPFLAGS = -I$(top_srcdir)/utils/common $(JSONC_CFLAGS)
+libtracer_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -ldl $(JSONC_LIBS)
+libtracer_la_LIBTOOLFLAGS = --tag=disable-static
+
+bin_PROGRAMS = tracer
+
+tracer_SOURCES = tracer.cpp
+tracer_CPPFLAGS = -I$(top_srcdir)/utils/common $(JSONC_CFLAGS)
+tracer_LDFLAGS = -lrt -lpthread $(JSONC_LIBS)
+
+endif
diff --git a/utils/tracer/libtracer.cpp b/utils/tracer/libtracer.cpp
new file mode 100644
index 00000000..0d18b224
--- /dev/null
+++ b/utils/tracer/libtracer.cpp
@@ -0,0 +1,217 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "libtracer.h"
+
+int open64(const char *path, int oflag, ...)
+{
+ errno = 0;
+ mode_t mode = 0;
+
+ if (oflag & O_CREAT) {
+ va_list ap;
+ va_start(ap, oflag);
+ mode = va_arg(ap, PROMOTED_MODE_T);
+ va_end(ap);
+ }
+
+ int (*original_open64)(const char *path, int oflag, ...);
+ original_open64 = (int (*)(const char*, int, ...)) dlsym(RTLD_NEXT, "open64");
+ int fd = (*original_open64)(path, oflag, mode);
+
+ /* Only trace calls to video or media devices. */
+ std::string dev_path_video = "/dev/video";
+ std::string dev_path_media = "/dev/media";
+ if (strncmp(path, dev_path_video.c_str(), dev_path_video.length()) &&
+ strncmp(path, dev_path_media.c_str(), dev_path_media.length()))
+ return fd;
+
+ /* Set trace options if this is the first call to libtracer. */
+ if (!options_are_set())
+ set_options();
+
+ add_device(fd, path);
+
+ json_object *open_obj = json_object_new_object();
+ json_object_object_add(open_obj, "syscall_str", json_object_new_string("open64"));
+ json_object_object_add(open_obj, "syscall", json_object_new_int(LIBTRACER_SYSCALL_OPEN64));
+ json_object_object_add(open_obj, "fd", json_object_new_int(fd));
+ json_object *open_args = trace_open(path, oflag, mode);
+ json_object_object_add(open_obj, "open_args", open_args);
+
+ write_json_object_to_json_file(open_obj);
+ json_object_put(open_obj);
+
+ return fd;
+}
+
+void *mmap64(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
+{
+ errno = 0;
+
+ void *(*original_mmap64)(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
+ original_mmap64 = (void*(*)(void*, size_t, int, int, int, off_t)) dlsym(RTLD_NEXT, "mmap64");
+ void *buf_address_pointer = (*original_mmap64)(addr, len, prot, flags, fildes, off);
+
+ /* Only trace mmap64 calls for output or capture buffers. */
+ __u32 type = get_buffer_type_trace(fildes);
+ if (!type)
+ return buf_address_pointer;
+
+ /* Save the buffer address for future reference. */
+ set_buffer_address_trace(fildes, (long int) buf_address_pointer);
+
+ json_object *mmap_obj = json_object_new_object();
+ if (errno) {
+ json_object_object_add(mmap_obj, "errno", json_object_new_int(errno));
+ json_object_object_add(mmap_obj, "errno_str", json_object_new_string(strerror(errno)));
+ }
+ json_object_object_add(mmap_obj, "syscall_str", json_object_new_string("mmap64"));
+ json_object_object_add(mmap_obj, "syscall", json_object_new_int(LIBTRACER_SYSCALL_MMAP64));
+
+ json_object *mmap64_args = trace_mmap64(addr, len, prot, flags, fildes, off);
+ json_object_object_add(mmap_obj, "mmap64_args", mmap64_args);
+
+ json_object_object_add(mmap_obj, "buffer_address", json_object_new_int64((long int) buf_address_pointer));
+ write_json_object_to_json_file(mmap_obj);
+ json_object_put(mmap_obj);
+
+ /*
+ * The capture buffer is traced for the first time here when the buffer is first mapped.
+ * Subsequently, the capture buffer will be traced when VIDIOC_DQBUF is called.
+ */
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ trace_mem(fildes);
+
+ return buf_address_pointer;
+}
+
+int munmap(void *start, size_t length)
+{
+ errno = 0;
+
+ int(*original_munmap)(void *start, size_t length);
+ original_munmap = (int(*)(void *, size_t)) dlsym(RTLD_NEXT, "munmap");
+ int ret = (*original_munmap)(start, length);
+
+ /* Only trace the unmapping if the original mapping was traced. */
+ if (!buffer_is_mapped((long int) start))
+ return ret;
+
+ json_object *munmap_obj = json_object_new_object();
+ json_object_object_add(munmap_obj, "syscall_str", json_object_new_string("munmap"));
+ json_object_object_add(munmap_obj, "syscall", json_object_new_int(LIBTRACER_SYSCALL_MUNMAP));
+
+ if (errno) {
+ json_object_object_add(munmap_obj, "errno", json_object_new_int(errno));
+ json_object_object_add(munmap_obj, "errno_str", json_object_new_string(strerror(errno)));
+ }
+
+ json_object *munmap_args = json_object_new_object();
+ json_object_object_add(munmap_args, "start", json_object_new_int64((int64_t)start));
+ json_object_object_add(munmap_args, "length", json_object_new_uint64(length));
+ json_object_object_add(munmap_obj, "munmap_args", munmap_args);
+
+ write_json_object_to_json_file(munmap_obj);
+ json_object_put(munmap_obj);
+
+ return ret;
+}
+
+int ioctl(int fd, unsigned long int request, ...)
+{
+ errno = 0;
+
+ va_list ap;
+ va_start(ap, request);
+ void *arg = va_arg(ap, void *);
+ va_end(ap);
+
+ int (*original_ioctl)(int fd, unsigned long int request, ...);
+ original_ioctl = (int (*)(int, long unsigned int, ...)) dlsym(RTLD_NEXT, "ioctl");
+
+ /* If the ioctl is queuing an output buffer, trace the output buffer before tracing the ioctl. */
+ if ((request == VIDIOC_QBUF) && (_IOC_TYPE(request) == 'V')) {
+ struct v4l2_buffer *buf = static_cast<struct v4l2_buffer*>(arg);
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ int buf_fd = get_buffer_fd_trace(buf->type, buf->index);
+ if (buf_fd) {
+ set_buffer_bytesused_trace(buf_fd, buf->m.planes[0].bytesused);
+ trace_mem(buf_fd);
+ }
+ }
+ }
+
+ json_object *ioctl_obj = json_object_new_object();
+ json_object_object_add(ioctl_obj, "syscall_str", json_object_new_string("ioctl"));
+ json_object_object_add(ioctl_obj, "syscall", json_object_new_int(LIBTRACER_SYSCALL_IOCTL));
+ json_object_object_add(ioctl_obj, "fd", json_object_new_int(fd));
+ json_object_object_add(ioctl_obj, "request", json_object_new_uint64(request));
+ json_object_object_add(ioctl_obj, "request_str", json_object_new_string(get_ioctl_request_str(request).c_str()));
+
+ /* Trace the ioctl arguments provided by userspace if relevant. */
+ json_object *ioctl_args_userspace = trace_ioctl_args(fd, request, arg);
+ if (json_object_object_length(ioctl_args_userspace))
+ json_object_object_add(ioctl_obj, "ioctl_args_from_userspace", ioctl_args_userspace);
+
+ /* Make the original ioctl call. */
+ int ret = (*original_ioctl)(fd, request, arg);
+
+ if (errno) {
+ json_object_object_add(ioctl_obj, "errno", json_object_new_int(errno));
+ json_object_object_add(ioctl_obj, "errno_str", json_object_new_string(strerror(errno)));
+ }
+
+ /* Also trace the ioctl arguments as modified by the driver if relevant. */
+ json_object *ioctl_args_driver = trace_ioctl_args(fd, request, arg, false);
+ if (json_object_object_length(ioctl_args_driver))
+ json_object_object_add(ioctl_obj, "ioctl_args_from_driver", ioctl_args_driver);
+
+ write_json_object_to_json_file(ioctl_obj);
+ json_object_put(ioctl_obj);
+
+ /* If the ioctl is exporting a buffer, store the buffer file descriptor and index for future access. */
+ if ((request == VIDIOC_EXPBUF) && (_IOC_TYPE(request) == 'V')) {
+ struct v4l2_exportbuffer *export_buffer = static_cast<struct v4l2_exportbuffer*>(arg);
+ add_buffer_trace(export_buffer->fd, export_buffer->type, export_buffer->index);
+ }
+
+ /* If the ioctl is dequeuing a capture buffer, trace the buffer. */
+ if ((request == VIDIOC_DQBUF) && (_IOC_TYPE(request) == 'V')) {
+ struct v4l2_buffer *buf = static_cast<struct v4l2_buffer*>(arg);
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ int buf_fd = get_buffer_fd_trace(buf->type, buf->index);
+ if (buf_fd) {
+ /* TODO tracer only works for decoded formats with one plane e.g. V4L2_PIX_FMT_NV12 */
+ set_buffer_bytesused_trace(buf_fd, buf->m.planes[0].bytesused);
+ trace_mem(buf_fd);
+ }
+ }
+ }
+
+ return ret;
+}
+
+int close(int fd)
+{
+ std::string path = get_device(fd);
+
+ /* Only trace the close if a corresponding open was also traced. */
+ if (!path.empty()) {
+ json_object *close_obj = json_object_new_object();
+ json_object_object_add(close_obj, "syscall_str", json_object_new_string("close"));
+ json_object_object_add(close_obj, "syscall", json_object_new_int(LIBTRACER_SYSCALL_CLOSE));
+ json_object_object_add(close_obj, "fd", json_object_new_int(fd));
+ json_object_object_add(close_obj, "path", json_object_new_string(path.c_str()));
+ write_json_object_to_json_file(close_obj);
+ json_object_put(close_obj);
+ remove_device(fd);
+ }
+
+ int (*original_close)(int fd);
+ original_close = (int (*)(int)) dlsym(RTLD_NEXT, "close");
+
+ return (*original_close)(fd);
+}
diff --git a/utils/tracer/libtracer.h b/utils/tracer/libtracer.h
new file mode 100644
index 00000000..5f86aadf
--- /dev/null
+++ b/utils/tracer/libtracer.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#ifndef LIBTRACER_H
+#define LIBTRACER_H
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <config.h>
+#include <pthread.h>
+#include <unistd.h> /* needed for close */
+#include <linux/dma-buf.h>
+#include <json-c/json.h>
+#include <v4l2-info.h>
+#include <unordered_map>
+#include <list>
+#include "trace-info.h"
+
+struct trace_options {
+ bool set;
+ bool pretty_print_all;
+ bool pretty_print_mem;
+ bool decoded_data_to_json;
+ bool create_yuv_file;
+};
+
+struct buffer_trace {
+ int fd;
+ __u32 type; /* enum v4l2_buf_type */
+ __u32 index;
+ long address;
+ __u32 bytesused;
+};
+
+struct trace_context {
+ FILE *trace_file;
+ std::string trace_filename;
+ pthread_mutex_t lock;
+ /* Key is the file descriptor, value is the path of the video or media device. */
+ std::unordered_map<int, std::string> devices;
+ /* List of output and capture buffers being traced. */
+ std::list<struct buffer_trace> buffers;
+};
+
+bool options_are_set(void);
+void set_options(void);
+bool pretty_print_mem(void);
+bool pretty_print_all(void);
+bool write_decoded_data_to_json(void);
+bool create_yuv_file(void);
+
+void add_device(int fd, std::string path);
+std::string get_device(int fd);
+int remove_device(int fd);
+void add_buffer_trace(int fd, __u32 type, __u32 index);
+int get_buffer_fd_trace(__u32 type, __u32 index);
+__u32 get_buffer_type_trace(int fd);
+int get_buffer_index_trace(int fd);
+void set_buffer_address_trace(int fd, long address);
+long get_buffer_address_trace(int fd);
+void set_buffer_bytesused_trace(int fd, __u32 bytesused);
+long get_buffer_bytesused_trace(int fd);
+bool buffer_is_mapped(long buffer_address);
+void write_json_object_to_json_file(json_object *jobj, int flags = JSON_C_TO_STRING_PLAIN);
+
+json_object *trace_open(const char *path, int oflag, mode_t mode);
+json_object *trace_mmap64(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
+void trace_mem(int fd);
+std::string get_ioctl_request_str(unsigned long request);
+json_object *trace_ioctl_args(int fd, unsigned long request, void *arg, bool from_userspace = true);
+
+void trace_v4l2_ctrl_h264_decode_mode(struct v4l2_ext_control ctrl, json_object *ctrl_obj);
+void trace_v4l2_ctrl_h264_start_code(struct v4l2_ext_control ctrl, json_object *ctrl_obj);
+void trace_v4l2_ctrl_h264_sps(void *p_h264_sps, json_object *ctrl_obj);
+void trace_v4l2_ctrl_h264_pps(void *p_h264_pps, json_object *ctrl_obj);
+void trace_v4l2_ctrl_h264_scaling_matrix(void *p_h264_scaling_matrix, json_object *ctrl_obj);
+void trace_v4l2_ctrl_h264_pred_weights(void *p_h264_pred_weights, json_object *ctrl_obj);
+void trace_v4l2_ctrl_h264_slice_params(void *p_h264_slice_params, json_object *ctrl_obj);
+void trace_v4l2_ctrl_h264_decode_params(void *p_h264_decode_params, json_object *ctrl_obj);
+
+void trace_v4l2_ctrl_vp8_frame(void *p_vp8_frame, json_object *ctrl_obj);
+
+void trace_v4l2_ctrl_fwht_params(void *p_fwht_params, json_object *ctrl_obj);
+
+#endif
diff --git a/utils/tracer/trace-helper.cpp b/utils/tracer/trace-helper.cpp
new file mode 100644
index 00000000..402c602b
--- /dev/null
+++ b/utils/tracer/trace-helper.cpp
@@ -0,0 +1,218 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "libtracer.h"
+
+struct trace_options options;
+
+struct trace_context ctx_trace = {
+ .lock = PTHREAD_MUTEX_INITIALIZER
+};
+
+bool options_are_set(void)
+{
+ return options.set;
+}
+
+void set_options(void)
+{
+ options.pretty_print_mem = getenv("TRACE_OPTION_PRETTY_PRINT_MEM") ? true : false;
+ options.pretty_print_all = getenv("TRACE_OPTION_PRETTY_PRINT_ALL") ? true : false;
+ options.decoded_data_to_json = getenv("TRACE_OPTION_DECODED_TO_JSON") ? true : false;
+ options.create_yuv_file = getenv("TRACE_OPTION_CREATE_YUV_FILE") ? true : false;
+ options.set = true;
+}
+
+bool pretty_print_mem(void)
+{
+ return options.pretty_print_mem;
+}
+
+bool pretty_print_all(void)
+{
+ return options.pretty_print_all;
+}
+
+bool write_decoded_data_to_json(void)
+{
+ return options.decoded_data_to_json;
+}
+
+bool create_yuv_file(void)
+{
+ return options.create_yuv_file;
+}
+
+void add_device(int fd, std::string path)
+{
+ std::pair<int, std::string> new_pair = std::make_pair(fd, path);
+ pthread_mutex_lock(&ctx_trace.lock);
+ ctx_trace.devices.insert(new_pair);
+ pthread_mutex_unlock(&ctx_trace.lock);
+}
+
+std::string get_device(int fd)
+{
+ std::string path;
+ std::unordered_map<int, std::string>::const_iterator it;
+ pthread_mutex_lock(&ctx_trace.lock);
+ it = ctx_trace.devices.find(fd);
+ if (it != ctx_trace.devices.end())
+ path = it->second;
+ pthread_mutex_unlock(&ctx_trace.lock);
+ return path;
+}
+
+int remove_device(int fd)
+{
+ int ret = 0;
+ pthread_mutex_lock(&ctx_trace.lock);
+ ret = ctx_trace.devices.erase(fd);
+ pthread_mutex_unlock(&ctx_trace.lock);
+ return ret;
+}
+
+void add_buffer_trace(int fd, __u32 type, __u32 index)
+{
+ struct buffer_trace buf;
+ memset(&buf, 0, sizeof(buffer_trace));
+ buf.fd = fd;
+ buf.type = type;
+ buf.index = index;
+ pthread_mutex_lock(&ctx_trace.lock);
+ ctx_trace.buffers.push_front(buf);
+ pthread_mutex_unlock(&ctx_trace.lock);
+}
+
+int get_buffer_fd_trace(__u32 type, __u32 index)
+{
+ int fd = 0;
+ pthread_mutex_lock(&ctx_trace.lock);
+ for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
+ if ((it->type == type) && (it->index == index)) {
+ fd = it->fd;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&ctx_trace.lock);
+ return fd;
+}
+
+__u32 get_buffer_type_trace(int fd)
+{
+ __u32 ret = 0;
+ pthread_mutex_lock(&ctx_trace.lock);
+ for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
+ if (it->fd == fd) {
+ ret = it->type;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&ctx_trace.lock);
+ return ret;
+}
+
+int get_buffer_index_trace(int fd)
+{
+ int index = -1;
+ pthread_mutex_lock(&ctx_trace.lock);
+ for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
+ if (it->fd == fd) {
+ index = it->index;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&ctx_trace.lock);
+ return index;
+}
+
+void set_buffer_address_trace(int fd, long address)
+{
+ pthread_mutex_lock(&ctx_trace.lock);
+ for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
+ if (it->fd == fd) {
+ it->address = address;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&ctx_trace.lock);
+}
+
+long get_buffer_address_trace(int fd)
+{
+ long int address = 0;
+ pthread_mutex_lock(&ctx_trace.lock);
+ for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
+ if (it->fd == fd) {
+ address = it->address;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&ctx_trace.lock);
+ return address;
+}
+
+void set_buffer_bytesused_trace(int fd, __u32 bytesused)
+{
+ pthread_mutex_lock(&ctx_trace.lock);
+ for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
+ if (it->fd == fd) {
+ it->bytesused = bytesused;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&ctx_trace.lock);
+}
+
+long get_buffer_bytesused_trace(int fd)
+{
+ long int bytesused = -1;
+
+ pthread_mutex_lock(&ctx_trace.lock);
+ for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
+ if (it->fd == fd) {
+ bytesused = it->bytesused;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&ctx_trace.lock);
+
+ return bytesused;
+}
+
+/* Returns true if the memory address is a mapped buffer address, false otherwise. */
+bool buffer_is_mapped(long buffer_address)
+{
+ bool ret = false;
+ pthread_mutex_lock(&ctx_trace.lock);
+ for (auto it = ctx_trace.buffers.cbegin(); it != ctx_trace.buffers.cend(); ++it) {
+ if (it->address == buffer_address) {
+ ret = true;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&ctx_trace.lock);
+ return ret;
+}
+
+void write_json_object_to_json_file(json_object *jobj, int flags)
+{
+ if (pretty_print_all())
+ flags = JSON_C_TO_STRING_PRETTY;
+
+ std::string json_str = json_object_to_json_string_ext(jobj, flags);
+ pthread_mutex_lock(&ctx_trace.lock);
+
+ if (ctx_trace.trace_filename.empty()) {
+ ctx_trace.trace_filename = getenv("TRACE_ID");
+ ctx_trace.trace_filename += ".json";
+ ctx_trace.trace_file = fopen(ctx_trace.trace_filename.c_str(), "a");
+ }
+
+ fwrite(json_str.c_str(), sizeof(char), json_str.length(), ctx_trace.trace_file);
+ fputs(",\n", ctx_trace.trace_file);
+ fflush(ctx_trace.trace_file);
+ pthread_mutex_unlock(&ctx_trace.lock);
+}
diff --git a/utils/tracer/trace-info.cpp b/utils/tracer/trace-info.cpp
new file mode 100644
index 00000000..a1d58c64
--- /dev/null
+++ b/utils/tracer/trace-info.cpp
@@ -0,0 +1,358 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include <v4l2-info.h>
+#include "trace-info.h"
+
+struct definition {
+ unsigned long val;
+ const char *str;
+};
+
+static std::string val2s(unsigned long val, const definition *def)
+{
+ std::string s;
+
+ if (val == 0)
+ return s;
+
+ while ((def->val) && (def->val != val))
+ def++;
+
+ if (def->val == val)
+ s = def->str;
+
+ return s;
+}
+
+std::string ioctl2s_video(unsigned long request)
+{
+ static constexpr definition defs[] = {
+ { VIDIOC_QUERYCAP, "VIDIOC_QUERYCAP" },
+ { VIDIOC_ENUM_FMT, "VIDIOC_ENUM_FMT" },
+ { VIDIOC_G_FMT, "VIDIOC_G_FMT" },
+ { VIDIOC_S_FMT, "VIDIOC_S_FMT" },
+ { VIDIOC_REQBUFS, "VIDIOC_REQBUFS" },
+ { VIDIOC_QUERYBUF, "VIDIOC_QUERYBUF" },
+ { VIDIOC_G_FBUF, "VIDIOC_G_FBUF" },
+ { VIDIOC_S_FBUF, "VIDIOC_S_FBUF" },
+ { VIDIOC_OVERLAY, "VIDIOC_OVERLAY" },
+ { VIDIOC_QBUF, "VIDIOC_QBUF" },
+ { VIDIOC_EXPBUF, "VIDIOC_EXPBUF" },
+ { VIDIOC_DQBUF, "VIDIOC_DQBUF" },
+ { VIDIOC_STREAMON, "VIDIOC_STREAMON" },
+ { VIDIOC_STREAMOFF, "VIDIOC_STREAMOFF" },
+ { VIDIOC_G_PARM, "VIDIOC_G_PARM" },
+ { VIDIOC_S_PARM, "VIDIOC_S_PARM" },
+ { VIDIOC_G_STD, "VIDIOC_G_STD" },
+ { VIDIOC_S_STD, "VIDIOC_S_STD" },
+ { VIDIOC_ENUMSTD, "VIDIOC_ENUMSTD" },
+ { VIDIOC_ENUMINPUT, "VIDIOC_ENUMINPUT" },
+ { VIDIOC_G_CTRL, "VIDIOC_G_CTRL" },
+ { VIDIOC_S_CTRL, "VIDIOC_S_CTRL" },
+ { VIDIOC_G_TUNER, "VIDIOC_G_TUNER" },
+ { VIDIOC_S_TUNER, "VIDIOC_S_TUNER" },
+ { VIDIOC_G_AUDIO, "VIDIOC_G_AUDIO" },
+ { VIDIOC_S_AUDIO, "VIDIOC_S_AUDIO" },
+ { VIDIOC_QUERYCTRL, "VIDIOC_QUERYCTRL" },
+ { VIDIOC_QUERYMENU, "VIDIOC_QUERYMENU" },
+ { VIDIOC_G_INPUT, "VIDIOC_G_INPUT" },
+ { VIDIOC_S_INPUT, "VIDIOC_S_INPUT" },
+ { VIDIOC_G_EDID, "VIDIOC_G_EDID" },
+ { VIDIOC_S_EDID, "VIDIOC_S_EDID" },
+ { VIDIOC_G_OUTPUT, "VIDIOC_G_OUTPUT" },
+ { VIDIOC_S_OUTPUT, "VIDIOC_S_OUTPUT" },
+ { VIDIOC_ENUMOUTPUT, "VIDIOC_ENUMOUTPUT" },
+ { VIDIOC_G_AUDOUT, "VIDIOC_G_AUDOUT" },
+ { VIDIOC_S_AUDOUT, "VIDIOC_S_AUDOUT" },
+ { VIDIOC_G_MODULATOR, "VIDIOC_G_MODULATOR" },
+ { VIDIOC_S_MODULATOR, "VIDIOC_S_MODULATOR" },
+ { VIDIOC_G_FREQUENCY, "VIDIOC_G_FREQUENCY" },
+ { VIDIOC_S_FREQUENCY, "VIDIOC_S_FREQUENCY" },
+ { VIDIOC_CROPCAP, "VIDIOC_CROPCAP" },
+ { VIDIOC_G_CROP, "VIDIOC_G_CROP" },
+ { VIDIOC_S_CROP, "VIDIOC_S_CROP" },
+ { VIDIOC_G_JPEGCOMP, "VIDIOC_G_JPEGCOMP" },
+ { VIDIOC_S_JPEGCOMP, "VIDIOC_S_JPEGCOMP" },
+ { VIDIOC_QUERYSTD, "VIDIOC_QUERYSTD" },
+ { VIDIOC_TRY_FMT, "VIDIOC_TRY_FMT" },
+ { VIDIOC_ENUMAUDIO, "VIDIOC_ENUMAUDIO" },
+ { VIDIOC_ENUMAUDOUT, "VIDIOC_ENUMAUDOUT" },
+ { VIDIOC_G_PRIORITY, "VIDIOC_G_PRIORITY" },
+ { VIDIOC_S_PRIORITY, "VIDIOC_S_PRIORITY" },
+ { VIDIOC_G_SLICED_VBI_CAP, "VIDIOC_G_SLICED_VBI_CAP" },
+ { VIDIOC_LOG_STATUS, "VIDIOC_LOG_STATUS" },
+ { VIDIOC_G_EXT_CTRLS, "VIDIOC_G_EXT_CTRLS" },
+ { VIDIOC_S_EXT_CTRLS, "VIDIOC_S_EXT_CTRLS" },
+ { VIDIOC_TRY_EXT_CTRLS, "VIDIOC_TRY_EXT_CTRLS" },
+ { VIDIOC_ENUM_FRAMESIZES, "VIDIOC_ENUM_FRAMESIZES" },
+ { VIDIOC_ENUM_FRAMEINTERVALS, "VIDIOC_ENUM_FRAMEINTERVALS" },
+ { VIDIOC_G_ENC_INDEX, "VIDIOC_G_ENC_INDEX" },
+ { VIDIOC_ENCODER_CMD, "VIDIOC_ENCODER_CMD" },
+ { VIDIOC_TRY_ENCODER_CMD, "VIDIOC_TRY_ENCODER_CMD" },
+ { VIDIOC_DBG_S_REGISTER, "VIDIOC_DBG_S_REGISTER" },
+ { VIDIOC_DBG_G_REGISTER, "VIDIOC_DBG_G_REGISTER" },
+ { VIDIOC_S_HW_FREQ_SEEK, "VIDIOC_S_HW_FREQ_SEEK" },
+ { VIDIOC_S_DV_TIMINGS, "VIDIOC_S_DV_TIMINGS" },
+ { VIDIOC_G_DV_TIMINGS, "VIDIOC_G_DV_TIMINGS" },
+ { VIDIOC_DQEVENT, "VIDIOC_DQEVENT" },
+ { VIDIOC_SUBSCRIBE_EVENT, "VIDIOC_SUBSCRIBE_EVENT" },
+ { VIDIOC_UNSUBSCRIBE_EVENT, "VIDIOC_UNSUBSCRIBE_EVENT" },
+ { VIDIOC_CREATE_BUFS, "VIDIOC_CREATE_BUFS" },
+ { VIDIOC_PREPARE_BUF, "VIDIOC_PREPARE_BUF" },
+ { VIDIOC_G_SELECTION, "VIDIOC_G_SELECTION" },
+ { VIDIOC_S_SELECTION, "VIDIOC_S_SELECTION" },
+ { VIDIOC_DECODER_CMD, "VIDIOC_DECODER_CMD" },
+ { VIDIOC_TRY_DECODER_CMD, "VIDIOC_TRY_DECODER_CMD" },
+ { VIDIOC_ENUM_DV_TIMINGS, "VIDIOC_ENUM_DV_TIMINGS" },
+ { VIDIOC_QUERY_DV_TIMINGS, "VIDIOC_QUERY_DV_TIMINGS" },
+ { VIDIOC_DV_TIMINGS_CAP, "VIDIOC_DV_TIMINGS_CAP" },
+ { VIDIOC_ENUM_FREQ_BANDS, "VIDIOC_ENUM_FREQ_BANDS" },
+ { VIDIOC_DBG_G_CHIP_INFO, "VIDIOC_DBG_G_CHIP_INFO" },
+ { VIDIOC_QUERY_EXT_CTRL, "VIDIOC_QUERY_EXT_CTRL" },
+ { BASE_VIDIOC_PRIVATE, "BASE_VIDIOC_PRIVATE" },
+ { 0, nullptr }
+ };
+ return val2s(request, defs);
+}
+
+std::string ioctl2s_media(unsigned long request)
+{
+ static constexpr definition defs[] = {
+ { MEDIA_IOC_DEVICE_INFO, "MEDIA_IOC_DEVICE_INFO" },
+ { MEDIA_IOC_ENUM_ENTITIES, "MEDIA_IOC_ENUM_ENTITIES" },
+ { MEDIA_IOC_ENUM_LINKS, "MEDIA_IOC_ENUM_LINKS" },
+ { MEDIA_IOC_SETUP_LINK, "MEDIA_IOC_SETUP_LINK" },
+ { MEDIA_IOC_G_TOPOLOGY, "MEDIA_IOC_G_TOPOLOGY" },
+ { MEDIA_IOC_REQUEST_ALLOC, "MEDIA_IOC_REQUEST_ALLOC" },
+ { MEDIA_REQUEST_IOC_QUEUE, "MEDIA_REQUEST_IOC_QUEUE" },
+ { MEDIA_REQUEST_IOC_REINIT, "MEDIA_REQUEST_IOC_REINIT" },
+ { 0, nullptr }
+ };
+ return val2s(request, defs);
+}
+
+std::string ctrltype2s(__u32 val)
+{
+ static constexpr definition defs[] = {
+ { V4L2_CTRL_TYPE_INTEGER, "V4L2_CTRL_TYPE_INTEGER" },
+ { V4L2_CTRL_TYPE_BOOLEAN, "V4L2_CTRL_TYPE_BOOLEAN" },
+ { V4L2_CTRL_TYPE_MENU, "V4L2_CTRL_TYPE_MENU" },
+ { V4L2_CTRL_TYPE_BUTTON, "V4L2_CTRL_TYPE_BUTTON" },
+ { V4L2_CTRL_TYPE_INTEGER64, "V4L2_CTRL_TYPE_INTEGER64" },
+ { V4L2_CTRL_TYPE_CTRL_CLASS, "V4L2_CTRL_TYPE_CTRL_CLASS" },
+ { V4L2_CTRL_TYPE_STRING, "V4L2_CTRL_TYPE_STRING" },
+ { V4L2_CTRL_TYPE_BITMASK, "V4L2_CTRL_TYPE_BITMASK" },
+ { V4L2_CTRL_TYPE_INTEGER_MENU, "V4L2_CTRL_TYPE_INTEGER_MENU" },
+ { V4L2_CTRL_COMPOUND_TYPES, "V4L2_CTRL_COMPOUND_TYPES" },
+ { V4L2_CTRL_TYPE_U8, "V4L2_CTRL_TYPE_U8" },
+ { V4L2_CTRL_TYPE_U16, "V4L2_CTRL_TYPE_U16" },
+ { V4L2_CTRL_TYPE_U32, "V4L2_CTRL_TYPE_U32" },
+ { V4L2_CTRL_TYPE_AREA, "V4L2_CTRL_TYPE_AREA" },
+ { V4L2_CTRL_TYPE_HDR10_CLL_INFO, "V4L2_CTRL_TYPE_HDR10_CLL_INFO" },
+ { V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY, "V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY" },
+ { V4L2_CTRL_TYPE_H264_SPS, "V4L2_CTRL_TYPE_H264_SPS" },
+ { V4L2_CTRL_TYPE_H264_PPS, "V4L2_CTRL_TYPE_H264_PPS" },
+ { V4L2_CTRL_TYPE_H264_SCALING_MATRIX, "V4L2_CTRL_TYPE_H264_SCALING_MATRIX" },
+ { V4L2_CTRL_TYPE_H264_SLICE_PARAMS, "V4L2_CTRL_TYPE_H264_SLICE_PARAMS" },
+ { V4L2_CTRL_TYPE_H264_DECODE_PARAMS, "V4L2_CTRL_TYPE_H264_DECODE_PARAMS" },
+ { V4L2_CTRL_TYPE_H264_PRED_WEIGHTS, "V4L2_CTRL_TYPE_H264_PRED_WEIGHTS" },
+ { V4L2_CTRL_TYPE_FWHT_PARAMS, "V4L2_CTRL_TYPE_FWHT_PARAMS" },
+ { V4L2_CTRL_TYPE_VP8_FRAME, "V4L2_CTRL_TYPE_VP8_FRAME" },
+ { V4L2_CTRL_TYPE_MPEG2_QUANTISATION, "V4L2_CTRL_TYPE_MPEG2_QUANTISATION" },
+ { V4L2_CTRL_TYPE_MPEG2_SEQUENCE, "V4L2_CTRL_TYPE_MPEG2_SEQUENCE" },
+ { V4L2_CTRL_TYPE_MPEG2_PICTURE, "V4L2_CTRL_TYPE_MPEG2_PICTURE" },
+ { V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR, "V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR" },
+ { V4L2_CTRL_TYPE_VP9_FRAME, "V4L2_CTRL_TYPE_VP9_FRAME" },
+ { 0, nullptr }
+ };
+ return val2s(val, defs);
+}
+
+std::string capflag2s(unsigned long cap)
+{
+ static constexpr flag_def def[] = {
+ { V4L2_CAP_VIDEO_M2M_MPLANE, "V4L2_CAP_VIDEO_M2M_MPLANE" },
+ { V4L2_CAP_VIDEO_CAPTURE, "V4L2_CAP_VIDEO_CAPTURE" },
+ { V4L2_CAP_VIDEO_OUTPUT, "V4L2_CAP_VIDEO_OUTPUT" },
+ { V4L2_CAP_VIDEO_OVERLAY, "V4L2_CAP_VIDEO_OVERLAY" },
+ { V4L2_CAP_VBI_CAPTURE, "V4L2_CAP_VBI_CAPTURE" },
+ { V4L2_CAP_VBI_OUTPUT, "V4L2_CAP_VBI_OUTPUT" },
+ { V4L2_CAP_SLICED_VBI_CAPTURE, "V4L2_CAP_SLICED_VBI_CAPTURE" },
+ { V4L2_CAP_SLICED_VBI_OUTPUT, "V4L2_CAP_SLICED_VBI_OUTPUT" },
+ { V4L2_CAP_RDS_CAPTURE, "V4L2_CAP_RDS_CAPTURE" },
+ { V4L2_CAP_VIDEO_OUTPUT_OVERLAY, "V4L2_CAP_VIDEO_OUTPUT_OVERLAY" },
+ { V4L2_CAP_HW_FREQ_SEEK, "V4L2_CAP_HW_FREQ_SEEK" },
+ { V4L2_CAP_RDS_OUTPUT, "V4L2_CAP_RDS_OUTPUT" },
+ { V4L2_CAP_VIDEO_CAPTURE_MPLANE, "V4L2_CAP_VIDEO_CAPTURE_MPLANE" },
+ { V4L2_CAP_VIDEO_OUTPUT_MPLANE, "V4L2_CAP_VIDEO_OUTPUT_MPLANE" },
+ { V4L2_CAP_VIDEO_M2M_MPLANE, "V4L2_CAP_VIDEO_M2M_MPLANE" },
+ { V4L2_CAP_VIDEO_M2M, "V4L2_CAP_VIDEO_M2M" },
+ { V4L2_CAP_TUNER, "V4L2_CAP_TUNER" },
+ { V4L2_CAP_AUDIO, "V4L2_CAP_AUDIO" },
+ { V4L2_CAP_RADIO, "V4L2_CAP_RADIO" },
+ { V4L2_CAP_MODULATOR, "V4L2_CAP_MODULATOR" },
+ { V4L2_CAP_SDR_CAPTURE, "V4L2_CAP_SDR_CAPTURE" },
+ { V4L2_CAP_EXT_PIX_FORMAT, "V4L2_CAP_EXT_PIX_FORMAT" },
+ { V4L2_CAP_SDR_OUTPUT, "V4L2_CAP_SDR_OUTPUT" },
+ { V4L2_CAP_META_CAPTURE, "V4L2_CAP_META_CAPTURE" },
+ { V4L2_CAP_READWRITE, "V4L2_CAP_READWRITE" },
+ { V4L2_CAP_ASYNCIO, "V4L2_CAP_ASYNCIO" },
+ { V4L2_CAP_STREAMING, "V4L2_CAP_STREAMING" },
+ { V4L2_CAP_META_OUTPUT, "V4L2_CAP_META_OUTPUT" },
+ { V4L2_CAP_TOUCH, "V4L2_CAP_TOUCH" },
+ { V4L2_CAP_IO_MC, "V4L2_CAP_IO_MC" },
+ { V4L2_CAP_DEVICE_CAPS, "V4L2_CAP_DEVICE_CAPS" },
+ { 0, nullptr }
+ };
+ return flags2s(cap, def);
+}
+
+std::string bufsyncflag2s(unsigned long flag)
+{
+ static constexpr flag_def def[] = {
+ { DMA_BUF_SYNC_READ , "DMA_BUF_SYNC_READ " },
+ { DMA_BUF_SYNC_WRITE, "DMA_BUF_SYNC_WRITE" },
+ { DMA_BUF_SYNC_RW , "DMA_BUF_SYNC_RW " },
+ { DMA_BUF_SYNC_START , "DMA_BUF_SYNC_START" },
+ { DMA_BUF_SYNC_END, "DMA_BUF_SYNC_END" },
+ { 0, nullptr }
+ };
+ return flags2s(flag, def);
+}
+
+std::string vp8_segment_flag2s(unsigned long flag)
+{
+ static constexpr flag_def def[] = {
+ { V4L2_VP8_SEGMENT_FLAG_ENABLED, "V4L2_VP8_SEGMENT_FLAG_ENABLED" },
+ { V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP, "V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP" },
+ { V4L2_VP8_SEGMENT_FLAG_UPDATE_FEATURE_DATA, "V4L2_VP8_SEGMENT_FLAG_UPDATE_FEATURE_DATA" },
+ { V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE, "V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE" },
+ { 0, nullptr }
+ };
+ return flags2s(flag, def);
+}
+
+std::string vp8_loop_filter_flag2s(unsigned long flag)
+{
+ static constexpr flag_def def[] = {
+ { V4L2_VP8_LF_ADJ_ENABLE, "V4L2_VP8_LF_ADJ_ENABLE" },
+ { V4L2_VP8_LF_DELTA_UPDATE, "V4L2_VP8_LF_DELTA_UPDATE" },
+ { V4L2_VP8_LF_FILTER_TYPE_SIMPLE, "V4L2_VP8_LF_FILTER_TYPE_SIMPLE" },
+ { 0, nullptr }
+ };
+ return flags2s(flag, def);
+}
+
+std::string vp8_frame_flag2s(unsigned long flag)
+{
+ static constexpr flag_def def[] = {
+ { V4L2_VP8_FRAME_FLAG_KEY_FRAME, "V4L2_VP8_FRAME_FLAG_KEY_FRAME" },
+ { V4L2_VP8_FRAME_FLAG_EXPERIMENTAL, "V4L2_VP8_FRAME_FLAG_EXPERIMENTAL" },
+ { V4L2_VP8_FRAME_FLAG_SHOW_FRAME, "V4L2_VP8_FRAME_FLAG_SHOW_FRAME" },
+ { V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF, "V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF" },
+ { V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN, "V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN" },
+ { V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT, "V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT" },
+ { 0, nullptr }
+ };
+ return flags2s(flag, def);
+}
+
+std::string ver2s(unsigned int version)
+{
+ char buf[16];
+ sprintf(buf, "%d.%d.%d", version >> 16, (version >> 8) & 0xff, version & 0xff);
+ return buf;
+}
+
+std::string which2s(unsigned long which)
+{
+ std::string s = "unknown";
+
+ switch (which) {
+ case V4L2_CTRL_WHICH_CUR_VAL:
+ s = "V4L2_CTRL_WHICH_CUR_VAL";
+ break;
+ case V4L2_CTRL_WHICH_DEF_VAL:
+ s= "V4L2_CTRL_WHICH_DEF_VAL";
+ break;
+ case V4L2_CTRL_WHICH_REQUEST_VAL:
+ s = "V4L2_CTRL_WHICH_REQUEST_VAL";
+ break;
+ default:
+ break;
+ }
+
+ return s;
+}
+
+std::string ctrlclass2s(__u32 id)
+{
+ static constexpr definition defs[] = {
+ { V4L2_CTRL_CLASS_USER, "V4L2_CTRL_CLASS_USER" },
+ { V4L2_CTRL_CLASS_CODEC, "V4L2_CTRL_CLASS_CODEC" },
+ { V4L2_CTRL_CLASS_CAMERA, "V4L2_CTRL_CLASS_CAMERA" },
+ { V4L2_CTRL_CLASS_FM_TX, "V4L2_CTRL_CLASS_FM_TX" },
+ { V4L2_CTRL_CLASS_FLASH, "V4L2_CTRL_CLASS_FLASH" },
+ { V4L2_CTRL_CLASS_JPEG, "V4L2_CTRL_CLASS_JPEG" },
+ { V4L2_CTRL_CLASS_IMAGE_SOURCE, "V4L2_CTRL_CLASS_IMAGE_SOURCE" },
+ { V4L2_CTRL_CLASS_IMAGE_PROC, "V4L2_CTRL_CLASS_IMAGE_PROC" },
+ { V4L2_CTRL_CLASS_DV, "V4L2_CTRL_CLASS_DV" },
+ { V4L2_CTRL_CLASS_FM_RX, "V4L2_CTRL_CLASS_FM_RX" },
+ { V4L2_CTRL_CLASS_RF_TUNER, "V4L2_CTRL_CLASS_RF_TUNER" },
+ { V4L2_CTRL_CLASS_DETECT, "V4L2_CTRL_CLASS_DETECT" },
+ { V4L2_CTRL_CLASS_CODEC_STATELESS, "V4L2_CTRL_CLASS_CODEC_STATELESS" },
+ { V4L2_CTRL_CLASS_COLORIMETRY, "V4L2_CTRL_CLASS_COLORIMETRY" },
+ { 0, nullptr }
+ };
+ return val2s(id & 0xff0000, defs);
+}
+
+std::string request_buffers_flag2s(unsigned int flag)
+{
+ static constexpr flag_def def[] = {
+ { V4L2_MEMORY_FLAG_NON_COHERENT, "V4L2_MEMORY_FLAG_NON_COHERENT" },
+ { 0, nullptr }
+ };
+ return flags2s(flag, def);
+}
+
+std::string v4l2_memory2s(__u32 id)
+{
+ static constexpr definition defs[] = {
+ { V4L2_MEMORY_MMAP, "V4L2_MEMORY_MMAP" },
+ { V4L2_MEMORY_USERPTR, "V4L2_MEMORY_USERPTR" },
+ { V4L2_MEMORY_OVERLAY, "V4L2_MEMORY_OVERLAY" },
+ { V4L2_MEMORY_DMABUF, "V4L2_MEMORY_DMABUF" },
+ { 0, nullptr }
+ };
+ return val2s(id, defs);
+}
+
+std::string tc_type2s(__u32 id)
+{
+ static constexpr definition defs[] = {
+ { V4L2_TC_TYPE_24FPS, "V4L2_TC_TYPE_24FPS" },
+ { V4L2_TC_TYPE_25FPS, "V4L2_TC_TYPE_25FPS" },
+ { V4L2_TC_TYPE_30FPS, "V4L2_TC_TYPE_30FPS" },
+ { V4L2_TC_TYPE_50FPS, "V4L2_TC_TYPE_50FPS" },
+ { V4L2_TC_TYPE_60FPS, "V4L2_TC_TYPE_60FPS" },
+ { 0, nullptr }
+ };
+ return val2s(id, defs);
+}
+
+std::string tc_flag2s(unsigned int flag)
+{
+ static constexpr flag_def def[] = {
+ { V4L2_TC_FLAG_DROPFRAME, "V4L2_TC_FLAG_DROPFRAME" },
+ { V4L2_TC_FLAG_COLORFRAME, "V4L2_TC_FLAG_COLORFRAME" },
+ { V4L2_TC_USERBITS_field, "V4L2_TC_USERBITS_field" },
+ { V4L2_TC_USERBITS_USERDEFINED, "V4L2_TC_USERBITS_USERDEFINED" },
+ { V4L2_TC_USERBITS_8BITCHARS, "V4L2_TC_USERBITS_8BITCHARS" },
+ { 0, nullptr }
+ };
+ return flags2s(flag, def);
+}
diff --git a/utils/tracer/trace-info.h b/utils/tracer/trace-info.h
new file mode 100644
index 00000000..4c06190a
--- /dev/null
+++ b/utils/tracer/trace-info.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#ifndef TRACE_INFO_H
+#define TRACE_INFO_H
+
+#include <cstring>
+#include <linux/videodev2.h>
+#include <linux/media.h>
+#include <linux/dma-buf.h>
+
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+
+enum LIBTRACER_SYSCALL {
+ LIBTRACER_SYSCALL_IOCTL,
+ LIBTRACER_SYSCALL_OPEN,
+ LIBTRACER_SYSCALL_OPEN64,
+ LIBTRACER_SYSCALL_CLOSE,
+ LIBTRACER_SYSCALL_MMAP64,
+ LIBTRACER_SYSCALL_MUNMAP,
+ LIBTRACER_SYSCALL_SELECT,
+ LIBTRACER_SYSCALL_POLL,
+};
+
+/* Convert an ioctl request of type 'V' to string. */
+std::string ioctl2s_video(unsigned long request);
+
+/* Convert an ioctl request of type '|' to string. */
+std::string ioctl2s_media(unsigned long request);
+
+/* Convert capability flags to common separated string. */
+std::string capflag2s(unsigned long cap);
+
+/* Convert a decimal number to a string with kernel_version.major_revision.minor_revision. */
+std::string ver2s(unsigned int version);
+
+/* Convert v4l2_ext_controls member "which" to string. */
+std::string which2s(unsigned long which);
+
+/* Convert control class to string. */
+std::string ctrlclass2s(__u32 id);
+
+/* Convert control type to string. */
+std::string ctrltype2s(__u32 type);
+
+/* Convert struct dma_buf_sync flags to string. */
+std::string bufsyncflag2s(unsigned long flag);
+
+/* Convert v4l2_vp8_segment flags to string. */
+std::string vp8_segment_flag2s(unsigned long flag);
+
+/* Convert v4l2_vp8_loop_filter flags to string. */
+std::string vp8_loop_filter_flag2s(unsigned long flag);
+
+/* Convert v4l2_ctrl_vp8_frame flags to string. */
+std::string vp8_frame_flag2s(unsigned long flag);
+
+/* Convert v4l2_requestbuffers flags to string. */
+std::string request_buffers_flag2s(unsigned int flag);
+
+/* Convert enum v4l2_memory type to string. */
+std::string v4l2_memory2s(__u32 id);
+
+/* Convert v4l2_timecode type to string. */
+std::string tc_type2s(__u32 id);
+
+/* Convert v4l2_timecode flags to string. */
+std::string tc_flag2s(unsigned int flag);
+
+#endif
diff --git a/utils/tracer/trace-vp8.cpp b/utils/tracer/trace-vp8.cpp
new file mode 100644
index 00000000..a0a8ccdd
--- /dev/null
+++ b/utils/tracer/trace-vp8.cpp
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "libtracer.h"
+
+json_object *trace_v4l2_vp8_segment(struct v4l2_vp8_segment segment)
+{
+ json_object *vp8_segment_obj = json_object_new_object();
+
+ /* __s8 quant_update[4] */
+ json_object *quant_update_obj = json_object_new_array();
+ for (size_t i = 0; i < ARRAY_SIZE(segment.quant_update); i++)
+ json_object_array_add(quant_update_obj, json_object_new_int(segment.quant_update[i]));
+ json_object_object_add(vp8_segment_obj, "quant_update", quant_update_obj);
+
+ /* __s8 lf_update[4] */
+ json_object *lf_update_obj = json_object_new_array();
+ for (size_t i = 0; i < ARRAY_SIZE(segment.lf_update); i++)
+ json_object_array_add(lf_update_obj, json_object_new_int(segment.lf_update[i]));
+ json_object_object_add(vp8_segment_obj, "lf_update", lf_update_obj);
+
+ /* __u8 segment_probs[3] */
+ json_object *segment_probs_obj = json_object_new_array();
+ for (size_t i = 0; i < ARRAY_SIZE(segment.segment_probs); i++)
+ json_object_array_add(segment_probs_obj, json_object_new_int(segment.segment_probs[i]));
+ json_object_object_add(vp8_segment_obj, "segment_probs", segment_probs_obj);
+
+ json_object_object_add(vp8_segment_obj, "padding", json_object_new_int(segment.padding));
+ json_object_object_add(vp8_segment_obj, "flags", json_object_new_int(segment.flags));
+ json_object_object_add(vp8_segment_obj, "flags_str",
+ json_object_new_string(vp8_segment_flag2s(segment.flags).c_str()));
+
+ return vp8_segment_obj;
+}
+
+json_object *trace_v4l2_vp8_loop_filter(struct v4l2_vp8_loop_filter lf)
+{
+ json_object *vp8_loop_filter_obj = json_object_new_object();
+
+ /* __s8 ref_frm_delta[4] */
+ json_object *ref_frm_delta_obj = json_object_new_array();
+ for (size_t i = 0; i < ARRAY_SIZE(lf.ref_frm_delta); i++)
+ json_object_array_add(ref_frm_delta_obj, json_object_new_int(lf.ref_frm_delta[i]));
+ json_object_object_add(vp8_loop_filter_obj, "ref_frm_delta", ref_frm_delta_obj);
+
+ /* __s8 mb_mode_delta[4] */
+ json_object *mb_mode_delta_obj = json_object_new_array();
+ for (size_t i = 0; i < ARRAY_SIZE(lf.mb_mode_delta); i++)
+ json_object_array_add(mb_mode_delta_obj, json_object_new_int(lf.mb_mode_delta[i]));
+ json_object_object_add(vp8_loop_filter_obj, "mb_mode_delta", mb_mode_delta_obj);
+
+ json_object_object_add(vp8_loop_filter_obj, "sharpness_level", json_object_new_int(lf.sharpness_level));
+ json_object_object_add(vp8_loop_filter_obj, "level", json_object_new_int(lf.level));
+ json_object_object_add(vp8_loop_filter_obj, "padding", json_object_new_int(lf.padding));
+ json_object_object_add(vp8_loop_filter_obj, "flags", json_object_new_int(lf.flags));
+ json_object_object_add(vp8_loop_filter_obj, "flags_str", json_object_new_string(vp8_loop_filter_flag2s(lf.flags).c_str()));
+
+ return vp8_loop_filter_obj;
+}
+
+json_object *trace_v4l2_vp8_quantization(struct v4l2_vp8_quantization quant)
+{
+ json_object *vp8_quantization_obj = json_object_new_object();
+
+ json_object_object_add(vp8_quantization_obj, "y_ac_qi", json_object_new_int(quant.y_ac_qi));
+ json_object_object_add(vp8_quantization_obj, "y_dc_delta", json_object_new_int(quant.y_dc_delta));
+ json_object_object_add(vp8_quantization_obj, "y2_dc_delta", json_object_new_int(quant.y2_dc_delta));
+ json_object_object_add(vp8_quantization_obj, "y2_ac_delta", json_object_new_int(quant.y2_ac_delta));
+ json_object_object_add(vp8_quantization_obj, "uv_dc_delta", json_object_new_int(quant.uv_dc_delta));
+ json_object_object_add(vp8_quantization_obj, "uv_ac_delta", json_object_new_int(quant.uv_ac_delta));
+ json_object_object_add(vp8_quantization_obj, "padding", json_object_new_int(quant.padding));
+
+ return vp8_quantization_obj;
+}
+
+json_object *trace_v4l2_vp8_entropy(struct v4l2_vp8_entropy entropy)
+{
+ json_object *vp8_entropy_obj = json_object_new_object();
+
+ /*__u8 coeff_probs[4][8][3][V4L2_VP8_COEFF_PROB_CNT] */
+ json_object *coeff_probs_obj = json_object_new_array();
+ for (size_t i = 0; i < ARRAY_SIZE(entropy.coeff_probs); i++)
+ for (size_t j = 0; j < ARRAY_SIZE(entropy.coeff_probs[0]); j++)
+ for (size_t k = 0; k < ARRAY_SIZE(entropy.coeff_probs[0][0]); k++)
+ for (size_t l = 0; l < V4L2_VP8_COEFF_PROB_CNT; l++)
+ json_object_array_add(coeff_probs_obj,
+ json_object_new_int(entropy.coeff_probs[i][j][k][l]));
+ json_object_object_add(vp8_entropy_obj, "coeff_probs", coeff_probs_obj);
+
+ /* __u8 y_mode_probs[4] */
+ json_object *y_mode_probs_obj = json_object_new_array();
+ for (size_t i = 0; i < ARRAY_SIZE(entropy.y_mode_probs); i++)
+ json_object_array_add(y_mode_probs_obj, json_object_new_int(entropy.y_mode_probs[i]));
+ json_object_object_add(vp8_entropy_obj, "y_mode_probs", y_mode_probs_obj);
+
+ /* __u8 uv_mode_probs[3] */
+ json_object *uv_mode_probs_obj = json_object_new_array();
+ for (size_t i = 0; i < ARRAY_SIZE(entropy.uv_mode_probs); i++)
+ json_object_array_add(uv_mode_probs_obj, json_object_new_int(entropy.uv_mode_probs[i]));
+ json_object_object_add(vp8_entropy_obj, "uv_mode_probs", uv_mode_probs_obj);
+
+ /* __u8 mv_probs[2][V4L2_VP8_MV_PROB_CNT] */
+ json_object *mv_probs_obj = json_object_new_array();
+ for (size_t i = 0; i < ARRAY_SIZE(entropy.mv_probs); i++)
+ for (size_t j = 0; j < V4L2_VP8_MV_PROB_CNT; j++)
+ json_object_array_add(mv_probs_obj, json_object_new_int(entropy.mv_probs[i][j]));
+ json_object_object_add(vp8_entropy_obj, "mv_probs", mv_probs_obj);
+
+ /*__u8 padding[3] */
+ json_object *padding_obj = json_object_new_array();
+ for (size_t i = 0; i < ARRAY_SIZE(entropy.padding); i++)
+ json_object_array_add(padding_obj, json_object_new_int(entropy.padding[i]));
+ json_object_object_add(vp8_entropy_obj, "padding", padding_obj);
+
+ return vp8_entropy_obj;
+}
+
+json_object *trace_v4l2_vp8_entropy_coder_state(struct v4l2_vp8_entropy_coder_state coder_state)
+{
+ json_object *vp8_entropy_coder_state_obj = json_object_new_object();
+
+ json_object_object_add(vp8_entropy_coder_state_obj, "range", json_object_new_int(coder_state.range));
+ json_object_object_add(vp8_entropy_coder_state_obj, "value", json_object_new_int(coder_state.value));
+ json_object_object_add(vp8_entropy_coder_state_obj, "bit_count", json_object_new_int(coder_state.bit_count));
+ json_object_object_add(vp8_entropy_coder_state_obj, "padding", json_object_new_int(coder_state.padding));
+
+ return vp8_entropy_coder_state_obj;
+}
+
+void trace_v4l2_ctrl_vp8_frame(void *p_vp8_frame, json_object *ctrl_obj)
+{
+ json_object *vp8_frame_obj = json_object_new_object();
+ struct v4l2_ctrl_vp8_frame *vp8_frame = static_cast<struct v4l2_ctrl_vp8_frame*>(p_vp8_frame);
+
+ /* struct v4l2_vp8_segment segment */
+ json_object *v4l2_vp8_segment_obj = trace_v4l2_vp8_segment(vp8_frame->segment);
+ json_object_object_add(vp8_frame_obj, "segment", v4l2_vp8_segment_obj);
+
+ /* struct v4l2_vp8_loop_filter lf */
+ json_object *v4l2_vp8_loop_filter_obj = trace_v4l2_vp8_loop_filter(vp8_frame->lf);
+ json_object_object_add(vp8_frame_obj, "lf", v4l2_vp8_loop_filter_obj);
+
+ /* struct v4l2_vp8_quantization quant */
+ json_object *v4l2_vp8_quantization_obj = trace_v4l2_vp8_quantization(vp8_frame->quant);
+ json_object_object_add(vp8_frame_obj, "quant", v4l2_vp8_quantization_obj);
+
+ /* struct v4l2_vp8_entropy entropy */
+ json_object *v4l2_vp8_entropy_obj = trace_v4l2_vp8_entropy(vp8_frame->entropy);
+ json_object_object_add(vp8_frame_obj, "entropy", v4l2_vp8_entropy_obj);
+
+ /* struct v4l2_vp8_entropy_coder_state coder_state */
+ json_object *v4l2_vp8_entropy_coder_state_obj = trace_v4l2_vp8_entropy_coder_state(vp8_frame->coder_state);
+ json_object_object_add(vp8_frame_obj, "coder_state", v4l2_vp8_entropy_coder_state_obj);
+
+ json_object_object_add(vp8_frame_obj, "width", json_object_new_int(vp8_frame->width));
+ json_object_object_add(vp8_frame_obj, "height", json_object_new_int(vp8_frame->height));
+ json_object_object_add(vp8_frame_obj, "horizontal_scale", json_object_new_int(vp8_frame->horizontal_scale));
+ json_object_object_add(vp8_frame_obj, "vertical_scale", json_object_new_int(vp8_frame->vertical_scale));
+ json_object_object_add(vp8_frame_obj, "version", json_object_new_int(vp8_frame->version));
+ json_object_object_add(vp8_frame_obj, "prob_skip_false", json_object_new_int(vp8_frame->prob_skip_false));
+ json_object_object_add(vp8_frame_obj, "prob_intra", json_object_new_int(vp8_frame->prob_intra));
+ json_object_object_add(vp8_frame_obj, "prob_last", json_object_new_int(vp8_frame->prob_last));
+ json_object_object_add(vp8_frame_obj, "prob_gf", json_object_new_int(vp8_frame->prob_gf));
+ json_object_object_add(vp8_frame_obj, "num_dct_parts", json_object_new_int(vp8_frame->num_dct_parts));
+ json_object_object_add(vp8_frame_obj, "first_part_size", json_object_new_int(vp8_frame->first_part_size));
+ json_object_object_add(vp8_frame_obj, "first_part_header_bits", json_object_new_int(vp8_frame->first_part_header_bits));
+
+ /* __u32 dct_part_sizes[8] */
+ json_object *dct_part_sizes_obj = json_object_new_array();
+ for (size_t i = 0; i < ARRAY_SIZE(vp8_frame->dct_part_sizes); i++)
+ json_object_array_add(dct_part_sizes_obj, json_object_new_int(vp8_frame->dct_part_sizes[i]));
+ json_object_object_add(vp8_frame_obj, "dct_part_sizes", dct_part_sizes_obj);
+
+ json_object_object_add(vp8_frame_obj, "last_frame_ts", json_object_new_int(vp8_frame->last_frame_ts));
+ json_object_object_add(vp8_frame_obj, "golden_frame_ts", json_object_new_int(vp8_frame->golden_frame_ts));
+ json_object_object_add(vp8_frame_obj, "alt_frame_ts", json_object_new_int(vp8_frame->alt_frame_ts));
+ json_object_object_add(vp8_frame_obj, "flags", json_object_new_int(vp8_frame->flags));
+ json_object_object_add(vp8_frame_obj, "flags_str", json_object_new_string(vp8_frame_flag2s(vp8_frame->flags).c_str()));
+
+ json_object_object_add(ctrl_obj, "v4l2_ctrl_vp8_frame", vp8_frame_obj);
+}
diff --git a/utils/tracer/trace.cpp b/utils/tracer/trace.cpp
new file mode 100644
index 00000000..5e952a7b
--- /dev/null
+++ b/utils/tracer/trace.cpp
@@ -0,0 +1,520 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "libtracer.h"
+
+json_object *trace_open(const char *path, int oflag, mode_t mode)
+{
+ json_object *open_args = json_object_new_object();
+
+ json_object_object_add(open_args, "path", json_object_new_string(path));
+ json_object_object_add(open_args, "oflag", json_object_new_int(oflag));
+ json_object_object_add(open_args, "mode", json_object_new_uint64(mode));
+
+ return open_args;
+}
+
+json_object *trace_mmap64(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
+{
+ json_object *mmap_args = json_object_new_object();
+
+ json_object_object_add(mmap_args, "addr", json_object_new_int64((int64_t)addr));
+ json_object_object_add(mmap_args, "len", json_object_new_uint64(len));
+ json_object_object_add(mmap_args, "prot", json_object_new_int(prot));
+ json_object_object_add(mmap_args, "flags", json_object_new_int(flags));
+ json_object_object_add(mmap_args, "fildes", json_object_new_int(fildes));
+ json_object_object_add(mmap_args, "off", json_object_new_int64(off));
+
+ return mmap_args;
+}
+
+/*
+ * Get a buffer from memory and convert it to a json string array.
+ * The bytes are displayed with the most-significant bits first.
+ */
+json_object *trace_buffer(unsigned char *buffer_pointer, __u32 bytesused)
+{
+ char buf[5];
+ std::string s;
+ int byte_count_per_line = 0;
+ json_object *mem_array_obj = json_object_new_array();
+
+ for (__u32 i = 0; i < bytesused; i++) {
+ memset(buf, 0, sizeof(buf));
+
+ /* Each byte e.g. D9 will write a string of two characters "D9". */
+ sprintf(buf, "%02x", buffer_pointer[i]);
+ s += buf;
+ byte_count_per_line++;
+
+ /* Add a space every two bytes e.g. "012A 4001" and a newline every 16 bytes. */
+ if (byte_count_per_line == 16) {
+ byte_count_per_line = 0;
+ json_object_array_add(mem_array_obj, json_object_new_string(s.c_str()));
+ s.clear();
+ } else if (i % 2) {
+ s += " ";
+ }
+ }
+
+ /* Trace the last line if it was less than a full 16 bytes. */
+ if (byte_count_per_line)
+ json_object_array_add(mem_array_obj, json_object_new_string(s.c_str()));
+
+ return mem_array_obj;
+}
+
+/* Get the decoded capture buffer from memory and write it to a binary yuv file. */
+void write_decoded_frames_to_yuv_file(unsigned char *buffer_pointer, __u32 bytesused, std::string filename)
+{
+ FILE *fp = fopen(filename.c_str(), "a");
+ for (__u32 i = 0; i < bytesused; i++)
+ fwrite(&buffer_pointer[i], sizeof(unsigned char), 1, fp);
+ fflush(fp);
+ fclose(fp);
+}
+
+/* Trace an output or capture buffer. */
+void trace_mem(int fd)
+{
+ int index;
+ __u32 type;
+ __u32 bytesused;
+ unsigned char *buffer_pointer;
+ long int start = get_buffer_address_trace(fd);
+
+ /* Don't trace unmapped memory. */
+ if (!start)
+ return;
+
+ buffer_pointer = (unsigned char*) start;
+ type = get_buffer_type_trace(fd);
+ index = get_buffer_index_trace(fd);
+ bytesused = get_buffer_bytesused_trace(fd);
+
+ json_object *mem_obj = json_object_new_object();
+ json_object_object_add(mem_obj, "mem_dump", json_object_new_string(buftype2s(type).c_str()));
+ json_object_object_add(mem_obj, "fd", json_object_new_int(fd));
+ json_object_object_add(mem_obj, "type", json_object_new_uint64(type));
+ json_object_object_add(mem_obj, "index", json_object_new_int(index));
+ json_object_object_add(mem_obj, "bytesused", json_object_new_uint64(bytesused));
+ json_object_object_add(mem_obj, "address", json_object_new_int64(start));
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ json_object *mem_array_obj = trace_buffer(buffer_pointer, bytesused);
+ json_object_object_add(mem_obj, "mem_array", mem_array_obj);
+ }
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+
+ if (create_yuv_file()) {
+ std::string filename = getenv("TRACE_ID");
+ filename += ".yuv";
+ write_decoded_frames_to_yuv_file(buffer_pointer, bytesused, filename);
+ json_object_object_add(mem_obj, "filename", json_object_new_string(filename.c_str()));
+ }
+
+ if (write_decoded_data_to_json()) {
+ json_object *mem_array_obj = trace_buffer(buffer_pointer, bytesused);
+ json_object_object_add(mem_obj, "mem_array_capture", mem_array_obj);
+ }
+
+ }
+
+ if (pretty_print_mem())
+ write_json_object_to_json_file(mem_obj, JSON_C_TO_STRING_PRETTY);
+ else
+ write_json_object_to_json_file(mem_obj);
+
+ json_object_put(mem_obj);
+}
+
+void trace_vidioc_querycap(void *arg, json_object *ioctl_args)
+{
+ json_object *cap_obj = json_object_new_object();
+ struct v4l2_capability *cap = static_cast<struct v4l2_capability*>(arg);
+
+ json_object_object_add(cap_obj, "driver", json_object_new_string((const char *)cap->driver));
+ json_object_object_add(cap_obj, "card", json_object_new_string((const char *)cap->card));
+ json_object_object_add(cap_obj, "bus_info", json_object_new_string((const char *)cap->bus_info));
+ json_object_object_add(cap_obj, "version", json_object_new_string(ver2s(cap->version).c_str()));
+ json_object_object_add(cap_obj, "capabilities", json_object_new_int64(cap->capabilities));
+ json_object_object_add(cap_obj, "capabilities_str",
+ json_object_new_string(capflag2s(cap->capabilities).c_str()));
+ json_object_object_add(cap_obj, "device_caps", json_object_new_int64(cap->device_caps));
+ json_object_object_add(ioctl_args, "v4l2_capability", cap_obj);
+}
+
+void trace_vidioc_enum_fmt(void *arg, json_object *ioctl_args)
+{
+ json_object *fmtdesc_obj = json_object_new_object();
+ struct v4l2_fmtdesc *fmtdesc = static_cast<struct v4l2_fmtdesc*>(arg);
+
+ json_object_object_add(fmtdesc_obj, "index", json_object_new_uint64(fmtdesc->index));
+ json_object_object_add(fmtdesc_obj, "type_str", json_object_new_string(buftype2s(fmtdesc->type).c_str()));
+ json_object_object_add(fmtdesc_obj, "type", json_object_new_uint64(fmtdesc->type));
+ json_object_object_add(fmtdesc_obj, "flags", json_object_new_uint64(fmtdesc->flags));
+ json_object_object_add(fmtdesc_obj, "description", json_object_new_string((const char *)fmtdesc->description));
+ json_object_object_add(fmtdesc_obj, "pixelformat", json_object_new_uint64(fmtdesc->pixelformat));
+ json_object_object_add(fmtdesc_obj, "mbus_code", json_object_new_uint64(fmtdesc->mbus_code));
+ json_object_object_add(ioctl_args, "v4l2_fmtdesc", fmtdesc_obj);
+}
+
+void trace_v4l2_plane_pix_format(json_object *pix_mp_obj, struct v4l2_plane_pix_format plane_fmt, int plane)
+{
+ json_object *plane_fmt_obj = json_object_new_object();
+
+ json_object_object_add(plane_fmt_obj, "sizeimage", json_object_new_uint64(plane_fmt.sizeimage));
+ json_object_object_add(plane_fmt_obj, "bytesperline", json_object_new_uint64(plane_fmt.bytesperline));
+
+ /* Create a unique key name for each plane. */
+ std::string unique_key_for_plane = "v4l2_plane_pix_format_";
+ unique_key_for_plane += std::to_string(plane);
+
+ json_object_object_add(pix_mp_obj, unique_key_for_plane.c_str(), plane_fmt_obj);
+}
+
+void trace_v4l2_pix_format_mplane(json_object *format_obj, struct v4l2_pix_format_mplane pix_mp)
+{
+ json_object *pix_mp_obj = json_object_new_object();
+
+ json_object_object_add(pix_mp_obj, "width", json_object_new_uint64(pix_mp.width));
+ json_object_object_add(pix_mp_obj, "height", json_object_new_uint64(pix_mp.height));
+ json_object_object_add(pix_mp_obj, "pixelformat", json_object_new_uint64(pix_mp.pixelformat));
+ json_object_object_add(pix_mp_obj, "pixelformat_str", json_object_new_string(fcc2s(pix_mp.pixelformat).c_str()));
+ json_object_object_add(pix_mp_obj, "field", json_object_new_uint64(pix_mp.field));
+ json_object_object_add(pix_mp_obj, "field_str", json_object_new_string(field2s(pix_mp.field).c_str()));
+ json_object_object_add(pix_mp_obj, "colorspace", json_object_new_uint64(pix_mp.colorspace));
+ json_object_object_add(pix_mp_obj, "colorspace_str", json_object_new_string(colorspace2s(pix_mp.colorspace).c_str()));
+ json_object_object_add(pix_mp_obj, "num_planes", json_object_new_int(pix_mp.num_planes));
+ for (int i = 0; i < pix_mp.num_planes; i++)
+ trace_v4l2_plane_pix_format(pix_mp_obj, pix_mp.plane_fmt[i], i);
+
+ json_object_object_add(pix_mp_obj, "flags", json_object_new_int(pix_mp.flags));
+ json_object_object_add(pix_mp_obj, "flags_str", json_object_new_string(pixflags2s(pix_mp.flags).c_str()));
+ json_object_object_add(pix_mp_obj, "ycbcr_enc", json_object_new_int(pix_mp.ycbcr_enc));
+ json_object_object_add(pix_mp_obj, "ycbcr_enc_str", json_object_new_string(ycbcr_enc2s(pix_mp.ycbcr_enc).c_str()));
+ json_object_object_add(pix_mp_obj, "quantization", json_object_new_int(pix_mp.quantization));
+ json_object_object_add(pix_mp_obj, "quantization_str", json_object_new_string(quantization2s(pix_mp.quantization).c_str()));
+ json_object_object_add(pix_mp_obj, "xfer_func", json_object_new_int(pix_mp.xfer_func));
+ json_object_object_add(pix_mp_obj, "xfer_func_str", json_object_new_string(xfer_func2s(pix_mp.xfer_func).c_str()));
+
+ json_object_object_add(format_obj, "v4l2_pix_format_mplane", pix_mp_obj);
+}
+
+void trace_v4l2_format(void *arg, json_object *ioctl_args)
+{
+ json_object *format_obj = json_object_new_object();
+ struct v4l2_format *format = static_cast<struct v4l2_format*>(arg);
+
+ json_object_object_add(format_obj, "type", json_object_new_uint64(format->type));
+ json_object_object_add(format_obj, "type_str", json_object_new_string(buftype2s(format->type).c_str()));
+
+ switch (format->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ trace_v4l2_pix_format_mplane(format_obj, format->fmt.pix_mp);
+ break;
+ case V4L2_BUF_TYPE_SDR_CAPTURE:
+ case V4L2_BUF_TYPE_SDR_OUTPUT:
+ case V4L2_BUF_TYPE_META_CAPTURE:
+ case V4L2_BUF_TYPE_META_OUTPUT:
+ break;
+ }
+ json_object_object_add(ioctl_args, "v4l2_format", format_obj);
+}
+
+void trace_v4l2_requestbuffers(void *arg, json_object *ioctl_args)
+{
+ json_object *request_buffers_obj = json_object_new_object();
+ struct v4l2_requestbuffers *request_buffers = static_cast<struct v4l2_requestbuffers*>(arg);
+
+ json_object_object_add(request_buffers_obj, "count", json_object_new_uint64(request_buffers->count));
+ json_object_object_add(request_buffers_obj, "type", json_object_new_uint64(request_buffers->type));
+ json_object_object_add(request_buffers_obj, "type_str",
+ json_object_new_string(buftype2s(request_buffers->type).c_str()));
+ json_object_object_add(request_buffers_obj, "memory", json_object_new_uint64(request_buffers->memory));
+ json_object_object_add(request_buffers_obj, "memory_str",
+ json_object_new_string(v4l2_memory2s(request_buffers->memory).c_str()));
+ json_object_object_add(request_buffers_obj, "capabilities", json_object_new_uint64(request_buffers->capabilities));
+ json_object_object_add(request_buffers_obj, "capabilities_str",
+ json_object_new_string(bufcap2s(request_buffers->capabilities).c_str()));
+ json_object_object_add(request_buffers_obj, "flags", json_object_new_int(request_buffers->flags));
+ json_object_object_add(request_buffers_obj, "flags_str",
+ json_object_new_string(request_buffers_flag2s(request_buffers->flags).c_str()));
+
+ json_object_object_add(ioctl_args, "v4l2_requestbuffers", request_buffers_obj);
+}
+
+json_object *trace_v4l2_plane(struct v4l2_plane *p, __u32 memory)
+{
+ json_object *plane_obj = json_object_new_object();
+
+ json_object_object_add(plane_obj, "bytesused", json_object_new_int64(p->bytesused));
+ json_object_object_add(plane_obj, "length", json_object_new_uint64(p->length));
+
+ json_object *m_obj = json_object_new_object();
+ if (memory == V4L2_MEMORY_MMAP)
+ json_object_object_add(m_obj, "mem_offset", json_object_new_int64(p->m.mem_offset));
+ json_object_object_add(plane_obj, "m", m_obj);
+
+ json_object_object_add(plane_obj, "data_offset", json_object_new_int64(p->data_offset));
+
+ return plane_obj;
+}
+
+void trace_v4l2_buffer(void *arg, json_object *ioctl_args)
+{
+ json_object *buf_obj = json_object_new_object();
+ struct v4l2_buffer *buf = static_cast<struct v4l2_buffer*>(arg);
+
+ json_object_object_add(buf_obj, "index", json_object_new_uint64(buf->index));
+ json_object_object_add(buf_obj, "type", json_object_new_uint64(buf->type));
+ json_object_object_add(buf_obj, "type_str",
+ json_object_new_string(buftype2s(buf->type).c_str()));
+ json_object_object_add(buf_obj, "bytesused", json_object_new_uint64(buf->bytesused));
+ json_object_object_add(buf_obj, "flags", json_object_new_uint64(buf->flags));
+ json_object_object_add(buf_obj, "flags_str",
+ json_object_new_string(bufferflags2s(buf->flags).c_str()));
+ json_object_object_add(buf_obj, "field", json_object_new_uint64(buf->field));
+
+ json_object *timestamp_obj = json_object_new_object();
+ json_object_object_add(timestamp_obj, "tv_sec", json_object_new_int64(buf->timestamp.tv_sec));
+ json_object_object_add(timestamp_obj, "tv_usec", json_object_new_int64(buf->timestamp.tv_usec));
+ json_object_object_add(buf_obj, "timestamp", timestamp_obj);
+ json_object_object_add(buf_obj, "sequence", json_object_new_uint64(buf->sequence));
+ json_object_object_add(buf_obj, "memory", json_object_new_uint64(buf->memory));
+ json_object_object_add(buf_obj, "memory_str",
+ json_object_new_string(v4l2_memory2s(buf->memory).c_str()));
+
+ json_object *m_obj = json_object_new_object();
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+
+ json_object *planes_obj = json_object_new_array();
+ /* TODO tracer only works for decoded formats with one plane e.g. V4L2_PIX_FMT_NV12 */
+ json_object_array_add(planes_obj, trace_v4l2_plane(buf->m.planes, buf->memory));
+ json_object_object_add(m_obj, "planes", planes_obj);
+ }
+ json_object_object_add(buf_obj, "m", m_obj);
+ json_object_object_add(buf_obj, "length", json_object_new_uint64(buf->length));
+
+ /* For memory-to-memory devices you can use requests only for output buffers. */
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ json_object_object_add(buf_obj, "request_fd", json_object_new_int(buf->request_fd));
+
+ json_object_object_add(ioctl_args, "v4l2_buffer", buf_obj);
+}
+
+void trace_v4l2_exportbuffer(void *arg, json_object *ioctl_args)
+{
+ json_object *exportbuffer_obj = json_object_new_object();
+ struct v4l2_exportbuffer *export_buffer = static_cast<struct v4l2_exportbuffer*>(arg);
+
+ json_object_object_add(exportbuffer_obj, "type", json_object_new_uint64(export_buffer->type));
+ json_object_object_add(exportbuffer_obj, "type_str",
+ json_object_new_string(buftype2s(export_buffer->type).c_str()));
+
+ json_object_object_add(exportbuffer_obj, "index", json_object_new_uint64(export_buffer->index));
+ json_object_object_add(exportbuffer_obj, "plane", json_object_new_uint64(export_buffer->plane));
+ json_object_object_add(exportbuffer_obj, "flags", json_object_new_uint64(export_buffer->flags));
+ json_object_object_add(exportbuffer_obj, "fd", json_object_new_int(export_buffer->fd));
+
+ json_object_object_add(ioctl_args, "v4l2_exportbuffer", exportbuffer_obj);
+}
+
+void trace_vidioc_stream(void *arg, json_object *ioctl_args)
+{
+ v4l2_buf_type buf_type = *(static_cast<v4l2_buf_type*>(arg));
+ json_object_object_add(ioctl_args, "buf_type", json_object_new_int(buf_type));
+ json_object_object_add(ioctl_args, "buf_type_str", json_object_new_string(buftype2s(buf_type).c_str()));
+}
+
+void trace_v4l2_ext_control(json_object *ext_controls_obj, struct v4l2_ext_control ctrl, __u32 control_idx)
+{
+ std::string unique_key_for_control;
+
+ json_object *ctrl_obj = json_object_new_object();
+ json_object_object_add(ctrl_obj, "id", json_object_new_uint64(ctrl.id));
+ json_object_object_add(ctrl_obj, "control_class_str", json_object_new_string(ctrlclass2s(ctrl.id).c_str()));
+ json_object_object_add(ctrl_obj, "size", json_object_new_uint64(ctrl.size));
+
+ if ((ctrl.id & V4L2_CID_CODEC_STATELESS_BASE) == V4L2_CID_CODEC_STATELESS_BASE) {
+ switch (ctrl.id) {
+ case V4L2_CID_STATELESS_VP8_FRAME:
+ trace_v4l2_ctrl_vp8_frame(ctrl.p_vp8_frame, ctrl_obj);
+ break;
+ default:
+ break;
+ }
+ }
+
+ unique_key_for_control = "v4l2_ext_control_";
+ unique_key_for_control += std::to_string(control_idx);
+
+ json_object_object_add(ext_controls_obj, unique_key_for_control.c_str(), ctrl_obj);
+}
+
+void trace_v4l2_ext_controls(void *arg, json_object *ioctl_args)
+{
+ json_object *ext_controls_obj = json_object_new_object();
+ struct v4l2_ext_controls *ext_controls = static_cast<struct v4l2_ext_controls*>(arg);
+
+ json_object_object_add(ext_controls_obj, "which", json_object_new_int64(ext_controls->which));
+ json_object_object_add(ext_controls_obj, "which_str", json_object_new_string(which2s(ext_controls->which).c_str()));
+ json_object_object_add(ext_controls_obj, "count", json_object_new_uint64(ext_controls->count));
+
+ /* error_idx is defined only if the ioctl returned an error */
+ if (errno)
+ json_object_object_add(ext_controls_obj, "error_idx", json_object_new_uint64(ext_controls->error_idx));
+
+ /* request_fd is only valid when "which" == V4L2_CTRL_WHICH_REQUEST_VAL */
+ if (ext_controls->which == V4L2_CTRL_WHICH_REQUEST_VAL)
+ json_object_object_add(ext_controls_obj, "request_fd", json_object_new_int(ext_controls->request_fd));
+
+ for (__u32 i = 0; i < ext_controls->count; i++)
+ trace_v4l2_ext_control(ext_controls_obj, ext_controls->controls[i], i);
+
+ json_object_object_add(ioctl_args, "v4l2_ext_controls", ext_controls_obj);
+}
+
+void trace_vidioc_query_ext_ctrl(void *arg, json_object *ioctl_args)
+{
+ json_object *query_ext_ctrl_obj = json_object_new_object();
+ struct v4l2_query_ext_ctrl *queryextctrl = static_cast<struct v4l2_query_ext_ctrl*>(arg);
+
+ json_object_object_add(query_ext_ctrl_obj, "id", json_object_new_uint64(queryextctrl->id));
+ json_object_object_add(query_ext_ctrl_obj, "control_class_str", json_object_new_string(ctrlclass2s(queryextctrl->id).c_str()));
+ json_object_object_add(query_ext_ctrl_obj, "type", json_object_new_uint64(queryextctrl->type));
+ json_object_object_add(query_ext_ctrl_obj, "type_str", json_object_new_string(ctrltype2s(queryextctrl->type).c_str()));
+ json_object_object_add(query_ext_ctrl_obj, "name", json_object_new_string(queryextctrl->name));
+ json_object_object_add(query_ext_ctrl_obj, "minimum", json_object_new_int64(queryextctrl->minimum));
+ json_object_object_add(query_ext_ctrl_obj, "maximum", json_object_new_int64(queryextctrl->maximum));
+ json_object_object_add(query_ext_ctrl_obj, "step", json_object_new_uint64(queryextctrl->step));
+ json_object_object_add(query_ext_ctrl_obj, "default_value", json_object_new_int64(queryextctrl->default_value));
+ json_object_object_add(query_ext_ctrl_obj, "flags", json_object_new_uint64(queryextctrl->flags));
+ json_object_object_add(query_ext_ctrl_obj, "flags_str", json_object_new_string(ctrlflags2s(queryextctrl->flags).c_str()));
+ json_object_object_add(query_ext_ctrl_obj, "elem_size", json_object_new_uint64(queryextctrl->elem_size));
+ json_object_object_add(query_ext_ctrl_obj, "elems", json_object_new_uint64(queryextctrl->elems));
+ json_object_object_add(query_ext_ctrl_obj, "nr_of_dims", json_object_new_uint64(queryextctrl->nr_of_dims));
+
+ /* __u32 dims[V4L2_CTRL_MAX_DIMS] */
+ json_object *dim_obj = json_object_new_array();
+
+ for (unsigned int i = 0; i < queryextctrl->nr_of_dims; i++)
+ json_object_array_add(dim_obj, json_object_new_uint64(queryextctrl->dims[i]));
+
+ json_object_object_add(query_ext_ctrl_obj, "dims", dim_obj);
+ json_object_object_add(ioctl_args, "v4l2_query_ext_ctrl", query_ext_ctrl_obj);
+}
+
+void trace_ioctl_media(unsigned long request, void *arg, json_object *ioctl_args)
+{
+ if (request == MEDIA_IOC_REQUEST_ALLOC) {
+ __s32 *request_fd = static_cast<__s32*>(arg);
+ json_object_object_add(ioctl_args, "request_fd", json_object_new_int(*request_fd));
+ }
+}
+
+void trace_ioctl_video(unsigned long int request, void *arg, json_object *ioctl_args, bool from_userspace)
+{
+ switch (request) {
+ case VIDIOC_QUERYCAP:
+ if (!from_userspace)
+ trace_vidioc_querycap(arg, ioctl_args);
+ break;
+ case VIDIOC_ENUM_FMT:
+ trace_vidioc_enum_fmt(arg, ioctl_args);
+ break;
+ case VIDIOC_G_FMT:
+ case VIDIOC_S_FMT:
+ trace_v4l2_format(arg, ioctl_args);
+ break;
+ case VIDIOC_REQBUFS:
+ trace_v4l2_requestbuffers(arg, ioctl_args);
+ break;
+ case VIDIOC_QUERYBUF:
+ case VIDIOC_QBUF:
+ case VIDIOC_DQBUF:
+ trace_v4l2_buffer(arg, ioctl_args);
+ break;
+ case VIDIOC_EXPBUF:
+ trace_v4l2_exportbuffer(arg, ioctl_args);
+ break;
+ case VIDIOC_STREAMON:
+ case VIDIOC_STREAMOFF:
+ if (from_userspace)
+ trace_vidioc_stream(arg, ioctl_args);
+ break;
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_S_EXT_CTRLS:
+ trace_v4l2_ext_controls(arg, ioctl_args);
+ break;
+ case VIDIOC_QUERY_EXT_CTRL:
+ trace_vidioc_query_ext_ctrl(arg, ioctl_args);
+ break;
+ default:
+ break;
+ }
+}
+
+void trace_dma_buf_ioctl_sync(void *arg, json_object *ioctl_args)
+{
+ struct dma_buf_sync *sync = static_cast<struct dma_buf_sync*>(arg);
+ json_object *sync_obj = json_object_new_object();
+ json_object_object_add(sync_obj, "flags", json_object_new_uint64(sync->flags));
+ json_object_object_add(sync_obj, "flags_str", json_object_new_string(bufsyncflag2s(sync->flags).c_str()));
+ json_object_object_add(ioctl_args, "dma_buf_sync", sync_obj);
+}
+
+std::string get_ioctl_request_str(unsigned long request)
+{
+ __u8 ioctl_type = _IOC_TYPE(request);
+ switch (ioctl_type) {
+ case 'V':
+ return ioctl2s_video(request);
+ case '|':
+ return ioctl2s_media(request);
+ case 'b':
+ if (request == DMA_BUF_IOCTL_SYNC)
+ return "DMA_BUF_IOCTL_SYNC";
+ break;
+ default:
+ break;
+ }
+ return "unknown ioctl";
+}
+
+json_object *trace_ioctl_args(int fd, unsigned long request, void *arg, bool from_userspace)
+{
+ json_object *ioctl_args = json_object_new_object();
+ __u8 ioctl_type = _IOC_TYPE(request);
+ switch (ioctl_type) {
+ case 'V':
+ trace_ioctl_video(request, arg, ioctl_args, from_userspace);
+ break;
+ case '|':
+ trace_ioctl_media(request, arg, ioctl_args);
+ break;
+ case 'b':
+ if (request == DMA_BUF_IOCTL_SYNC && from_userspace) {
+ trace_dma_buf_ioctl_sync(arg, ioctl_args);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ioctl_args;
+}
diff --git a/utils/tracer/tracer.cpp b/utils/tracer/tracer.cpp
new file mode 100644
index 00000000..1b84f85d
--- /dev/null
+++ b/utils/tracer/tracer.cpp
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <json.h>
+#include <time.h>
+#include <cstring>
+#include <string>
+
+int main(int argc, char *argv[])
+{
+ int ch;
+ char short_options[] = {'e', 'p', 'r', 'y'};
+
+ if (argc <= 1) {
+ fprintf(stderr, "usage: tracer [-e] [-p] [-r] [-y] <tracee>\n");
+ return -1;
+ }
+
+ do {
+ ch = getopt(argc, argv, short_options);
+ switch (ch){
+ case 'e':
+ setenv("TRACE_OPTION_PRETTY_PRINT_MEM", "true", 0);
+ break;
+ case 'p':
+ setenv("TRACE_OPTION_PRETTY_PRINT_ALL", "true", 0);
+ break;
+ case 'r':
+ setenv("TRACE_OPTION_DECODED_TO_JSON", "true", 0);
+ break;
+ case 'y':
+ setenv("TRACE_OPTION_CREATE_YUV_FILE", "true", 0);
+ break;
+ }
+ } while (ch != -1);
+
+ /* Get the tracee from the command line. */
+ int count = 0;
+ char *exec_array[argc];
+ while (optind < argc)
+ exec_array[count++] = argv[optind++];
+ exec_array[count] = nullptr;
+
+ /* Use a substring of the time to create a unique id for the trace. */
+ std::string trace_id = std::to_string(time(nullptr));
+ trace_id = trace_id.substr(5, trace_id.npos) + "_trace";
+
+ /* Create the trace file to hold the json-objects as a large json array. */
+ std::string trace_filename = trace_id + ".json";
+ FILE *trace_file = fopen(trace_filename.c_str(), "w");
+ fputs("[\n", trace_file);
+ fflush(trace_file);
+
+ setenv("TRACE_ID", trace_id.c_str(), 0);
+ setenv("LD_PRELOAD", ".libs/libtracer.so", 0);
+
+ if (fork() == 0) {
+ execvpe(exec_array[0], exec_array, environ);
+ perror("Could not execute tracee");
+ return -1;
+ }
+
+ int tracee_result;
+ wait(&tracee_result);
+ unsetenv("TRACE_ID");
+ unsetenv("LD_PRELOAD");
+
+ if (WEXITSTATUS(tracee_result)) {
+ fprintf(stderr, "Trace error: %s\n", trace_filename.c_str());
+ exit(EXIT_FAILURE);
+ }
+
+ /* Close the json-array and the trace file. */
+ trace_file = fopen(trace_filename.c_str(), "r+");
+ fseek(trace_file, 0L, SEEK_END);
+ fseek(trace_file, (ftell(trace_file) - 2), SEEK_SET);
+ std::string end = "\n]\n";
+ fwrite(end.c_str(), sizeof(char), end.length(), trace_file);
+ fclose(trace_file);
+
+ fprintf(stderr, "Trace complete: %s\n", trace_filename.c_str());
+ return 0;
+}
--
2.37.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [RFC 2/2] utils: add stateless retracer utility
2022-08-20 0:50 [RFC 0/2] v4l2 stateless tracer/retracer utilities Deborah Brouwer
2022-08-20 0:50 ` [RFC 1/2] utils: add stateless tracer utility Deborah Brouwer
@ 2022-08-20 0:50 ` Deborah Brouwer
2022-08-26 19:05 ` [RFC 0/2] v4l2 stateless tracer/retracer utilities Nicolas Dufresne
2 siblings, 0 replies; 5+ messages in thread
From: Deborah Brouwer @ 2022-08-20 0:50 UTC (permalink / raw)
To: linux-media
Cc: daniel.almeida, nfraprado, nicolas.dufresne, hverkuil-cisco,
Deborah Brouwer
The retracer utility reads and replays a json-formatted file produced by
the stateless tracer utility. The retracer runs independently from other
userspace applications and so allows a driver to be tested in different
userspace environments. If the trace file is edited to send specific
errors to a driver, the retracer can be used to test a driver's error-
handling abilities.
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
---
utils/tracer/Makefile.am | 6 +-
utils/tracer/retrace-helper.cpp | 141 ++++
utils/tracer/retrace-helper.h | 18 +
utils/tracer/retrace-vp8.cpp | 288 ++++++++
utils/tracer/retrace-vp8.h | 11 +
utils/tracer/retracer.cpp | 1090 +++++++++++++++++++++++++++++++
utils/tracer/retracer.h | 24 +
7 files changed, 1577 insertions(+), 1 deletion(-)
create mode 100755 utils/tracer/retrace-helper.cpp
create mode 100644 utils/tracer/retrace-helper.h
create mode 100755 utils/tracer/retrace-vp8.cpp
create mode 100644 utils/tracer/retrace-vp8.h
create mode 100755 utils/tracer/retracer.cpp
create mode 100644 utils/tracer/retracer.h
diff --git a/utils/tracer/Makefile.am b/utils/tracer/Makefile.am
index f5579198..6cf5f000 100644
--- a/utils/tracer/Makefile.am
+++ b/utils/tracer/Makefile.am
@@ -10,10 +10,14 @@ libtracer_la_CPPFLAGS = -I$(top_srcdir)/utils/common $(JSONC_CFLAGS)
libtracer_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -ldl $(JSONC_LIBS)
libtracer_la_LIBTOOLFLAGS = --tag=disable-static
-bin_PROGRAMS = tracer
+bin_PROGRAMS = tracer retracer
tracer_SOURCES = tracer.cpp
tracer_CPPFLAGS = -I$(top_srcdir)/utils/common $(JSONC_CFLAGS)
tracer_LDFLAGS = -lrt -lpthread $(JSONC_LIBS)
+retracer_SOURCES = retracer.cpp retrace-helper.cpp retrace-vp8.cpp trace-info.cpp ../common/v4l2-info.cpp
+retracer_CPPFLAGS = -I$(top_srcdir)/utils/common $(JSONC_CFLAGS)
+retracer_LDFLAGS = -lrt -lpthread $(JSONC_LIBS)
+
endif
diff --git a/utils/tracer/retrace-helper.cpp b/utils/tracer/retrace-helper.cpp
new file mode 100755
index 00000000..491bf42d
--- /dev/null
+++ b/utils/tracer/retrace-helper.cpp
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "retracer.h"
+
+struct buffer_retrace {
+ int fd;
+ long int address_trace;
+ long int address_retrace;
+};
+
+struct retrace_context {
+ pthread_mutex_t lock;
+
+ /* Key is a file descriptor from the trace, value is the corresponding fd in the retrace context. */
+ std::unordered_map<int, int> retrace_fds;
+
+ /* List of output and capture buffers being retraced. */
+ std::list<struct buffer_retrace> buffers;
+};
+
+static struct retrace_context ctx_retrace = {
+ .lock = PTHREAD_MUTEX_INITIALIZER
+};
+
+/* Take a buffer's file descriptor and create a new buffer entry in retrace context. */
+void add_buffer_retrace(int fd)
+{
+ struct buffer_retrace buf;
+ memset(&buf, 0, sizeof(buffer_retrace));
+ buf.fd = fd;
+ pthread_mutex_lock(&ctx_retrace.lock);
+ ctx_retrace.buffers.push_front(buf);
+ pthread_mutex_unlock(&ctx_retrace.lock);
+}
+
+/*
+ * Use the buffer file descriptor to get a buffer entry from the retrace context.
+ * Add the buffer's memory address from both the trace and retrace context to the buffer entry.
+ */
+void set_buffer_address_retrace(int fd, long address_trace, long address_retrace)
+{
+ pthread_mutex_lock(&ctx_retrace.lock);
+ for (auto it = ctx_retrace.buffers.begin(); it != ctx_retrace.buffers.end(); ++it) {
+ if (it->fd == fd) {
+ it->address_trace = address_trace;
+ it->address_retrace = address_retrace;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&ctx_retrace.lock);
+}
+
+/* Take an address from the trace and return the corresponding address in the retrace context. */
+long int get_buffer_address_retrace(long address_trace)
+{
+ long int address_retrace = 0;
+
+ pthread_mutex_lock(&ctx_retrace.lock);
+ for (auto it = ctx_retrace.buffers.cbegin(); it != ctx_retrace.buffers.cend(); ++it) {
+ if (it->address_trace == address_trace) {
+ address_retrace = it->address_retrace;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&ctx_retrace.lock);
+
+ return address_retrace;
+}
+
+void print_buffers_retrace(void)
+{
+ pthread_mutex_lock(&ctx_retrace.lock);
+ for (auto it = ctx_retrace.buffers.cbegin(); it != ctx_retrace.buffers.cend(); ++it) {
+ fprintf(stderr, "fd: %d, address_trace:%ld, address_retrace:%ld\n",
+ it->fd, it->address_trace, it->address_retrace);
+ }
+ pthread_mutex_unlock(&ctx_retrace.lock);
+}
+
+/*
+ * Create a new file descriptor entry in retrace context.
+ * Add both the fd from the trace context and the corresponding fd from the retrace context.
+ */
+void add_fd(int fd_trace, int fd_retrace)
+{
+ std::pair<int, int> new_pair;
+ new_pair = std::make_pair(fd_trace, fd_retrace);
+ pthread_mutex_lock(&ctx_retrace.lock);
+ ctx_retrace.retrace_fds.insert(new_pair);
+ pthread_mutex_unlock(&ctx_retrace.lock);
+}
+
+/* Take a file descriptor from the trace and return the corresponding fd in the retrace context. */
+int get_fd(int fd_trace)
+{
+ int fd_retrace = 0;
+ std::unordered_map<int, int>::const_iterator it;
+
+ pthread_mutex_lock(&ctx_retrace.lock);
+ it = ctx_retrace.retrace_fds.find(fd_trace);
+ if (it != ctx_retrace.retrace_fds.end())
+ fd_retrace = it->second;
+ pthread_mutex_unlock(&ctx_retrace.lock);
+
+ return fd_retrace;
+}
+
+/* Using a file descriptor from the trace, find and remove an fd entry from the retrace context.*/
+void remove_fd(int fd_trace)
+{
+ pthread_mutex_lock(&ctx_retrace.lock);
+ ctx_retrace.retrace_fds.erase(fd_trace);
+ pthread_mutex_unlock(&ctx_retrace.lock);
+}
+
+void print_fds(void)
+{
+ pthread_mutex_lock(&ctx_retrace.lock);
+ if (ctx_retrace.retrace_fds.empty())
+ fprintf(stderr, "all devices closed\n");
+ for ( auto it = ctx_retrace.retrace_fds.cbegin(); it != ctx_retrace.retrace_fds.cend(); ++it )
+ fprintf(stderr, "fd_trace: %d, fd_retrace: %d\n", it->first, it->second);
+ pthread_mutex_unlock(&ctx_retrace.lock);
+}
+
+void print_context(void)
+{
+ print_fds();
+ print_buffers_retrace();
+ fprintf(stderr, "\n");
+}
+
+int retrace_v4l2_ext_control_value(json_object *ctrl_obj)
+{
+ json_object *value_obj;
+ json_object_object_get_ex(ctrl_obj, "value", &value_obj);
+ return json_object_get_int(value_obj);
+}
diff --git a/utils/tracer/retrace-helper.h b/utils/tracer/retrace-helper.h
new file mode 100644
index 00000000..3634d2a7
--- /dev/null
+++ b/utils/tracer/retrace-helper.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#ifndef RETRACE_HELPER_H
+#define RETRACE_HELPER_H
+
+void add_buffer_retrace(int fd);
+void set_buffer_address_retrace(int fd, long address_trace, long address_retrace);
+long int get_buffer_address_retrace(long address_trace);
+int get_fd(int fd_trace);
+void add_fd(int fd_trace, int fd_retrace);
+void remove_fd(int fd_trace);
+void print_context(void);
+int retrace_v4l2_ext_control_value(json_object *ctrl_obj);
+
+#endif
diff --git a/utils/tracer/retrace-vp8.cpp b/utils/tracer/retrace-vp8.cpp
new file mode 100755
index 00000000..5b893f19
--- /dev/null
+++ b/utils/tracer/retrace-vp8.cpp
@@ -0,0 +1,288 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "retracer.h"
+
+struct v4l2_vp8_loop_filter retrace_v4l2_vp8_loop_filter(json_object *lf_obj)
+{
+ struct v4l2_vp8_loop_filter lf;
+ memset(&lf, 0, sizeof(lf));
+
+ /* __s8 ref_frm_delta[4] */
+ json_object *ref_frm_delta_obj;
+ json_object_object_get_ex(lf_obj, "ref_frm_delta", &ref_frm_delta_obj);
+ for (size_t i = 0; i < ARRAY_SIZE(lf.ref_frm_delta); i++)
+ lf.ref_frm_delta[i] = (__s8) json_object_get_int(json_object_array_get_idx(ref_frm_delta_obj, i));
+
+ /* __s8 mb_mode_delta[4] */
+ json_object *mb_mode_delta_obj;
+ json_object_object_get_ex(lf_obj, "mb_mode_delta", &mb_mode_delta_obj);
+ for (size_t i = 0; i < ARRAY_SIZE(lf.mb_mode_delta); i++)
+ lf.mb_mode_delta[i] = (__s8) json_object_get_int(json_object_array_get_idx(mb_mode_delta_obj, i));
+
+ json_object *sharpness_level_obj;
+ json_object_object_get_ex(lf_obj, "sharpness_level", &sharpness_level_obj);
+ lf.sharpness_level = json_object_get_int(sharpness_level_obj);
+
+ json_object *level_obj;
+ json_object_object_get_ex(lf_obj, "level", &level_obj);
+ lf.level = json_object_get_int(level_obj);
+
+ json_object *padding_obj;
+ json_object_object_get_ex(lf_obj, "padding", &padding_obj);
+ lf.padding = json_object_get_int(padding_obj);
+
+ json_object *flags_obj;
+ json_object_object_get_ex(lf_obj, "flags", &flags_obj);
+ lf.flags = json_object_get_int(flags_obj);
+
+ return lf;
+}
+
+struct v4l2_vp8_segment retrace_v4l2_vp8_segment(json_object *segment_obj)
+{
+ struct v4l2_vp8_segment segment;
+ memset(&segment, 0, sizeof(v4l2_vp8_segment));
+
+ /* __s8 quant_update[4] */
+ json_object *quant_update_obj;
+ json_object_object_get_ex(segment_obj, "quant_update", &quant_update_obj);
+ for (size_t i = 0; i < ARRAY_SIZE(segment.quant_update); i++)
+ segment.quant_update[i] = (__s8) json_object_get_int(json_object_array_get_idx(quant_update_obj, i));
+
+ /* __s8 lf_update[4] */
+ json_object *lf_update_obj;
+ json_object_object_get_ex(segment_obj, "lf_update", &lf_update_obj);
+ for (size_t i = 0; i < ARRAY_SIZE(segment.lf_update); i++)
+ segment.lf_update[i] = (__s8) json_object_get_int(json_object_array_get_idx(lf_update_obj, i));
+
+ /* __u8 segment_probs[3] */
+ json_object *segment_probs_obj;
+ json_object_object_get_ex(segment_obj, "segment_probs", &segment_probs_obj);
+ for (size_t i = 0; i < ARRAY_SIZE(segment.segment_probs); i++)
+ segment.segment_probs[i] = json_object_get_int(json_object_array_get_idx(segment_probs_obj, i));
+
+ json_object *padding_obj;
+ json_object_object_get_ex(segment_obj, "padding", &padding_obj);
+ segment.padding = json_object_get_int(padding_obj);
+
+ json_object *flags_obj;
+ json_object_object_get_ex(segment_obj, "flags", &flags_obj);
+ segment.flags = json_object_get_int(flags_obj);
+
+ return segment;
+}
+
+struct v4l2_vp8_quantization retrace_v4l2_vp8_quantization(json_object *quant_obj)
+{
+ struct v4l2_vp8_quantization quant;
+ memset(&quant, 0, sizeof(quant));
+
+ json_object *y_ac_qi_obj;
+ json_object_object_get_ex(quant_obj, "y_ac_qi", &y_ac_qi_obj);
+ quant.y_ac_qi = json_object_get_int(y_ac_qi_obj);
+
+ json_object *y_dc_delta_obj;
+ json_object_object_get_ex(quant_obj, "y_dc_delta", &y_dc_delta_obj);
+ quant.y_dc_delta = (__s8) json_object_get_int(y_dc_delta_obj);
+
+ json_object *y2_dc_delta_obj;
+ json_object_object_get_ex(quant_obj, "y2_dc_delta", &y2_dc_delta_obj);
+ quant.y2_dc_delta = (__s8) json_object_get_int(y2_dc_delta_obj);
+
+ json_object *y2_ac_delta_obj;
+ json_object_object_get_ex(quant_obj, "y2_ac_delta", &y2_ac_delta_obj);
+ quant.y2_ac_delta = (__s8) json_object_get_int(y2_ac_delta_obj);
+
+ json_object *uv_dc_delta_obj;
+ json_object_object_get_ex(quant_obj, "uv_dc_delta", &uv_dc_delta_obj);
+ quant.uv_dc_delta = (__s8) json_object_get_int(uv_dc_delta_obj);
+
+ json_object *uv_ac_delta_obj;
+ json_object_object_get_ex(quant_obj, "uv_ac_delta", &uv_ac_delta_obj);
+ quant.uv_ac_delta = (__s8) json_object_get_int(uv_ac_delta_obj);
+
+ json_object *padding_obj;
+ json_object_object_get_ex(quant_obj, "padding", &padding_obj);
+ quant.padding = json_object_get_int(padding_obj);
+
+ return quant;
+}
+
+struct v4l2_vp8_entropy retrace_v4l2_vp8_entropy(json_object *entropy_obj)
+{
+ struct v4l2_vp8_entropy entropy;
+ memset(&entropy, 0, sizeof(entropy));
+
+ int count = 0;
+
+ /* __u8 coeff_probs[4][8][3][V4L2_VP8_COEFF_PROB_CNT] */
+ json_object *coeff_probs_obj;
+ json_object_object_get_ex(entropy_obj, "coeff_probs", &coeff_probs_obj);
+
+ for (size_t i = 0; i < ARRAY_SIZE(entropy.coeff_probs); i++)
+ for (size_t j = 0; j < ARRAY_SIZE(entropy.coeff_probs[0]); j++)
+ for (size_t k = 0; k < ARRAY_SIZE(entropy.coeff_probs[0][0]); k++)
+ for (size_t l = 0; l < V4L2_VP8_COEFF_PROB_CNT; l++)
+ entropy.coeff_probs[i][j][k][l] = json_object_get_int(json_object_array_get_idx(coeff_probs_obj, count++));
+
+ /* __u8 y_mode_probs[4] */
+ json_object *y_mode_probs_obj;
+ json_object_object_get_ex(entropy_obj, "y_mode_probs", &y_mode_probs_obj);
+ for (size_t i = 0; i < ARRAY_SIZE(entropy.y_mode_probs); i++)
+ entropy.y_mode_probs[i] = json_object_get_int(json_object_array_get_idx(y_mode_probs_obj, i));
+
+ /* __u8 uv_mode_probs[3] */
+ json_object *uv_mode_probs_obj;
+ json_object_object_get_ex(entropy_obj, "uv_mode_probs", &uv_mode_probs_obj);
+ for (size_t i = 0; i < ARRAY_SIZE(entropy.uv_mode_probs); i++)
+ entropy.uv_mode_probs[i] = json_object_get_int(json_object_array_get_idx(uv_mode_probs_obj, i));
+
+ /* __u8 mv_probs[2][V4L2_VP8_MV_PROB_CNT] */
+ count = 0;
+ json_object *mv_probs_obj;
+ json_object_object_get_ex(entropy_obj, "mv_probs", &mv_probs_obj);
+ for (size_t i = 0; i < ARRAY_SIZE(entropy.mv_probs); i++)
+ for (size_t j = 0; j < V4L2_VP8_MV_PROB_CNT; j++)
+ entropy.mv_probs[i][j] = json_object_get_int(json_object_array_get_idx(mv_probs_obj, count++));
+
+ /* __u8 padding[3] */
+ json_object *padding_obj;
+ json_object_object_get_ex(entropy_obj, "padding", &padding_obj);
+ for (size_t i = 0; i < ARRAY_SIZE(entropy.padding); i++)
+ entropy.padding[i] = json_object_get_int(json_object_array_get_idx(padding_obj, i));
+
+ return entropy;
+}
+
+struct v4l2_vp8_entropy_coder_state retrace_v4l2_vp8_entropy_coder_state(json_object *coder_state_obj)
+{
+ struct v4l2_vp8_entropy_coder_state coder_state;
+ memset(&coder_state, 0, sizeof(coder_state));
+
+ json_object *range_obj;
+ json_object_object_get_ex(coder_state_obj, "range", &range_obj);
+ coder_state.range = json_object_get_int(range_obj);
+
+ json_object *value_obj;
+ json_object_object_get_ex(coder_state_obj, "value", &value_obj);
+ coder_state.value = json_object_get_int(value_obj);
+
+ json_object *bit_count_obj;
+ json_object_object_get_ex(coder_state_obj, "bit_count", &bit_count_obj);
+ coder_state.bit_count = json_object_get_int(bit_count_obj);
+
+ json_object *padding_obj;
+ json_object_object_get_ex(coder_state_obj, "padding", &padding_obj);
+ coder_state.padding = json_object_get_int(padding_obj);
+
+ return coder_state;
+}
+
+struct v4l2_ctrl_vp8_frame *retrace_v4l2_ctrl_vp8_frame_pointer(json_object *ctrl_obj)
+{
+ struct v4l2_ctrl_vp8_frame *vp8_frame_pointer = (struct v4l2_ctrl_vp8_frame *) malloc(sizeof(v4l2_ctrl_vp8_frame));
+ memset(vp8_frame_pointer, 0, sizeof(v4l2_ctrl_vp8_frame));
+
+ json_object *vp8_frame_obj;
+ json_object_object_get_ex(ctrl_obj, "v4l2_ctrl_vp8_frame", &vp8_frame_obj);
+
+ /* struct v4l2_vp8_segment segment */
+ json_object *segment_obj;
+ json_object_object_get_ex(vp8_frame_obj, "segment", &segment_obj);
+ vp8_frame_pointer->segment = retrace_v4l2_vp8_segment(segment_obj);
+
+ /* struct v4l2_vp8_loop_filter lf */
+ json_object *lf_obj;
+ json_object_object_get_ex(vp8_frame_obj, "lf", &lf_obj);
+ vp8_frame_pointer->lf = retrace_v4l2_vp8_loop_filter(lf_obj);
+
+ /* struct v4l2_vp8_quantization quant */
+ json_object *quant_obj;
+ json_object_object_get_ex(vp8_frame_obj, "quant", &quant_obj);
+ vp8_frame_pointer->quant = retrace_v4l2_vp8_quantization(quant_obj);
+
+ /* struct v4l2_vp8_entropy entropy */
+ json_object *entropy_obj;
+ json_object_object_get_ex(vp8_frame_obj, "entropy", &entropy_obj);
+ vp8_frame_pointer->entropy = retrace_v4l2_vp8_entropy(entropy_obj);
+
+ /* struct v4l2_vp8_entropy_coder_state coder_state */
+ json_object *coder_state_obj;
+ json_object_object_get_ex(vp8_frame_obj, "coder_state", &coder_state_obj);
+ vp8_frame_pointer->coder_state = retrace_v4l2_vp8_entropy_coder_state(coder_state_obj);
+
+ json_object *width_obj;
+ json_object_object_get_ex(vp8_frame_obj, "width", &width_obj);
+ vp8_frame_pointer->width = json_object_get_int(width_obj);
+
+ json_object *height_obj;
+ json_object_object_get_ex(vp8_frame_obj, "height", &height_obj);
+ vp8_frame_pointer->height = json_object_get_int(height_obj);
+
+ json_object *horizontal_scale_obj;
+ json_object_object_get_ex(vp8_frame_obj, "horizontal_scale", &horizontal_scale_obj);
+ vp8_frame_pointer->horizontal_scale = json_object_get_int(horizontal_scale_obj);
+
+ json_object *vertical_scale_obj;
+ json_object_object_get_ex(vp8_frame_obj, "vertical_scale", &vertical_scale_obj);
+ vp8_frame_pointer->vertical_scale = json_object_get_int(vertical_scale_obj);
+
+ json_object *version_obj;
+ json_object_object_get_ex(vp8_frame_obj, "version", &version_obj);
+ vp8_frame_pointer->version = json_object_get_int(version_obj);
+
+ json_object *prob_skip_false_obj;
+ json_object_object_get_ex(vp8_frame_obj, "prob_skip_false", &prob_skip_false_obj);
+ vp8_frame_pointer->prob_skip_false = json_object_get_int(prob_skip_false_obj);
+
+ json_object *prob_intra_obj;
+ json_object_object_get_ex(vp8_frame_obj, "prob_intra", &prob_intra_obj);
+ vp8_frame_pointer->prob_intra = json_object_get_int(prob_intra_obj);
+
+ json_object *prob_last_obj;
+ json_object_object_get_ex(vp8_frame_obj, "prob_last", &prob_last_obj);
+ vp8_frame_pointer->prob_last = json_object_get_int(prob_last_obj);
+
+ json_object *prob_gf_obj;
+ json_object_object_get_ex(vp8_frame_obj, "prob_gf", &prob_gf_obj);
+ vp8_frame_pointer->prob_gf = json_object_get_int(prob_gf_obj);
+
+ json_object *num_dct_parts_obj;
+ json_object_object_get_ex(vp8_frame_obj, "num_dct_parts", &num_dct_parts_obj);
+ vp8_frame_pointer->num_dct_parts = json_object_get_int(num_dct_parts_obj);
+
+ json_object *first_part_size_obj;
+ json_object_object_get_ex(vp8_frame_obj, "first_part_size", &first_part_size_obj);
+ vp8_frame_pointer->first_part_size = json_object_get_int(first_part_size_obj);
+
+ json_object *first_part_header_bits_obj;
+ json_object_object_get_ex(vp8_frame_obj, "first_part_header_bits", &first_part_header_bits_obj);
+ vp8_frame_pointer->first_part_header_bits = json_object_get_int(first_part_header_bits_obj);
+
+ /* __u32 dct_part_sizes[8] */
+ json_object *dct_part_sizes_obj;
+ json_object_object_get_ex(vp8_frame_obj, "dct_part_sizes", &dct_part_sizes_obj);
+ for (int i = 0; i < 8; i++)
+ vp8_frame_pointer->dct_part_sizes[i] = json_object_get_int(json_object_array_get_idx(dct_part_sizes_obj, i));
+
+ json_object *last_frame_ts_obj;
+ json_object_object_get_ex(vp8_frame_obj, "last_frame_ts", &last_frame_ts_obj);
+ vp8_frame_pointer->last_frame_ts = json_object_get_int(last_frame_ts_obj);
+
+ json_object *golden_frame_ts_obj;
+ json_object_object_get_ex(vp8_frame_obj, "golden_frame_ts", &golden_frame_ts_obj);
+ vp8_frame_pointer->golden_frame_ts = json_object_get_int(golden_frame_ts_obj);
+
+ json_object *alt_frame_ts_obj;
+ json_object_object_get_ex(vp8_frame_obj, "alt_frame_ts", &alt_frame_ts_obj);
+ vp8_frame_pointer->alt_frame_ts = json_object_get_int(alt_frame_ts_obj);
+
+ json_object *flags_obj;
+ json_object_object_get_ex(vp8_frame_obj, "flags", &flags_obj);
+ vp8_frame_pointer->flags = json_object_get_int(flags_obj);
+
+ return vp8_frame_pointer;
+}
\ No newline at end of file
diff --git a/utils/tracer/retrace-vp8.h b/utils/tracer/retrace-vp8.h
new file mode 100644
index 00000000..a5bea25e
--- /dev/null
+++ b/utils/tracer/retrace-vp8.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#ifndef RETRACE_VP8_H
+#define RETRACE_VP8_H
+
+struct v4l2_ctrl_vp8_frame *retrace_v4l2_ctrl_vp8_frame_pointer(json_object *ctrl_obj);
+
+#endif
diff --git a/utils/tracer/retracer.cpp b/utils/tracer/retracer.cpp
new file mode 100755
index 00000000..049761f4
--- /dev/null
+++ b/utils/tracer/retracer.cpp
@@ -0,0 +1,1090 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "retracer.h"
+
+bool debug = false;
+std::string retrace_filename;
+std::string dev_path_video;
+std::string dev_path_media;
+
+void retrace_mmap64(json_object *mmap_obj)
+{
+ json_object *mmap64_args_obj;
+ json_object_object_get_ex(mmap_obj, "mmap64_args", &mmap64_args_obj);
+
+ json_object *len_obj;
+ json_object_object_get_ex(mmap64_args_obj, "len", &len_obj);
+ size_t len = (size_t) json_object_get_int(len_obj);
+
+ json_object *prot_obj;
+ json_object_object_get_ex(mmap64_args_obj, "prot", &prot_obj);
+ int prot = json_object_get_int(prot_obj);
+
+ json_object *flags_obj;
+ json_object_object_get_ex(mmap64_args_obj, "flags", &flags_obj);
+ int flags = json_object_get_int(flags_obj);
+
+ json_object *fildes_obj;
+ json_object_object_get_ex(mmap64_args_obj, "fildes", &fildes_obj);
+ int buf_fd_trace = json_object_get_int(fildes_obj);
+
+ json_object *off_obj;
+ json_object_object_get_ex(mmap64_args_obj, "off", &off_obj);
+ off_t off = (off_t) json_object_get_int(off_obj);
+
+ /* Only retrace mmap64 calls that map a buffer. */
+ int buf_fd_retrace = get_fd(buf_fd_trace);
+ if (buf_fd_retrace < 0)
+ return;
+
+ void *buf_address_retrace_pointer = mmap64(0, len, prot, flags, buf_fd_retrace, off);
+ long int buf_address_retrace = (long int) buf_address_retrace_pointer;
+
+ if (buf_address_retrace_pointer == MAP_FAILED) {
+ perror("mmap64");
+ print_context();
+ exit(EXIT_FAILURE);
+ }
+
+ /* Store the original trace address so that it can be matched with the munmap address later. */
+ json_object *buffer_address_obj;
+ json_object_object_get_ex(mmap_obj, "buffer_address", &buffer_address_obj);
+ long int buf_address_trace = json_object_get_int64(buffer_address_obj);
+ set_buffer_address_retrace(buf_fd_retrace, buf_address_trace, buf_address_retrace);
+
+ if (debug || (errno != 0)) {
+ perror("mmap64 ");
+ fprintf(stderr, "fd: %d\n", buf_fd_retrace);
+ print_context();
+ }
+}
+
+void retrace_munmap(json_object *syscall_obj)
+{
+ json_object *munmap_args_obj;
+ json_object_object_get_ex(syscall_obj, "munmap_args", &munmap_args_obj);
+
+ json_object *start_obj;
+ json_object_object_get_ex(munmap_args_obj, "start", &start_obj);
+ long int start = json_object_get_int64(start_obj);
+
+ json_object *length_obj;
+ json_object_object_get_ex(munmap_args_obj, "length", &length_obj);
+ size_t length = (size_t) json_object_get_int(length_obj);
+
+ long int buffer_address_retrace = get_buffer_address_retrace(start);
+
+ if (buffer_address_retrace < 0)
+ return;
+
+ munmap((void *)buffer_address_retrace, length);
+
+ if (debug || (errno != 0)) {
+ perror("munmap");
+ fprintf(stderr, "unmapped: %ld\n", buffer_address_retrace);
+ fprintf(stderr, "\n");
+ }
+}
+
+void retrace_open64(json_object *jobj)
+{
+ json_object *fd_trace_obj;
+ json_object_object_get_ex(jobj, "fd", &fd_trace_obj);
+ int fd_trace = json_object_get_int(fd_trace_obj);
+
+ json_object *open_args_obj;
+ json_object_object_get_ex(jobj, "open_args", &open_args_obj);
+
+ json_object *path_obj;
+ json_object_object_get_ex(open_args_obj, "path", &path_obj);
+ std::string path = json_object_get_string(path_obj);
+
+ json_object *oflag_obj;
+ json_object_object_get_ex(open_args_obj, "oflag", &oflag_obj);
+ int oflag = json_object_get_int(oflag_obj);
+
+ json_object *mode_obj;
+ json_object_object_get_ex(open_args_obj, "mode", &mode_obj);
+ int mode = json_object_get_int(mode_obj);
+
+ /* If a device is provided on the command line, use it instead of the device from the trace file. */
+ if ((path.find("video") != path.npos) && !dev_path_video.empty())
+ path = dev_path_video;
+
+ if ((path.find("media") != path.npos) && !dev_path_media.empty())
+ path = dev_path_media;
+
+ int fd_retrace = open64(path.c_str(), oflag, mode);
+
+ if (fd_retrace < 0) {
+ fprintf(stderr, "Cannot open: %s\n", path.c_str());
+ exit(fd_retrace);
+ }
+
+ add_fd(fd_trace, fd_retrace);
+
+ if (debug || (errno != 0)) {
+ perror("open64");
+ fprintf(stderr, "opened: %s \n", path.c_str());
+ print_context();
+ }
+}
+
+void retrace_close(json_object *jobj)
+{
+ json_object *fd_trace_obj;
+ json_object_object_get_ex(jobj, "fd", &fd_trace_obj);
+ int fd_retrace = get_fd(json_object_get_int(fd_trace_obj));
+
+ /* Only close devices that were opened in the retrace context. */
+ if (fd_retrace) {
+ close(fd_retrace);
+ remove_fd(json_object_get_int(fd_trace_obj));
+
+ if (debug || (errno != 0)) {
+ perror("close");
+ fprintf(stderr, "fd: %d\n\n", fd_retrace);
+ print_context();
+ }
+ }
+}
+
+struct v4l2_requestbuffers retrace_v4l2_requestbuffers(json_object *ioctl_args)
+{
+ struct v4l2_requestbuffers request_buffers;
+ memset(&request_buffers, 0, sizeof(v4l2_requestbuffers));
+
+ json_object *requestbuffers_obj;
+ json_object_object_get_ex(ioctl_args, "v4l2_requestbuffers", &requestbuffers_obj);
+
+ json_object *count_obj;
+ json_object_object_get_ex(requestbuffers_obj, "count", &count_obj);
+ request_buffers.count = json_object_get_int(count_obj);
+
+ json_object *type_obj;
+ json_object_object_get_ex(requestbuffers_obj, "type", &type_obj);
+ request_buffers.type = json_object_get_int(type_obj);
+
+ json_object *memory_obj;
+ json_object_object_get_ex(requestbuffers_obj, "memory", &memory_obj);
+ request_buffers.memory = json_object_get_int(memory_obj);
+
+ json_object *capabilities_obj;
+ json_object_object_get_ex(requestbuffers_obj, "capabilities", &capabilities_obj);
+ request_buffers.capabilities = json_object_get_int(capabilities_obj);
+
+ json_object *flags_obj;
+ json_object_object_get_ex(requestbuffers_obj, "flags", &flags_obj);
+ request_buffers.flags = json_object_get_int(flags_obj);
+
+ return request_buffers;
+}
+
+void retrace_vidioc_reqbufs(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_requestbuffers request_buffers = retrace_v4l2_requestbuffers(ioctl_args);
+
+ ioctl(fd_retrace, VIDIOC_REQBUFS, &request_buffers);
+
+ if (debug || (errno != 0)) {
+ perror("VIDIOC_REQBUFS");
+ fprintf(stderr, "type: %s, request_buffers.count: %d\n",
+ buftype2s(request_buffers.type).c_str(), request_buffers.count);
+ print_context();
+ }
+}
+
+struct v4l2_plane *retrace_v4l2_plane(json_object *plane_obj, __u32 memory)
+{
+ struct v4l2_plane *pl = (struct v4l2_plane *) malloc(sizeof(v4l2_plane));
+
+ json_object *bytesused_obj;
+ json_object_object_get_ex(plane_obj, "bytesused", &bytesused_obj);
+ pl->bytesused = (__u32) json_object_get_int(bytesused_obj);
+
+ json_object *length_obj;
+ json_object_object_get_ex(plane_obj, "length", &length_obj);
+ pl->length = (__u32) json_object_get_int(length_obj);
+
+ json_object *m_obj;
+ json_object_object_get_ex(plane_obj, "m", &m_obj);
+ /* https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/buffer.html#c.V4L.v4l2_plane */
+ if (memory == V4L2_MEMORY_MMAP) {
+ json_object *mem_offset_obj;
+ json_object_object_get_ex(m_obj, "mem_offset", &mem_offset_obj);
+ pl->m.mem_offset = (__u32) json_object_get_int(mem_offset_obj);
+ }
+
+ json_object *data_offset_obj;
+ json_object_object_get_ex(plane_obj, "data_offset", &data_offset_obj);
+ pl->data_offset = (__u32) json_object_get_int(data_offset_obj);
+
+ return pl;
+}
+
+struct v4l2_buffer retrace_v4l2_buffer(json_object *ioctl_args)
+{
+ struct v4l2_buffer buf;
+ memset(&buf, 0, sizeof(v4l2_buffer));
+
+ json_object *buf_obj;
+ json_object_object_get_ex(ioctl_args, "v4l2_buffer", &buf_obj);
+
+ /* Index of the buffer. */
+ json_object *index_obj;
+ json_object_object_get_ex(buf_obj, "index", &index_obj);
+ buf.index = (__u32) json_object_get_int(index_obj);
+
+ /* Type of the buffer. */
+ json_object *type_obj;
+ json_object_object_get_ex(buf_obj, "type", &type_obj);
+ buf.type = (__u32) json_object_get_int(type_obj);
+
+ /* For multiplanar formats this bytesused field is ignored and the planes pointer is used instead. */
+ json_object *bytesused_obj;
+ json_object_object_get_ex(buf_obj, "bytesused", &bytesused_obj);
+ buf.bytesused = (__u32) json_object_get_int(bytesused_obj);
+
+ /* Flags set by application and modified by driver. */
+ json_object *flags_obj;
+ json_object_object_get_ex(buf_obj, "flags", &flags_obj);
+ buf.flags = (__u32) json_object_get_int(flags_obj);
+
+ /* Applications set the field for output, drivers set the field for capture. */
+ json_object *field_obj;
+ json_object_object_get_ex(buf_obj, "field", &field_obj);
+ buf.field = (__u32) json_object_get_int(field_obj);
+
+ json_object *timestamp_obj;
+ json_object_object_get_ex(buf_obj, "timestamp", ×tamp_obj);
+
+ struct timeval tv;
+ memset(&tv, 0, sizeof(timeval));
+ json_object *tv_sec_obj;
+ json_object_object_get_ex(timestamp_obj, "tv_sec", &tv_sec_obj);
+ tv.tv_sec = json_object_get_int(tv_sec_obj);
+ json_object *tv_usec_obj;
+ json_object_object_get_ex(timestamp_obj, "tv_usec", &tv_usec_obj);
+ tv.tv_usec = json_object_get_int(tv_usec_obj);
+ buf.timestamp = tv;
+
+ /* Sequence set by the driver. */
+ json_object *sequence_obj;
+ json_object_object_get_ex(buf_obj, "sequence", &sequence_obj);
+ buf.sequence = (__u32) json_object_get_int(sequence_obj);
+
+ json_object *memory_obj;
+ json_object_object_get_ex(buf_obj, "memory", &memory_obj);
+ buf.memory = (__u32) json_object_get_int(memory_obj);
+
+ /* Get the length before the m union, since the length holds the number of planes. */
+ json_object *length_obj;
+ json_object_object_get_ex(buf_obj, "length", &length_obj);
+ buf.length = (__u32) json_object_get_int(length_obj);
+
+ json_object *m_obj;
+ json_object_object_get_ex(buf_obj, "m", &m_obj);
+ if (buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ json_object *planes_obj;
+ json_object_object_get_ex(m_obj, "planes", &planes_obj);
+ json_object *plane_obj = json_object_array_get_idx(planes_obj, 0); /* TODO add planes > 0 */
+ buf.m.planes = retrace_v4l2_plane(plane_obj, buf.memory);
+ }
+
+ /* For memory-to-memory devices, applications use requests only for output buffers. */
+ if (buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ json_object *request_fd_obj;
+ json_object_object_get_ex(buf_obj, "request_fd", &request_fd_obj);
+ buf.request_fd = (__s32) get_fd(json_object_get_int(request_fd_obj));
+ }
+
+ return buf;
+}
+
+void retrace_vidioc_querybuf(int fd_retrace, json_object *ioctl_args_user)
+{
+ struct v4l2_buffer buf = retrace_v4l2_buffer(ioctl_args_user);
+
+ ioctl(fd_retrace, VIDIOC_QUERYBUF, &buf);
+
+ if (buf.m.planes != nullptr)
+ free(buf.m.planes);
+
+ if (debug || (errno != 0)) {
+ perror("VIDIOC_QUERYBUF");
+ fprintf(stderr, "buf.type: %s, buf.index: %d, fd_retrace: %d, \n",
+ buftype2s(buf.type).c_str(), buf.index, fd_retrace);
+ print_context();
+ }
+}
+
+
+void retrace_vidioc_qbuf(int fd_retrace, json_object *ioctl_args_user)
+{
+ struct v4l2_buffer buf = retrace_v4l2_buffer(ioctl_args_user);
+
+ ioctl(fd_retrace, VIDIOC_QBUF, &buf);
+
+ if (buf.m.planes != nullptr)
+ free(buf.m.planes);
+
+ if (debug || (errno != 0)) {
+ perror("VIDIOC_QBUF");
+ fprintf(stderr, "buf.type: %s, buf.index: %d, fd_retrace: %d, \n",
+ buftype2s(buf.type).c_str(), buf.index, fd_retrace);
+ print_context();
+ }
+}
+
+struct v4l2_exportbuffer retrace_v4l2_exportbuffer(json_object *ioctl_args)
+{
+ struct v4l2_exportbuffer export_buffer;
+ memset(&export_buffer, 0, sizeof(v4l2_exportbuffer));
+
+ json_object *exportbuffer_obj;
+ json_object_object_get_ex(ioctl_args, "v4l2_exportbuffer", &exportbuffer_obj);
+
+ json_object *type_obj;
+ json_object_object_get_ex(exportbuffer_obj, "type", &type_obj);
+ export_buffer.type = json_object_get_int(type_obj);
+
+ json_object *index_obj;
+ json_object_object_get_ex(exportbuffer_obj, "index", &index_obj);
+ export_buffer.index = json_object_get_int(index_obj);
+
+ json_object *plane_obj;
+ json_object_object_get_ex(exportbuffer_obj, "plane", &plane_obj);
+ export_buffer.plane = json_object_get_int(plane_obj);
+
+ json_object *flags_obj;
+ json_object_object_get_ex(exportbuffer_obj, "flags", &flags_obj);
+ export_buffer.flags = json_object_get_int(flags_obj);
+
+ json_object *fd_obj;
+ json_object_object_get_ex(exportbuffer_obj, "fd", &fd_obj);
+ export_buffer.fd = json_object_get_int(fd_obj);
+
+ return export_buffer;
+}
+
+void retrace_vidioc_expbuf(int fd_retrace, json_object *ioctl_args_user, json_object *ioctl_args_driver)
+{
+ struct v4l2_exportbuffer export_buffer;
+ memset(&export_buffer, 0, sizeof(v4l2_exportbuffer));
+
+ export_buffer = retrace_v4l2_exportbuffer(ioctl_args_user);
+ ioctl(fd_retrace, VIDIOC_EXPBUF, &export_buffer);
+ int buf_fd_retrace = export_buffer.fd;
+ add_buffer_retrace(buf_fd_retrace);
+
+ /*
+ * Get the export buffer file descriptor as provided by the driver in the original trace context.
+ * Then associate this original file descriptor with the current file descriptor in the retrace context.
+ */
+ memset(&export_buffer, 0, sizeof(v4l2_exportbuffer));
+ export_buffer = retrace_v4l2_exportbuffer(ioctl_args_driver);
+ int buf_fd_trace = export_buffer.fd;
+ add_fd(buf_fd_trace, buf_fd_retrace);
+
+ if (debug || (errno != 0)) {
+ perror("VIDIOC_EXPBUF");
+ fprintf(stderr, "type: %s \n", buftype2s(export_buffer.type).c_str());
+ fprintf(stderr, "index: %d, fd: %d\n", export_buffer.index, buf_fd_retrace);
+ print_context();
+ }
+}
+
+void retrace_vidioc_dqbuf(int fd_retrace, json_object *ioctl_args_user)
+{
+ struct v4l2_buffer buf = retrace_v4l2_buffer(ioctl_args_user);
+
+ struct pollfd *pfds = (struct pollfd *) calloc(1, sizeof(struct pollfd));
+ if (pfds == NULL)
+ exit(EXIT_FAILURE);
+ pfds[0].fd = fd_retrace;
+ pfds[0].events = POLLIN;
+ poll(pfds, 1, 5000);
+ free(pfds);
+
+ ioctl(fd_retrace, VIDIOC_DQBUF, &buf);
+
+ if (debug || (errno != 0)) {
+ perror("VIDIOC_DQBUF");
+ fprintf(stderr, "fd_retrace: %d\n", fd_retrace);
+ fprintf(stderr, "buf.index: %d\n", buf.index);
+ fprintf(stderr, "buf.type: %s,\n", buftype2s(buf.type).c_str());
+ fprintf(stderr, "buf.bytesused: %u, \n", buf.bytesused);
+ fprintf(stderr, "buf.flags: %u\n", buf.flags);
+ fprintf(stderr, "buf.field: %u, buf.request_fd: %d\n", buf.field, buf.request_fd);
+ fprintf(stderr, "buf.request_fd: %d\n", buf.request_fd);
+ print_context();
+ }
+
+ if (buf.m.planes != nullptr)
+ free(buf.m.planes);
+}
+
+void retrace_vidioc_streamon(int fd_retrace, json_object *ioctl_args)
+{
+ json_object *buf_type_obj;
+ json_object_object_get_ex(ioctl_args, "buf_type", &buf_type_obj);
+ v4l2_buf_type buf_type = (v4l2_buf_type) json_object_get_int(buf_type_obj);
+
+ ioctl(fd_retrace, VIDIOC_STREAMON, &buf_type);
+
+ if (debug || (errno != 0)) {
+ perror("VIDIOC_STREAMON");
+ fprintf(stderr, "buftype: %s\n\n", buftype2s(buf_type).c_str());
+ }
+}
+
+void retrace_vidioc_streamoff(int fd_retrace, json_object *ioctl_args)
+{
+ json_object *buf_type_obj;
+ json_object_object_get_ex(ioctl_args, "buf_type", &buf_type_obj);
+ v4l2_buf_type buf_type = (v4l2_buf_type) json_object_get_int(buf_type_obj);
+
+ ioctl(fd_retrace, VIDIOC_STREAMOFF, &buf_type);
+
+ if (debug || (errno != 0)) {
+ perror("VIDIOC_STREAMOFF");
+ fprintf(stderr, "buftype: %s\n", buftype2s(buf_type).c_str());
+ fprintf(stderr, "\n");
+ }
+}
+
+struct v4l2_plane_pix_format get_v4l2_plane_pix_format(json_object *pix_mp_obj, int plane)
+{
+ std::string key;
+ struct v4l2_plane_pix_format plane_fmt;
+ memset(&plane_fmt, 0, sizeof(v4l2_plane_pix_format));
+
+ json_object *plane_fmt_obj, *sizeimage_obj, *bytesperline_obj;
+
+ key = "v4l2_plane_pix_format_";
+ key += std::to_string(plane);
+ json_object_object_get_ex(pix_mp_obj, key.c_str(), &plane_fmt_obj);
+
+ json_object_object_get_ex(plane_fmt_obj, "sizeimage", &sizeimage_obj);
+ plane_fmt.sizeimage = json_object_get_int(sizeimage_obj);
+
+ json_object_object_get_ex(plane_fmt_obj, "bytesperline", &bytesperline_obj);
+ plane_fmt.bytesperline = json_object_get_int(bytesperline_obj);
+
+ return plane_fmt;
+}
+
+struct v4l2_pix_format_mplane retrace_v4l2_pix_format_mplane(json_object *v4l2_format_obj)
+{
+ struct v4l2_pix_format_mplane pix_mp;
+ memset(&pix_mp, 0, sizeof(v4l2_pix_format_mplane));
+
+ json_object *pix_mp_obj;
+ json_object_object_get_ex(v4l2_format_obj, "v4l2_pix_format_mplane", &pix_mp_obj);
+
+ json_object *width_obj;
+ json_object_object_get_ex(pix_mp_obj, "width", &width_obj);
+ pix_mp.width = json_object_get_int(width_obj);
+
+ json_object *height_obj;
+ json_object_object_get_ex(pix_mp_obj, "height", &height_obj);
+ pix_mp.height = json_object_get_int(height_obj);
+
+ json_object *pixelformat_obj;
+ json_object_object_get_ex(pix_mp_obj, "pixelformat", &pixelformat_obj);
+ pix_mp.pixelformat = json_object_get_int(pixelformat_obj);
+
+ json_object *field_obj;
+ json_object_object_get_ex(pix_mp_obj, "field", &field_obj);
+ pix_mp.field = json_object_get_int(field_obj);
+
+ json_object *colorspace_obj;
+ json_object_object_get_ex(pix_mp_obj, "colorspace", &colorspace_obj);
+ pix_mp.colorspace = json_object_get_int(colorspace_obj);
+
+ json_object *num_planes_obj;
+ json_object_object_get_ex(pix_mp_obj, "num_planes", &num_planes_obj);
+ pix_mp.num_planes = json_object_get_int(num_planes_obj);
+
+ for (int i = 0; i < pix_mp.num_planes; i++)
+ pix_mp.plane_fmt[i] = get_v4l2_plane_pix_format(pix_mp_obj, i);
+
+ json_object *flags_obj;
+ json_object_object_get_ex(pix_mp_obj, "flags", &flags_obj);
+ pix_mp.flags = json_object_get_int(flags_obj);
+
+ json_object *ycbcr_enc_obj;
+ json_object_object_get_ex(pix_mp_obj, "ycbcr_enc", &ycbcr_enc_obj);
+ pix_mp.ycbcr_enc = json_object_get_int(ycbcr_enc_obj);
+
+ json_object *quantization_obj;
+ json_object_object_get_ex(pix_mp_obj, "quantization", &quantization_obj);
+ pix_mp.quantization = json_object_get_int(quantization_obj);
+
+ json_object *xfer_func_obj;
+ json_object_object_get_ex(pix_mp_obj, "xfer_func", &xfer_func_obj);
+ pix_mp.xfer_func = json_object_get_int(xfer_func_obj);
+
+ return pix_mp;
+}
+
+struct v4l2_format retrace_v4l2_format(json_object *ioctl_args)
+{
+ struct v4l2_format format;
+ memset(&format, 0, sizeof(format));
+
+ json_object *v4l2_format_obj;
+ json_object_object_get_ex(ioctl_args, "v4l2_format", &v4l2_format_obj);
+
+ json_object *type_obj;
+ json_object_object_get_ex(v4l2_format_obj, "type", &type_obj);
+ format.type = json_object_get_int(type_obj);
+
+ switch (format.type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ format.fmt.pix_mp = retrace_v4l2_pix_format_mplane(v4l2_format_obj);
+ break;
+ case V4L2_BUF_TYPE_SDR_CAPTURE:
+ case V4L2_BUF_TYPE_SDR_OUTPUT:
+ case V4L2_BUF_TYPE_META_CAPTURE:
+ case V4L2_BUF_TYPE_META_OUTPUT:
+ break;
+ }
+
+ return format;
+}
+
+void retrace_vidioc_g_fmt(int fd_retrace, json_object *ioctl_args_user)
+{
+ struct v4l2_format format;
+ memset(&format, 0, sizeof(format));
+
+ format = retrace_v4l2_format(ioctl_args_user);
+
+ ioctl(fd_retrace, VIDIOC_G_FMT, &format);
+
+ if (debug || (errno != 0))
+ perror("VIDIOC_G_FMT");
+}
+
+void retrace_vidioc_s_fmt(int fd_retrace, json_object *ioctl_args_user)
+{
+ struct v4l2_format format;
+ memset(&format, 0, sizeof(format));
+
+ format = retrace_v4l2_format(ioctl_args_user);
+
+ ioctl(fd_retrace, VIDIOC_S_FMT, &format);
+
+ if (debug || (errno != 0)) {
+ perror("VIDIOC_S_FMT");
+ fprintf(stderr, "%s\n", buftype2s(format.type).c_str());
+ fprintf(stderr, "format.fmt.pix_mp.pixelformat: %s\n\n",
+ fcc2s(format.fmt.pix_mp.pixelformat).c_str());
+ }
+}
+
+struct v4l2_ext_control retrace_v4l2_ext_control(json_object *ext_controls_obj, int ctrl_idx)
+{
+ struct v4l2_ext_control ctrl;
+ memset(&ctrl, 0, sizeof(v4l2_ext_control));
+
+ std::string unique_key_for_control = "v4l2_ext_control_";
+ unique_key_for_control += std::to_string(ctrl_idx);
+
+ json_object *ctrl_obj;
+ json_object_object_get_ex(ext_controls_obj, unique_key_for_control.c_str(), &ctrl_obj);
+
+ json_object *id_obj;
+ json_object_object_get_ex(ctrl_obj, "id", &id_obj);
+ ctrl.id = json_object_get_int(id_obj);
+
+ json_object *size_obj;
+ json_object_object_get_ex(ctrl_obj, "size", &size_obj);
+ ctrl.size = json_object_get_int(size_obj);
+
+ if ((ctrl.id & V4L2_CID_CODEC_STATELESS_BASE) == V4L2_CID_CODEC_STATELESS_BASE) {
+ switch (ctrl.id) {
+ case V4L2_CID_STATELESS_VP8_FRAME:
+ ctrl.ptr = retrace_v4l2_ctrl_vp8_frame_pointer(ctrl_obj);
+ break;
+ }
+ }
+ return ctrl;
+}
+
+struct v4l2_ext_control *retrace_v4l2_ext_control_array_pointer(json_object *ext_controls_obj, int count)
+{
+ struct v4l2_ext_control *ctrl_array_pointer = (struct v4l2_ext_control *) calloc(count, sizeof(v4l2_ext_control));
+
+ for (int i = 0; i < count; i++)
+ ctrl_array_pointer[i] = retrace_v4l2_ext_control(ext_controls_obj, i);
+
+ return ctrl_array_pointer;
+}
+
+void retrace_vidioc_s_ext_ctrls(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_ext_controls ext_controls;
+ memset(&ext_controls, 0, sizeof(ext_controls));
+
+ json_object *ext_controls_obj;
+ json_object_object_get_ex(ioctl_args, "v4l2_ext_controls", &ext_controls_obj);
+
+ json_object *which_obj;
+ json_object_object_get_ex(ext_controls_obj, "which", &which_obj);
+ ext_controls.which = json_object_get_int(which_obj);
+
+ json_object *count_obj;
+ json_object_object_get_ex(ext_controls_obj, "count", &count_obj);
+ ext_controls.count = json_object_get_int(count_obj);
+
+ /* request_fd is only valid for V4L2_CTRL_WHICH_REQUEST_VAL */
+ if (ext_controls.which == V4L2_CTRL_WHICH_REQUEST_VAL) {
+ json_object *request_fd_obj;
+ json_object_object_get_ex(ext_controls_obj, "request_fd", &request_fd_obj);
+ int request_fd_trace = json_object_get_int(request_fd_obj);
+ ext_controls.request_fd = get_fd(request_fd_trace);
+ }
+
+ ext_controls.controls = retrace_v4l2_ext_control_array_pointer(ext_controls_obj, ext_controls.count);
+
+ ioctl(fd_retrace, VIDIOC_S_EXT_CTRLS, &ext_controls);
+
+ if (debug || (errno != 0))
+ perror("VIDIOC_S_EXT_CTRLS");
+
+ /* Free controls working backwards from the end of the controls array. */
+ for (int i = (ext_controls.count - 1); i >= 0 ; i--) {
+ if (ext_controls.controls[i].ptr != nullptr)
+ free(ext_controls.controls[i].ptr);
+ }
+
+ if (ext_controls.controls != nullptr)
+ free(ext_controls.controls);
+}
+
+void retrace_query_ext_ctrl(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_query_ext_ctrl query_ext_ctrl;
+ memset(&query_ext_ctrl, 0, sizeof(v4l2_query_ext_ctrl));
+
+ json_object *query_ext_ctrl_obj;
+ json_object_object_get_ex(ioctl_args, "v4l2_query_ext_ctrl", &query_ext_ctrl_obj);
+
+ json_object *id_obj;
+ json_object_object_get_ex(query_ext_ctrl_obj, "id", &id_obj);
+ query_ext_ctrl.id = json_object_get_int(id_obj);
+
+ ioctl(fd_retrace, VIDIOC_QUERY_EXT_CTRL, &query_ext_ctrl);
+
+ if (debug) {
+ perror("VIDIOC_QUERY_EXT_CTRL");
+ fprintf(stderr, "id: %u\n\n", query_ext_ctrl.id);
+ }
+}
+
+void retrace_vidioc_enum_fmt(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_fmtdesc fmtdesc;
+ memset(&fmtdesc, 0, sizeof(v4l2_fmtdesc));
+
+ json_object *v4l2_fmtdesc_obj;
+ json_object_object_get_ex(ioctl_args, "v4l2_fmtdesc", &v4l2_fmtdesc_obj);
+
+ json_object *index_obj;
+ json_object_object_get_ex(v4l2_fmtdesc_obj, "index", &index_obj);
+ fmtdesc.index = json_object_get_int(index_obj);
+
+ json_object *type_obj;
+ json_object_object_get_ex(v4l2_fmtdesc_obj, "type", &type_obj);
+ fmtdesc.type = json_object_get_int(type_obj);
+
+ json_object *mbus_code_obj;
+ json_object_object_get_ex(v4l2_fmtdesc_obj, "mbus_code", &mbus_code_obj);
+ fmtdesc.mbus_code = json_object_get_int(mbus_code_obj);
+
+ ioctl(fd_retrace, VIDIOC_ENUM_FMT, &fmtdesc);
+
+ if (debug) {
+ perror("VIDIOC_ENUM_FMT");
+ fprintf(stderr, "index: %u\n", fmtdesc.index);
+ fprintf(stderr, "type: %u\n", fmtdesc.type);
+ fprintf(stderr, "flags: %u\n", fmtdesc.flags);
+ fprintf(stderr, "description: %s\n", fmtdesc.description);
+ fprintf(stderr, "pixelformat: %u\n", fmtdesc.pixelformat);
+ fprintf(stderr, "mbus_code: %u\n\n", fmtdesc.mbus_code);
+ }
+}
+
+void retrace_vidioc_querycap(int fd_retrace)
+{
+ struct v4l2_capability argp;
+ memset(&argp, 0, sizeof(v4l2_capability));
+
+ ioctl(fd_retrace, VIDIOC_QUERYCAP, &argp);
+
+ if (debug || (errno != 0))
+ perror("VIDIOC_QUERYCAP");
+}
+
+void retrace_media_ioc_request_alloc(int fd_retrace, json_object *ioctl_args)
+{
+ /* Get the original request file descriptor from the original trace file. */
+ json_object *request_fd_trace_obj;
+ json_object_object_get_ex(ioctl_args, "request_fd", &request_fd_trace_obj);
+ int request_fd_trace = json_object_get_int(request_fd_trace_obj);
+
+ /* Allocate a request in the retrace context. */
+ __s32 request_fd_retrace = 0;
+ ioctl(fd_retrace, MEDIA_IOC_REQUEST_ALLOC, &request_fd_retrace);
+
+ /* Associate the original request file descriptor with the current request file descriptor. */
+ add_fd(request_fd_trace, request_fd_retrace);
+
+ if (debug || (errno != 0))
+ perror("MEDIA_IOC_REQUEST_ALLOC");
+}
+
+void retrace_dma_buf_ioctl_sync(int fd_retrace, json_object *ioctl_args_user)
+{
+ struct dma_buf_sync sync;
+ memset(&sync, 0, sizeof(dma_buf_sync));
+
+ json_object *sync_obj;
+ json_object_object_get_ex(ioctl_args_user, "dma_buf_sync",&sync_obj);
+
+ json_object *flags_obj;
+ json_object_object_get_ex(sync_obj, "flags",&flags_obj);
+ sync.flags = json_object_get_int(flags_obj);
+
+ ioctl(fd_retrace, DMA_BUF_IOCTL_SYNC, &sync);
+
+ if (debug || (errno != 0))
+ perror("DMA_BUF_IOCTL_SYNC");
+}
+
+void retrace_ioctl_media(int fd_retrace, long int request, json_object *ioctl_args_driver)
+{
+ switch (request){
+ case MEDIA_IOC_DEVICE_INFO:
+ case MEDIA_IOC_ENUM_ENTITIES:
+ case MEDIA_IOC_ENUM_LINKS:
+ case MEDIA_IOC_SETUP_LINK:
+ case MEDIA_IOC_G_TOPOLOGY: {
+ struct media_v2_topology top;
+ memset(&top, 0, sizeof(media_v2_topology));
+ ioctl(fd_retrace, MEDIA_IOC_G_TOPOLOGY, &top);
+ if (debug || (errno != 0))
+ perror("MEDIA_IOC_G_TOPOLOGY");
+ break;
+ }
+ case MEDIA_IOC_REQUEST_ALLOC:
+ retrace_media_ioc_request_alloc(fd_retrace, ioctl_args_driver);
+ break;
+ case MEDIA_REQUEST_IOC_QUEUE: {
+ ioctl(fd_retrace, MEDIA_REQUEST_IOC_QUEUE);
+ if (debug || (errno != 0))
+ perror("MEDIA_REQUEST_IOC_QUEUE");
+ break;
+ }
+ case MEDIA_REQUEST_IOC_REINIT: {
+ ioctl(fd_retrace, MEDIA_REQUEST_IOC_REINIT);
+ if (debug || (errno != 0))
+ perror("MEDIA_REQUEST_IOC_REINIT");
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void retrace_ioctl_video(int fd_retrace, long int request, json_object *ioctl_args_user, json_object *ioctl_args_driver)
+{
+ switch (request) {
+ //TODO ADD QUERYBUF
+ case VIDIOC_QUERYCAP:
+ retrace_vidioc_querycap(fd_retrace);
+ break;
+ case VIDIOC_ENUM_FMT:
+ retrace_vidioc_enum_fmt(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_G_FMT:
+ retrace_vidioc_g_fmt(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_S_FMT:
+ retrace_vidioc_s_fmt(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_REQBUFS:
+ retrace_vidioc_reqbufs(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_QUERYBUF:
+ retrace_vidioc_querybuf(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_QBUF:
+ retrace_vidioc_qbuf(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_EXPBUF:
+ retrace_vidioc_expbuf(fd_retrace, ioctl_args_user, ioctl_args_driver);
+ break;
+ case VIDIOC_DQBUF:
+ retrace_vidioc_dqbuf(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_STREAMON:
+ retrace_vidioc_streamon(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_STREAMOFF:
+ retrace_vidioc_streamoff(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_S_EXT_CTRLS:
+ retrace_vidioc_s_ext_ctrls(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_QUERY_EXT_CTRL:
+ retrace_query_ext_ctrl(fd_retrace, ioctl_args_user);
+ break;
+ default:
+ break;
+ }
+}
+
+void retrace_ioctl(json_object *syscall_obj)
+{
+ int fd_retrace = 0;
+ __u8 ioctl_type = 0;
+ long int request = 0;
+
+ json_object *fd_trace_obj;
+ json_object_object_get_ex(syscall_obj, "fd", &fd_trace_obj);
+ fd_retrace = get_fd(json_object_get_int(fd_trace_obj));
+
+ json_object *request_obj;
+ json_object_object_get_ex(syscall_obj, "request", &request_obj);
+ request = json_object_get_int64(request_obj);
+
+ json_object *ioctl_args_user;
+ json_object_object_get_ex(syscall_obj, "ioctl_args_from_userspace", &ioctl_args_user);
+
+ json_object *ioctl_args_driver;
+ json_object_object_get_ex(syscall_obj, "ioctl_args_from_driver", &ioctl_args_driver);
+
+ ioctl_type = _IOC_TYPE(request);
+ switch (ioctl_type) {
+ case 'V':
+ retrace_ioctl_video(fd_retrace, request, ioctl_args_user, ioctl_args_driver);
+ break;
+ case '|':
+ retrace_ioctl_media(fd_retrace, request, ioctl_args_driver);
+ break;
+ case 'b':
+ if (request == DMA_BUF_IOCTL_SYNC)
+ retrace_dma_buf_ioctl_sync(fd_retrace, ioctl_args_user);
+ break;
+ default:
+ break;
+ }
+}
+
+void write_to_output_buffer(unsigned char *buffer_pointer, int bytesused, json_object *mem_obj)
+{
+ std::string data;
+ int byteswritten = 0;
+ json_object *line_obj;
+ size_t number_of_lines;
+
+ json_object *mem_array_obj;
+ json_object_object_get_ex(mem_obj, "mem_array", &mem_array_obj);
+ number_of_lines = json_object_array_length(mem_array_obj);
+
+ for (long unsigned int i = 0; i < number_of_lines; i++) {
+ line_obj = json_object_array_get_idx(mem_array_obj, i);
+ data = json_object_get_string(line_obj);
+
+ for (long unsigned i = 0; i < data.length(); i++) {
+ if (std::isspace(data[i]))
+ continue;
+ try {
+ /* Two values from the string e.g. "D9" are needed to write one byte. */
+ *buffer_pointer = (char) std::stoi(data.substr(i,2), nullptr, 16);
+ buffer_pointer++;
+ i++;
+ byteswritten++;
+ } catch (std::invalid_argument& ia) {
+ fprintf(stderr, "Warning: \'%s\' is an invalid argument; %s line: %d.\n",
+ data.substr(i,2).c_str(), __func__, __LINE__);
+ } catch (std::out_of_range& oor) {
+ fprintf(stderr, "Warning: \'%s\' is out of range; %s line: %d.\n",
+ data.substr(i,2).c_str(), __func__, __LINE__);
+ }
+ }
+ }
+
+ if (debug) {
+ fprintf(stderr, "\nWrite to Output Buffer\n");
+ fprintf(stderr, "bytesused: %d, byteswritten: %d\n", bytesused, byteswritten);
+ fprintf(stderr, "\n");
+ }
+}
+
+void write_decoded_frames_to_yuv_file_retrace(unsigned char *buffer_pointer, int bytesused)
+{
+ int byteswritten = 0;
+
+ FILE *fp = fopen(retrace_filename.c_str(), "a");
+ for (int i = 0; i < bytesused; i++) {
+ fwrite(&buffer_pointer[i], sizeof(unsigned char), 1, fp);
+ byteswritten++;
+ }
+ fclose(fp);
+
+ if (debug){
+ fprintf(stderr, "\nWrite to File\n");
+ fprintf(stderr, "%s\n", retrace_filename.c_str());
+ fprintf(stderr, "bytesused: %d, byteswritten: %d\n", bytesused, byteswritten);
+ fprintf(stderr, "\n");
+ }
+}
+
+void retrace_mem(json_object *mem_obj)
+{
+ json_object *type_obj;
+ json_object_object_get_ex(mem_obj, "type", &type_obj);
+ v4l2_buf_type type = (v4l2_buf_type) json_object_get_int(type_obj);
+
+ json_object *bytesused_obj;
+ json_object_object_get_ex(mem_obj, "bytesused", &bytesused_obj);
+ int bytesused = json_object_get_int(bytesused_obj);
+
+ json_object *address_obj;
+ json_object_object_get_ex(mem_obj, "address", &address_obj);
+ long int buffer_address_trace = json_object_get_int64(address_obj);
+
+ /* Convert the trace address to the corresponding retrace address. */
+ long int buffer_address_retrace = get_buffer_address_retrace(buffer_address_trace);
+
+ unsigned char *buffer_pointer = (unsigned char *) buffer_address_retrace;
+
+ /* Get the encoded data from the json file and write it to output buffer memory. */
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ write_to_output_buffer(buffer_pointer, bytesused, mem_obj);
+
+ /* Get the decoded capture buffer from memory and write it to a binary yuv file. */
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ write_decoded_frames_to_yuv_file_retrace(buffer_pointer, bytesused);
+}
+
+void retrace_object(json_object *jobj)
+{
+ json_object *syscall_obj;
+ int ret = json_object_object_get_ex(jobj, "syscall", &syscall_obj);
+ int syscall = json_object_get_int(syscall_obj);
+
+ /* If the json object doesn't hold a syscall, check if it holds a memory dump. */
+ if (ret == 0) {
+ json_object *temp_obj;
+ if (json_object_object_get_ex(jobj, "mem_dump", &temp_obj)) {
+ retrace_mem(jobj);
+ }
+ return;
+ }
+
+ errno = 0;
+
+ switch (syscall) {
+ case LIBTRACER_SYSCALL_IOCTL:
+ retrace_ioctl(jobj);
+ break;
+ case LIBTRACER_SYSCALL_OPEN:
+ case LIBTRACER_SYSCALL_OPEN64:
+ retrace_open64(jobj);
+ break;
+ case LIBTRACER_SYSCALL_CLOSE:
+ retrace_close(jobj);
+ break;
+ case LIBTRACER_SYSCALL_MMAP64:
+ retrace_mmap64(jobj);
+ break;
+ case LIBTRACER_SYSCALL_MUNMAP:
+ retrace_munmap(jobj);
+ break;
+ default:
+ break;
+ }
+}
+
+void retrace_array(json_object *root_array_obj)
+{
+ json_object *jobj;
+ struct array_list *al = json_object_get_array(root_array_obj);
+
+ for (size_t i = 0; i < array_list_length(al); i++) {
+ jobj = (json_object *) array_list_get_idx(al, i);
+ retrace_object(jobj);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int ch;
+ char short_options[] = {'d', 'm', ':', 'v', ':'};
+
+ do {
+ ch = getopt(argc, argv, short_options);
+ switch (ch){
+ case 'd':
+ debug = true;
+ break;
+ case 'm':
+ dev_path_media = *optarg;
+ dev_path_media = "/dev/media" + dev_path_media;
+ fprintf(stderr, "Using: %s\n", dev_path_media.c_str());
+ break;
+ case 'v':
+ dev_path_video = *optarg;
+ dev_path_video = "/dev/video" + dev_path_video;
+ fprintf(stderr, "Using: %s\n", dev_path_video.c_str());
+ break;
+ }
+ } while (ch != -1);
+
+
+ if (optind == argc) {
+ fprintf(stderr, "usage: retracer [-d] [-m <media device number>] [-v <video device number>] <some_trace_file>.json\n");
+ return -1;
+ }
+
+ std::string trace_filename = argv[optind];
+
+ FILE *trace_file = fopen(trace_filename.c_str(), "r");
+ if (trace_file == NULL) {
+ fprintf(stderr, "Trace file error: %s\n", trace_filename.c_str());
+ return -1;
+ }
+ fclose(trace_file);
+
+ fprintf(stderr, "Retracing: %s\n", trace_filename.c_str());
+
+ /* Create file to hold the decoded frames. Discard previous retraced file if any. */
+ retrace_filename = trace_filename;
+ retrace_filename = retrace_filename.replace(5, retrace_filename.npos, "_retrace");
+ retrace_filename += ".yuv";
+
+ json_object *root_array_obj = json_object_from_file(trace_filename.c_str());
+ retrace_array(root_array_obj);
+ json_object_put(root_array_obj);
+
+ fprintf(stderr, "Retracing complete in %s\n", retrace_filename.c_str());
+}
diff --git a/utils/tracer/retracer.h b/utils/tracer/retracer.h
new file mode 100644
index 00000000..e575b4b5
--- /dev/null
+++ b/utils/tracer/retracer.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#ifndef RETRACER_H
+#define RETRACER_H
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <poll.h>
+#include <unordered_map>
+#include <stdexcept>
+#include <list>
+#include <json.h>
+#include <v4l2-info.h>
+#include "trace-info.h"
+#include "retrace-vp8.h"
+#include "retrace-helper.h"
+
+#endif
--
2.37.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [RFC 0/2] v4l2 stateless tracer/retracer utilities
2022-08-20 0:50 [RFC 0/2] v4l2 stateless tracer/retracer utilities Deborah Brouwer
2022-08-20 0:50 ` [RFC 1/2] utils: add stateless tracer utility Deborah Brouwer
2022-08-20 0:50 ` [RFC 2/2] utils: add stateless retracer utility Deborah Brouwer
@ 2022-08-26 19:05 ` Nicolas Dufresne
2 siblings, 0 replies; 5+ messages in thread
From: Nicolas Dufresne @ 2022-08-26 19:05 UTC (permalink / raw)
To: Deborah Brouwer, linux-media; +Cc: daniel.almeida, nfraprado, hverkuil-cisco
Hi Deborah,
very nice work, I'm very exciting to see such a tool coming to life.
Le vendredi 19 août 2022 à 17:50 -0700, Deborah Brouwer a écrit :
> This project helps to test v4l2 stateless decoder drivers by tracing,
> recording and replaying (i.e. "retracing") userspace's interaction with
> a stateless decoder driver.
>
> The tracer utility attaches to a userspace application and generates a
> json file with relevant system calls, parameters and encoded data.
>
> The retracer utility reads the json-file and makes the same system calls
> to a v4l2 stateless driver. Since the retracer is independent from the
> original userspace application that was traced, testing can be decoupled
> from extraneous factors in the userspace environment. The json-file can
> also be edited to inject errors and test a driver's error-handling
> abilities.
>
> NOTE:
> This project is work in progress and currently only traces VP8, but
> H264 and FWHT will follow shortly.
>
> EXAMPLE:
> ./tracer gst-launch-1.0 -- filesrc location=<some_vp8_file> ! parsebin !
> v4l2slvp8dec ! videocodectestsink
>
> ./retracer 10284_trace.json
My first comment would be to polish a bit the user interface, and make the
installation of the tool a bit more friendly to Linux distribution. I would like
suggest to have a single executable, and to name it with a proper name space.
Something like:
v4l2-tracer trace -- gst-launch-1.0 filesrc location=<some_vp8_file> ...
v4l2-tracer retrace 10284_trace.json
We can refine this interface over time, but at least this gives a single command
to remember, and will be more friendly when installing this onto your system.
regards,
Nicolas
>
> FURTHER INFO AND TEST FILES:
> https://gitlab.collabora.com/dbrouwer/v4l2-stateless-tracer-utility/-/tree/main/
>
> Deborah Brouwer (2):
> utils: add stateless tracer utility
> utils: add stateless retracer utility
>
> configure.ac | 6 +
> utils/Makefile.am | 5 +
> utils/common/v4l2-info.cpp | 7 +-
> utils/common/v4l2-info.h | 8 +
> utils/tracer/.gitignore | 9 +
> utils/tracer/Makefile.am | 23 +
> utils/tracer/libtracer.cpp | 217 ++++++
> utils/tracer/libtracer.h | 92 +++
> utils/tracer/retrace-helper.cpp | 141 ++++
> utils/tracer/retrace-helper.h | 18 +
> utils/tracer/retrace-vp8.cpp | 288 ++++++++
> utils/tracer/retrace-vp8.h | 11 +
> utils/tracer/retracer.cpp | 1090 +++++++++++++++++++++++++++++++
> utils/tracer/retracer.h | 24 +
> utils/tracer/trace-helper.cpp | 218 +++++++
> utils/tracer/trace-info.cpp | 358 ++++++++++
> utils/tracer/trace-info.h | 72 ++
> utils/tracer/trace-vp8.cpp | 183 ++++++
> utils/tracer/trace.cpp | 520 +++++++++++++++
> utils/tracer/tracer.cpp | 91 +++
> 20 files changed, 3375 insertions(+), 6 deletions(-)
> create mode 100644 utils/tracer/.gitignore
> create mode 100644 utils/tracer/Makefile.am
> create mode 100644 utils/tracer/libtracer.cpp
> create mode 100644 utils/tracer/libtracer.h
> create mode 100755 utils/tracer/retrace-helper.cpp
> create mode 100644 utils/tracer/retrace-helper.h
> create mode 100755 utils/tracer/retrace-vp8.cpp
> create mode 100644 utils/tracer/retrace-vp8.h
> create mode 100755 utils/tracer/retracer.cpp
> create mode 100644 utils/tracer/retracer.h
> create mode 100644 utils/tracer/trace-helper.cpp
> create mode 100644 utils/tracer/trace-info.cpp
> create mode 100644 utils/tracer/trace-info.h
> create mode 100644 utils/tracer/trace-vp8.cpp
> create mode 100644 utils/tracer/trace.cpp
> create mode 100644 utils/tracer/tracer.cpp
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFC 1/2] utils: add stateless tracer utility
2022-08-20 0:50 ` [RFC 1/2] utils: add stateless tracer utility Deborah Brouwer
@ 2022-08-26 19:18 ` Nicolas Dufresne
0 siblings, 0 replies; 5+ messages in thread
From: Nicolas Dufresne @ 2022-08-26 19:18 UTC (permalink / raw)
To: Deborah Brouwer, linux-media; +Cc: daniel.almeida, nfraprado, hverkuil-cisco
Hi Deborah,
I've been trying the tracer, nice work.
Le vendredi 19 août 2022 à 17:50 -0700, Deborah Brouwer a écrit :
> The tracer utility helps to test v4l2 stateless decoder drivers by tracing
> and recording userspace's interaction with a stateless decoder driver.
> Only system calls relevant to the v4l2 memory-to-memory stateless video
> decoder interface are traced. The tracer traces encoded data from output
> buffers and, optionally, may trace decoded data from capture buffers. The
> tracer records its results in a json-formatted trace file.
>
> Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
> ---
> configure.ac | 6 +
> utils/Makefile.am | 5 +
> utils/common/v4l2-info.cpp | 7 +-
> utils/common/v4l2-info.h | 8 +
> utils/tracer/.gitignore | 9 +
> utils/tracer/Makefile.am | 19 ++
> utils/tracer/libtracer.cpp | 217 ++++++++++++++
> utils/tracer/libtracer.h | 92 ++++++
> utils/tracer/trace-helper.cpp | 218 ++++++++++++++
> utils/tracer/trace-info.cpp | 358 +++++++++++++++++++++++
> utils/tracer/trace-info.h | 72 +++++
> utils/tracer/trace-vp8.cpp | 183 ++++++++++++
> utils/tracer/trace.cpp | 520 ++++++++++++++++++++++++++++++++++
> utils/tracer/tracer.cpp | 91 ++++++
> 14 files changed, 1799 insertions(+), 6 deletions(-)
> create mode 100644 utils/tracer/.gitignore
> create mode 100644 utils/tracer/Makefile.am
> create mode 100644 utils/tracer/libtracer.cpp
> create mode 100644 utils/tracer/libtracer.h
> create mode 100644 utils/tracer/trace-helper.cpp
> create mode 100644 utils/tracer/trace-info.cpp
> create mode 100644 utils/tracer/trace-info.h
> create mode 100644 utils/tracer/trace-vp8.cpp
> create mode 100644 utils/tracer/trace.cpp
> create mode 100644 utils/tracer/tracer.cpp
>
> diff --git a/configure.ac b/configure.ac
> index 05298981..cc604cad 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -42,6 +42,7 @@ AC_CONFIG_FILES([Makefile
> utils/cec-follower/cec-follower.1
> utils/qvidcap/Makefile
> utils/rds-ctl/Makefile
> + utils/tracer/Makefile
>
> contrib/Makefile
> contrib/freebsd/Makefile
> @@ -311,6 +312,11 @@ AS_IF([test "x$with_libudev" != xno -o "x$enable_libdvbv5" != xno],
>
> AC_SUBST([JPEG_LIBS])
>
> +PKG_CHECK_MODULES(JSONC, [json-c >= 0.16], [jsonc_pkgconfig=yes], [jsonc_pkgconfig=no])
I found during compilation is that the dependency on json-c might be a bit over
agressive. My device OS is Fedora 36 (so not too old) and version 0.16 was not
available there. I downgrade to 0.15 and it "worked", at least I think. Are you
aware of any issue with downgrading this version for wider support ?
> +AC_SUBST([JSONC_CFLAGS])
> +AC_SUBST([JSONC_LIBS])
> +AM_CONDITIONAL([HAVE_JSONC], [test x$jsonc_pkgconfig = xyes])
nit: I think it would be nice to have some way to fail the configure stage if
the deps is missing.
> +
> # Check for pthread
>
> AS_IF([test x$enable_shared != xno],
> diff --git a/utils/Makefile.am b/utils/Makefile.am
> index 0e68a612..3ccc3d81 100644
> --- a/utils/Makefile.am
> +++ b/utils/Makefile.am
> @@ -15,6 +15,11 @@ SUBDIRS = \
> cec-follower \
> rds-ctl
>
> +if HAVE_JSONC
> +SUBDIRS += \
> + tracer
> +endif
> +
> if WITH_LIBDVBV5
> SUBDIRS += \
> dvb
> diff --git a/utils/common/v4l2-info.cpp b/utils/common/v4l2-info.cpp
> index 40d45471..74e77b7a 100644
> --- a/utils/common/v4l2-info.cpp
> +++ b/utils/common/v4l2-info.cpp
> @@ -16,12 +16,7 @@ static std::string num2s(unsigned num, bool is_hex = true)
> return buf;
> }
>
> -struct flag_def {
> - unsigned flag;
> - const char *str;
> -};
> -
> -static std::string flags2s(unsigned val, const flag_def *def)
> +std::string flags2s(unsigned int val, const flag_def *def)
> {
> std::string s;
>
> diff --git a/utils/common/v4l2-info.h b/utils/common/v4l2-info.h
> index 35237853..8eb14bc4 100644
> --- a/utils/common/v4l2-info.h
> +++ b/utils/common/v4l2-info.h
> @@ -11,6 +11,14 @@
> #include <linux/videodev2.h>
> #include <linux/v4l2-subdev.h>
>
> +struct flag_def {
> + unsigned int flag;
> + const char *str;
> +};
> +
> +/* Return a comma-separated string of flags or hex value if unknown */
> +std::string flags2s(unsigned int val, const flag_def *def);
> +
> /* Print capability information */
> void v4l2_info_capability(const v4l2_capability &cap);
> void v4l2_info_subdev_capability(const v4l2_subdev_capability &subdevcap);
> diff --git a/utils/tracer/.gitignore b/utils/tracer/.gitignore
> new file mode 100644
> index 00000000..4098662d
> --- /dev/null
> +++ b/utils/tracer/.gitignore
> @@ -0,0 +1,9 @@
> +*.json
> +*.yuv
> +*.webm
> +*.h264
> +*.ivf
> +*.vp8
> +tracer
> +retracer
> +.clang-tidy
> diff --git a/utils/tracer/Makefile.am b/utils/tracer/Makefile.am
> new file mode 100644
> index 00000000..f5579198
> --- /dev/null
> +++ b/utils/tracer/Makefile.am
> @@ -0,0 +1,19 @@
> +if HAVE_JSONC
> +
> +lib_LTLIBRARIES = libtracer.la
> +include_HEADERS = libtracer.h ../../utils/common/v4l2-info.h ../../utils/common/media-info.h
> +libtracer_la_SOURCES = libtracer.cpp trace.cpp trace-vp8.cpp trace-helper.cpp \
> +trace-info.cpp ../../utils/common/v4l2-info.cpp ../../utils/common/media-info.cpp
> +
> +libtracer_la_CFLAGS = $(JSONC_CFLAGS)
> +libtracer_la_CPPFLAGS = -I$(top_srcdir)/utils/common $(JSONC_CFLAGS)
> +libtracer_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -ldl $(JSONC_LIBS)
> +libtracer_la_LIBTOOLFLAGS = --tag=disable-static
> +
> +bin_PROGRAMS = tracer
> +
> +tracer_SOURCES = tracer.cpp
> +tracer_CPPFLAGS = -I$(top_srcdir)/utils/common $(JSONC_CFLAGS)
> +tracer_LDFLAGS = -lrt -lpthread $(JSONC_LIBS)
> +
> +endif
> diff --git a/utils/tracer/libtracer.cpp b/utils/tracer/libtracer.cpp
> new file mode 100644
> index 00000000..0d18b224
> --- /dev/null
> +++ b/utils/tracer/libtracer.cpp
> @@ -0,0 +1,217 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright 2022 Collabora Ltd.
> + */
> +
> +#include "libtracer.h"
> +
> +int open64(const char *path, int oflag, ...)
> +{
> + errno = 0;
> + mode_t mode = 0;
> +
> + if (oflag & O_CREAT) {
> + va_list ap;
> + va_start(ap, oflag);
> + mode = va_arg(ap, PROMOTED_MODE_T);
> + va_end(ap);
> + }
> +
> + int (*original_open64)(const char *path, int oflag, ...);
> + original_open64 = (int (*)(const char*, int, ...)) dlsym(RTLD_NEXT, "open64");
> + int fd = (*original_open64)(path, oflag, mode);
> +
> + /* Only trace calls to video or media devices. */
> + std::string dev_path_video = "/dev/video";
> + std::string dev_path_media = "/dev/media";
> + if (strncmp(path, dev_path_video.c_str(), dev_path_video.length()) &&
> + strncmp(path, dev_path_media.c_str(), dev_path_media.length()))
> + return fd;
> +
> + /* Set trace options if this is the first call to libtracer. */
> + if (!options_are_set())
> + set_options();
> +
> + add_device(fd, path);
> +
> + json_object *open_obj = json_object_new_object();
> + json_object_object_add(open_obj, "syscall_str", json_object_new_string("open64"));
> + json_object_object_add(open_obj, "syscall", json_object_new_int(LIBTRACER_SYSCALL_OPEN64));
> + json_object_object_add(open_obj, "fd", json_object_new_int(fd));
> + json_object *open_args = trace_open(path, oflag, mode);
> + json_object_object_add(open_obj, "open_args", open_args);
> +
> + write_json_object_to_json_file(open_obj);
> + json_object_put(open_obj);
> +
> + return fd;
> +}
> +
> +void *mmap64(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
> +{
> + errno = 0;
> +
> + void *(*original_mmap64)(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
> + original_mmap64 = (void*(*)(void*, size_t, int, int, int, off_t)) dlsym(RTLD_NEXT, "mmap64");
> + void *buf_address_pointer = (*original_mmap64)(addr, len, prot, flags, fildes, off);
> +
> + /* Only trace mmap64 calls for output or capture buffers. */
> + __u32 type = get_buffer_type_trace(fildes);
> + if (!type)
> + return buf_address_pointer;
> +
> + /* Save the buffer address for future reference. */
> + set_buffer_address_trace(fildes, (long int) buf_address_pointer);
> +
> + json_object *mmap_obj = json_object_new_object();
> + if (errno) {
> + json_object_object_add(mmap_obj, "errno", json_object_new_int(errno));
> + json_object_object_add(mmap_obj, "errno_str", json_object_new_string(strerror(errno)));
> + }
> + json_object_object_add(mmap_obj, "syscall_str", json_object_new_string("mmap64"));
> + json_object_object_add(mmap_obj, "syscall", json_object_new_int(LIBTRACER_SYSCALL_MMAP64));
> +
> + json_object *mmap64_args = trace_mmap64(addr, len, prot, flags, fildes, off);
> + json_object_object_add(mmap_obj, "mmap64_args", mmap64_args);
> +
> + json_object_object_add(mmap_obj, "buffer_address", json_object_new_int64((long int) buf_address_pointer));
> + write_json_object_to_json_file(mmap_obj);
> + json_object_put(mmap_obj);
> +
> + /*
> + * The capture buffer is traced for the first time here when the buffer is first mapped.
> + * Subsequently, the capture buffer will be traced when VIDIOC_DQBUF is called.
> + */
> + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + trace_mem(fildes);
> +
> + return buf_address_pointer;
> +}
> +
> +int munmap(void *start, size_t length)
> +{
> + errno = 0;
> +
> + int(*original_munmap)(void *start, size_t length);
> + original_munmap = (int(*)(void *, size_t)) dlsym(RTLD_NEXT, "munmap");
> + int ret = (*original_munmap)(start, length);
> +
> + /* Only trace the unmapping if the original mapping was traced. */
> + if (!buffer_is_mapped((long int) start))
> + return ret;
> +
> + json_object *munmap_obj = json_object_new_object();
> + json_object_object_add(munmap_obj, "syscall_str", json_object_new_string("munmap"));
> + json_object_object_add(munmap_obj, "syscall", json_object_new_int(LIBTRACER_SYSCALL_MUNMAP));
> +
> + if (errno) {
> + json_object_object_add(munmap_obj, "errno", json_object_new_int(errno));
> + json_object_object_add(munmap_obj, "errno_str", json_object_new_string(strerror(errno)));
> + }
> +
> + json_object *munmap_args = json_object_new_object();
> + json_object_object_add(munmap_args, "start", json_object_new_int64((int64_t)start));
> + json_object_object_add(munmap_args, "length", json_object_new_uint64(length));
> + json_object_object_add(munmap_obj, "munmap_args", munmap_args);
> +
> + write_json_object_to_json_file(munmap_obj);
> + json_object_put(munmap_obj);
> +
> + return ret;
> +}
> +
> +int ioctl(int fd, unsigned long int request, ...)
> +{
> + errno = 0;
> +
> + va_list ap;
> + va_start(ap, request);
> + void *arg = va_arg(ap, void *);
> + va_end(ap);
> +
> + int (*original_ioctl)(int fd, unsigned long int request, ...);
> + original_ioctl = (int (*)(int, long unsigned int, ...)) dlsym(RTLD_NEXT, "ioctl");
> +
> + /* If the ioctl is queuing an output buffer, trace the output buffer before tracing the ioctl. */
> + if ((request == VIDIOC_QBUF) && (_IOC_TYPE(request) == 'V')) {
> + struct v4l2_buffer *buf = static_cast<struct v4l2_buffer*>(arg);
> + if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> + int buf_fd = get_buffer_fd_trace(buf->type, buf->index);
> + if (buf_fd) {
> + set_buffer_bytesused_trace(buf_fd, buf->m.planes[0].bytesused);
> + trace_mem(buf_fd);
> + }
> + }
> + }
> +
> + json_object *ioctl_obj = json_object_new_object();
> + json_object_object_add(ioctl_obj, "syscall_str", json_object_new_string("ioctl"));
> + json_object_object_add(ioctl_obj, "syscall", json_object_new_int(LIBTRACER_SYSCALL_IOCTL));
> + json_object_object_add(ioctl_obj, "fd", json_object_new_int(fd));
> + json_object_object_add(ioctl_obj, "request", json_object_new_uint64(request));
> + json_object_object_add(ioctl_obj, "request_str", json_object_new_string(get_ioctl_request_str(request).c_str()));
> +
> + /* Trace the ioctl arguments provided by userspace if relevant. */
> + json_object *ioctl_args_userspace = trace_ioctl_args(fd, request, arg);
> + if (json_object_object_length(ioctl_args_userspace))
> + json_object_object_add(ioctl_obj, "ioctl_args_from_userspace", ioctl_args_userspace);
> +
> + /* Make the original ioctl call. */
> + int ret = (*original_ioctl)(fd, request, arg);
> +
> + if (errno) {
> + json_object_object_add(ioctl_obj, "errno", json_object_new_int(errno));
> + json_object_object_add(ioctl_obj, "errno_str", json_object_new_string(strerror(errno)));
> + }
> +
> + /* Also trace the ioctl arguments as modified by the driver if relevant. */
> + json_object *ioctl_args_driver = trace_ioctl_args(fd, request, arg, false);
> + if (json_object_object_length(ioctl_args_driver))
> + json_object_object_add(ioctl_obj, "ioctl_args_from_driver", ioctl_args_driver);
> +
> + write_json_object_to_json_file(ioctl_obj);
> + json_object_put(ioctl_obj);
> +
> + /* If the ioctl is exporting a buffer, store the buffer file descriptor and index for future access. */
> + if ((request == VIDIOC_EXPBUF) && (_IOC_TYPE(request) == 'V')) {
> + struct v4l2_exportbuffer *export_buffer = static_cast<struct v4l2_exportbuffer*>(arg);
> + add_buffer_trace(export_buffer->fd, export_buffer->type, export_buffer->index);
> + }
> +
> + /* If the ioctl is dequeuing a capture buffer, trace the buffer. */
> + if ((request == VIDIOC_DQBUF) && (_IOC_TYPE(request) == 'V')) {
> + struct v4l2_buffer *buf = static_cast<struct v4l2_buffer*>(arg);
> + if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> + int buf_fd = get_buffer_fd_trace(buf->type, buf->index);
> + if (buf_fd) {
> + /* TODO tracer only works for decoded formats with one plane e.g. V4L2_PIX_FMT_NV12 */
> + set_buffer_bytesused_trace(buf_fd, buf->m.planes[0].bytesused);
> + trace_mem(buf_fd);
> + }
> + }
> + }
> +
> + return ret;
> +}
> +
> +int close(int fd)
> +{
> + std::string path = get_device(fd);
> +
> + /* Only trace the close if a corresponding open was also traced. */
> + if (!path.empty()) {
> + json_object *close_obj = json_object_new_object();
> + json_object_object_add(close_obj, "syscall_str", json_object_new_string("close"));
> + json_object_object_add(close_obj, "syscall", json_object_new_int(LIBTRACER_SYSCALL_CLOSE));
> + json_object_object_add(close_obj, "fd", json_object_new_int(fd));
> + json_object_object_add(close_obj, "path", json_object_new_string(path.c_str()));
> + write_json_object_to_json_file(close_obj);
> + json_object_put(close_obj);
> + remove_device(fd);
> + }
> +
> + int (*original_close)(int fd);
> + original_close = (int (*)(int)) dlsym(RTLD_NEXT, "close");
> +
> + return (*original_close)(fd);
> +}
> diff --git a/utils/tracer/libtracer.h b/utils/tracer/libtracer.h
> new file mode 100644
> index 00000000..5f86aadf
> --- /dev/null
> +++ b/utils/tracer/libtracer.h
> @@ -0,0 +1,92 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright 2022 Collabora Ltd.
> + */
> +
> +#ifndef LIBTRACER_H
> +#define LIBTRACER_H
> +
> +#include <stdio.h>
> +#include <stdarg.h>
> +#include <dlfcn.h>
> +#include <stdlib.h>
> +#include <fcntl.h>
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +#include <config.h>
> +#include <pthread.h>
> +#include <unistd.h> /* needed for close */
> +#include <linux/dma-buf.h>
> +#include <json-c/json.h>
> +#include <v4l2-info.h>
> +#include <unordered_map>
> +#include <list>
> +#include "trace-info.h"
> +
> +struct trace_options {
> + bool set;
> + bool pretty_print_all;
> + bool pretty_print_mem;
> + bool decoded_data_to_json;
> + bool create_yuv_file;
> +};
> +
> +struct buffer_trace {
> + int fd;
> + __u32 type; /* enum v4l2_buf_type */
> + __u32 index;
> + long address;
> + __u32 bytesused;
> +};
> +
> +struct trace_context {
> + FILE *trace_file;
> + std::string trace_filename;
> + pthread_mutex_t lock;
> + /* Key is the file descriptor, value is the path of the video or media device. */
> + std::unordered_map<int, std::string> devices;
> + /* List of output and capture buffers being traced. */
> + std::list<struct buffer_trace> buffers;
> +};
> +
> +bool options_are_set(void);
> +void set_options(void);
> +bool pretty_print_mem(void);
> +bool pretty_print_all(void);
> +bool write_decoded_data_to_json(void);
> +bool create_yuv_file(void);
> +
> +void add_device(int fd, std::string path);
> +std::string get_device(int fd);
> +int remove_device(int fd);
> +void add_buffer_trace(int fd, __u32 type, __u32 index);
> +int get_buffer_fd_trace(__u32 type, __u32 index);
> +__u32 get_buffer_type_trace(int fd);
> +int get_buffer_index_trace(int fd);
> +void set_buffer_address_trace(int fd, long address);
> +long get_buffer_address_trace(int fd);
> +void set_buffer_bytesused_trace(int fd, __u32 bytesused);
> +long get_buffer_bytesused_trace(int fd);
> +bool buffer_is_mapped(long buffer_address);
> +void write_json_object_to_json_file(json_object *jobj, int flags = JSON_C_TO_STRING_PLAIN);
> +
> +json_object *trace_open(const char *path, int oflag, mode_t mode);
> +json_object *trace_mmap64(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
> +void trace_mem(int fd);
> +std::string get_ioctl_request_str(unsigned long request);
> +json_object *trace_ioctl_args(int fd, unsigned long request, void *arg, bool from_userspace = true);
> +
> +void trace_v4l2_ctrl_h264_decode_mode(struct v4l2_ext_control ctrl, json_object *ctrl_obj);
> +void trace_v4l2_ctrl_h264_start_code(struct v4l2_ext_control ctrl, json_object *ctrl_obj);
> +void trace_v4l2_ctrl_h264_sps(void *p_h264_sps, json_object *ctrl_obj);
> +void trace_v4l2_ctrl_h264_pps(void *p_h264_pps, json_object *ctrl_obj);
> +void trace_v4l2_ctrl_h264_scaling_matrix(void *p_h264_scaling_matrix, json_object *ctrl_obj);
> +void trace_v4l2_ctrl_h264_pred_weights(void *p_h264_pred_weights, json_object *ctrl_obj);
> +void trace_v4l2_ctrl_h264_slice_params(void *p_h264_slice_params, json_object *ctrl_obj);
> +void trace_v4l2_ctrl_h264_decode_params(void *p_h264_decode_params, json_object *ctrl_obj);
> +
> +void trace_v4l2_ctrl_vp8_frame(void *p_vp8_frame, json_object *ctrl_obj);
> +
> +void trace_v4l2_ctrl_fwht_params(void *p_fwht_params, json_object *ctrl_obj);
> +
> +#endif
> diff --git a/utils/tracer/trace-helper.cpp b/utils/tracer/trace-helper.cpp
> new file mode 100644
> index 00000000..402c602b
> --- /dev/null
> +++ b/utils/tracer/trace-helper.cpp
> @@ -0,0 +1,218 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright 2022 Collabora Ltd.
> + */
> +
> +#include "libtracer.h"
> +
> +struct trace_options options;
> +
> +struct trace_context ctx_trace = {
> + .lock = PTHREAD_MUTEX_INITIALIZER
> +};
> +
> +bool options_are_set(void)
> +{
> + return options.set;
> +}
> +
> +void set_options(void)
> +{
> + options.pretty_print_mem = getenv("TRACE_OPTION_PRETTY_PRINT_MEM") ? true : false;
> + options.pretty_print_all = getenv("TRACE_OPTION_PRETTY_PRINT_ALL") ? true : false;
> + options.decoded_data_to_json = getenv("TRACE_OPTION_DECODED_TO_JSON") ? true : false;
> + options.create_yuv_file = getenv("TRACE_OPTION_CREATE_YUV_FILE") ? true : false;
> + options.set = true;
> +}
> +
> +bool pretty_print_mem(void)
> +{
> + return options.pretty_print_mem;
> +}
> +
> +bool pretty_print_all(void)
> +{
> + return options.pretty_print_all;
> +}
> +
> +bool write_decoded_data_to_json(void)
> +{
> + return options.decoded_data_to_json;
> +}
> +
> +bool create_yuv_file(void)
> +{
> + return options.create_yuv_file;
> +}
> +
> +void add_device(int fd, std::string path)
> +{
> + std::pair<int, std::string> new_pair = std::make_pair(fd, path);
> + pthread_mutex_lock(&ctx_trace.lock);
> + ctx_trace.devices.insert(new_pair);
> + pthread_mutex_unlock(&ctx_trace.lock);
> +}
> +
> +std::string get_device(int fd)
> +{
> + std::string path;
> + std::unordered_map<int, std::string>::const_iterator it;
> + pthread_mutex_lock(&ctx_trace.lock);
> + it = ctx_trace.devices.find(fd);
> + if (it != ctx_trace.devices.end())
> + path = it->second;
> + pthread_mutex_unlock(&ctx_trace.lock);
> + return path;
> +}
> +
> +int remove_device(int fd)
> +{
> + int ret = 0;
> + pthread_mutex_lock(&ctx_trace.lock);
> + ret = ctx_trace.devices.erase(fd);
> + pthread_mutex_unlock(&ctx_trace.lock);
> + return ret;
> +}
> +
> +void add_buffer_trace(int fd, __u32 type, __u32 index)
> +{
> + struct buffer_trace buf;
> + memset(&buf, 0, sizeof(buffer_trace));
> + buf.fd = fd;
> + buf.type = type;
> + buf.index = index;
> + pthread_mutex_lock(&ctx_trace.lock);
> + ctx_trace.buffers.push_front(buf);
> + pthread_mutex_unlock(&ctx_trace.lock);
> +}
> +
> +int get_buffer_fd_trace(__u32 type, __u32 index)
> +{
> + int fd = 0;
> + pthread_mutex_lock(&ctx_trace.lock);
> + for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
> + if ((it->type == type) && (it->index == index)) {
> + fd = it->fd;
> + break;
> + }
> + }
> + pthread_mutex_unlock(&ctx_trace.lock);
> + return fd;
> +}
> +
> +__u32 get_buffer_type_trace(int fd)
> +{
> + __u32 ret = 0;
> + pthread_mutex_lock(&ctx_trace.lock);
> + for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
> + if (it->fd == fd) {
> + ret = it->type;
> + break;
> + }
> + }
> + pthread_mutex_unlock(&ctx_trace.lock);
> + return ret;
> +}
> +
> +int get_buffer_index_trace(int fd)
> +{
> + int index = -1;
> + pthread_mutex_lock(&ctx_trace.lock);
> + for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
> + if (it->fd == fd) {
> + index = it->index;
> + break;
> + }
> + }
> + pthread_mutex_unlock(&ctx_trace.lock);
> + return index;
> +}
> +
> +void set_buffer_address_trace(int fd, long address)
> +{
> + pthread_mutex_lock(&ctx_trace.lock);
> + for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
> + if (it->fd == fd) {
> + it->address = address;
> + break;
> + }
> + }
> + pthread_mutex_unlock(&ctx_trace.lock);
> +}
> +
> +long get_buffer_address_trace(int fd)
> +{
> + long int address = 0;
> + pthread_mutex_lock(&ctx_trace.lock);
> + for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
> + if (it->fd == fd) {
> + address = it->address;
> + break;
> + }
> + }
> + pthread_mutex_unlock(&ctx_trace.lock);
> + return address;
> +}
> +
> +void set_buffer_bytesused_trace(int fd, __u32 bytesused)
> +{
> + pthread_mutex_lock(&ctx_trace.lock);
> + for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
> + if (it->fd == fd) {
> + it->bytesused = bytesused;
> + break;
> + }
> + }
> + pthread_mutex_unlock(&ctx_trace.lock);
> +}
> +
> +long get_buffer_bytesused_trace(int fd)
> +{
> + long int bytesused = -1;
> +
> + pthread_mutex_lock(&ctx_trace.lock);
> + for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
> + if (it->fd == fd) {
> + bytesused = it->bytesused;
> + break;
> + }
> + }
> + pthread_mutex_unlock(&ctx_trace.lock);
> +
> + return bytesused;
> +}
> +
> +/* Returns true if the memory address is a mapped buffer address, false otherwise. */
> +bool buffer_is_mapped(long buffer_address)
> +{
> + bool ret = false;
> + pthread_mutex_lock(&ctx_trace.lock);
> + for (auto it = ctx_trace.buffers.cbegin(); it != ctx_trace.buffers.cend(); ++it) {
> + if (it->address == buffer_address) {
> + ret = true;
> + break;
> + }
> + }
> + pthread_mutex_unlock(&ctx_trace.lock);
> + return ret;
> +}
> +
> +void write_json_object_to_json_file(json_object *jobj, int flags)
> +{
> + if (pretty_print_all())
> + flags = JSON_C_TO_STRING_PRETTY;
> +
> + std::string json_str = json_object_to_json_string_ext(jobj, flags);
> + pthread_mutex_lock(&ctx_trace.lock);
> +
> + if (ctx_trace.trace_filename.empty()) {
> + ctx_trace.trace_filename = getenv("TRACE_ID");
> + ctx_trace.trace_filename += ".json";
> + ctx_trace.trace_file = fopen(ctx_trace.trace_filename.c_str(), "a");
> + }
> +
> + fwrite(json_str.c_str(), sizeof(char), json_str.length(), ctx_trace.trace_file);
> + fputs(",\n", ctx_trace.trace_file);
> + fflush(ctx_trace.trace_file);
> + pthread_mutex_unlock(&ctx_trace.lock);
> +}
> diff --git a/utils/tracer/trace-info.cpp b/utils/tracer/trace-info.cpp
> new file mode 100644
> index 00000000..a1d58c64
> --- /dev/null
> +++ b/utils/tracer/trace-info.cpp
> @@ -0,0 +1,358 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright 2022 Collabora Ltd.
> + */
> +
> +#include <v4l2-info.h>
> +#include "trace-info.h"
> +
> +struct definition {
> + unsigned long val;
> + const char *str;
> +};
> +
> +static std::string val2s(unsigned long val, const definition *def)
> +{
> + std::string s;
> +
> + if (val == 0)
> + return s;
> +
> + while ((def->val) && (def->val != val))
> + def++;
> +
> + if (def->val == val)
> + s = def->str;
> +
> + return s;
> +}
> +
> +std::string ioctl2s_video(unsigned long request)
> +{
> + static constexpr definition defs[] = {
> + { VIDIOC_QUERYCAP, "VIDIOC_QUERYCAP" },
> + { VIDIOC_ENUM_FMT, "VIDIOC_ENUM_FMT" },
> + { VIDIOC_G_FMT, "VIDIOC_G_FMT" },
> + { VIDIOC_S_FMT, "VIDIOC_S_FMT" },
> + { VIDIOC_REQBUFS, "VIDIOC_REQBUFS" },
> + { VIDIOC_QUERYBUF, "VIDIOC_QUERYBUF" },
> + { VIDIOC_G_FBUF, "VIDIOC_G_FBUF" },
> + { VIDIOC_S_FBUF, "VIDIOC_S_FBUF" },
> + { VIDIOC_OVERLAY, "VIDIOC_OVERLAY" },
> + { VIDIOC_QBUF, "VIDIOC_QBUF" },
> + { VIDIOC_EXPBUF, "VIDIOC_EXPBUF" },
> + { VIDIOC_DQBUF, "VIDIOC_DQBUF" },
> + { VIDIOC_STREAMON, "VIDIOC_STREAMON" },
> + { VIDIOC_STREAMOFF, "VIDIOC_STREAMOFF" },
> + { VIDIOC_G_PARM, "VIDIOC_G_PARM" },
> + { VIDIOC_S_PARM, "VIDIOC_S_PARM" },
> + { VIDIOC_G_STD, "VIDIOC_G_STD" },
> + { VIDIOC_S_STD, "VIDIOC_S_STD" },
> + { VIDIOC_ENUMSTD, "VIDIOC_ENUMSTD" },
> + { VIDIOC_ENUMINPUT, "VIDIOC_ENUMINPUT" },
> + { VIDIOC_G_CTRL, "VIDIOC_G_CTRL" },
> + { VIDIOC_S_CTRL, "VIDIOC_S_CTRL" },
> + { VIDIOC_G_TUNER, "VIDIOC_G_TUNER" },
> + { VIDIOC_S_TUNER, "VIDIOC_S_TUNER" },
> + { VIDIOC_G_AUDIO, "VIDIOC_G_AUDIO" },
> + { VIDIOC_S_AUDIO, "VIDIOC_S_AUDIO" },
> + { VIDIOC_QUERYCTRL, "VIDIOC_QUERYCTRL" },
> + { VIDIOC_QUERYMENU, "VIDIOC_QUERYMENU" },
> + { VIDIOC_G_INPUT, "VIDIOC_G_INPUT" },
> + { VIDIOC_S_INPUT, "VIDIOC_S_INPUT" },
> + { VIDIOC_G_EDID, "VIDIOC_G_EDID" },
> + { VIDIOC_S_EDID, "VIDIOC_S_EDID" },
> + { VIDIOC_G_OUTPUT, "VIDIOC_G_OUTPUT" },
> + { VIDIOC_S_OUTPUT, "VIDIOC_S_OUTPUT" },
> + { VIDIOC_ENUMOUTPUT, "VIDIOC_ENUMOUTPUT" },
> + { VIDIOC_G_AUDOUT, "VIDIOC_G_AUDOUT" },
> + { VIDIOC_S_AUDOUT, "VIDIOC_S_AUDOUT" },
> + { VIDIOC_G_MODULATOR, "VIDIOC_G_MODULATOR" },
> + { VIDIOC_S_MODULATOR, "VIDIOC_S_MODULATOR" },
> + { VIDIOC_G_FREQUENCY, "VIDIOC_G_FREQUENCY" },
> + { VIDIOC_S_FREQUENCY, "VIDIOC_S_FREQUENCY" },
> + { VIDIOC_CROPCAP, "VIDIOC_CROPCAP" },
> + { VIDIOC_G_CROP, "VIDIOC_G_CROP" },
> + { VIDIOC_S_CROP, "VIDIOC_S_CROP" },
> + { VIDIOC_G_JPEGCOMP, "VIDIOC_G_JPEGCOMP" },
> + { VIDIOC_S_JPEGCOMP, "VIDIOC_S_JPEGCOMP" },
> + { VIDIOC_QUERYSTD, "VIDIOC_QUERYSTD" },
> + { VIDIOC_TRY_FMT, "VIDIOC_TRY_FMT" },
> + { VIDIOC_ENUMAUDIO, "VIDIOC_ENUMAUDIO" },
> + { VIDIOC_ENUMAUDOUT, "VIDIOC_ENUMAUDOUT" },
> + { VIDIOC_G_PRIORITY, "VIDIOC_G_PRIORITY" },
> + { VIDIOC_S_PRIORITY, "VIDIOC_S_PRIORITY" },
> + { VIDIOC_G_SLICED_VBI_CAP, "VIDIOC_G_SLICED_VBI_CAP" },
> + { VIDIOC_LOG_STATUS, "VIDIOC_LOG_STATUS" },
> + { VIDIOC_G_EXT_CTRLS, "VIDIOC_G_EXT_CTRLS" },
> + { VIDIOC_S_EXT_CTRLS, "VIDIOC_S_EXT_CTRLS" },
> + { VIDIOC_TRY_EXT_CTRLS, "VIDIOC_TRY_EXT_CTRLS" },
> + { VIDIOC_ENUM_FRAMESIZES, "VIDIOC_ENUM_FRAMESIZES" },
> + { VIDIOC_ENUM_FRAMEINTERVALS, "VIDIOC_ENUM_FRAMEINTERVALS" },
> + { VIDIOC_G_ENC_INDEX, "VIDIOC_G_ENC_INDEX" },
> + { VIDIOC_ENCODER_CMD, "VIDIOC_ENCODER_CMD" },
> + { VIDIOC_TRY_ENCODER_CMD, "VIDIOC_TRY_ENCODER_CMD" },
> + { VIDIOC_DBG_S_REGISTER, "VIDIOC_DBG_S_REGISTER" },
> + { VIDIOC_DBG_G_REGISTER, "VIDIOC_DBG_G_REGISTER" },
> + { VIDIOC_S_HW_FREQ_SEEK, "VIDIOC_S_HW_FREQ_SEEK" },
> + { VIDIOC_S_DV_TIMINGS, "VIDIOC_S_DV_TIMINGS" },
> + { VIDIOC_G_DV_TIMINGS, "VIDIOC_G_DV_TIMINGS" },
> + { VIDIOC_DQEVENT, "VIDIOC_DQEVENT" },
> + { VIDIOC_SUBSCRIBE_EVENT, "VIDIOC_SUBSCRIBE_EVENT" },
> + { VIDIOC_UNSUBSCRIBE_EVENT, "VIDIOC_UNSUBSCRIBE_EVENT" },
> + { VIDIOC_CREATE_BUFS, "VIDIOC_CREATE_BUFS" },
> + { VIDIOC_PREPARE_BUF, "VIDIOC_PREPARE_BUF" },
> + { VIDIOC_G_SELECTION, "VIDIOC_G_SELECTION" },
> + { VIDIOC_S_SELECTION, "VIDIOC_S_SELECTION" },
> + { VIDIOC_DECODER_CMD, "VIDIOC_DECODER_CMD" },
> + { VIDIOC_TRY_DECODER_CMD, "VIDIOC_TRY_DECODER_CMD" },
> + { VIDIOC_ENUM_DV_TIMINGS, "VIDIOC_ENUM_DV_TIMINGS" },
> + { VIDIOC_QUERY_DV_TIMINGS, "VIDIOC_QUERY_DV_TIMINGS" },
> + { VIDIOC_DV_TIMINGS_CAP, "VIDIOC_DV_TIMINGS_CAP" },
> + { VIDIOC_ENUM_FREQ_BANDS, "VIDIOC_ENUM_FREQ_BANDS" },
> + { VIDIOC_DBG_G_CHIP_INFO, "VIDIOC_DBG_G_CHIP_INFO" },
> + { VIDIOC_QUERY_EXT_CTRL, "VIDIOC_QUERY_EXT_CTRL" },
> + { BASE_VIDIOC_PRIVATE, "BASE_VIDIOC_PRIVATE" },
> + { 0, nullptr }
> + };
> + return val2s(request, defs);
> +}
> +
> +std::string ioctl2s_media(unsigned long request)
> +{
> + static constexpr definition defs[] = {
> + { MEDIA_IOC_DEVICE_INFO, "MEDIA_IOC_DEVICE_INFO" },
> + { MEDIA_IOC_ENUM_ENTITIES, "MEDIA_IOC_ENUM_ENTITIES" },
> + { MEDIA_IOC_ENUM_LINKS, "MEDIA_IOC_ENUM_LINKS" },
> + { MEDIA_IOC_SETUP_LINK, "MEDIA_IOC_SETUP_LINK" },
> + { MEDIA_IOC_G_TOPOLOGY, "MEDIA_IOC_G_TOPOLOGY" },
> + { MEDIA_IOC_REQUEST_ALLOC, "MEDIA_IOC_REQUEST_ALLOC" },
> + { MEDIA_REQUEST_IOC_QUEUE, "MEDIA_REQUEST_IOC_QUEUE" },
> + { MEDIA_REQUEST_IOC_REINIT, "MEDIA_REQUEST_IOC_REINIT" },
> + { 0, nullptr }
> + };
> + return val2s(request, defs);
> +}
> +
> +std::string ctrltype2s(__u32 val)
> +{
> + static constexpr definition defs[] = {
> + { V4L2_CTRL_TYPE_INTEGER, "V4L2_CTRL_TYPE_INTEGER" },
> + { V4L2_CTRL_TYPE_BOOLEAN, "V4L2_CTRL_TYPE_BOOLEAN" },
> + { V4L2_CTRL_TYPE_MENU, "V4L2_CTRL_TYPE_MENU" },
> + { V4L2_CTRL_TYPE_BUTTON, "V4L2_CTRL_TYPE_BUTTON" },
> + { V4L2_CTRL_TYPE_INTEGER64, "V4L2_CTRL_TYPE_INTEGER64" },
> + { V4L2_CTRL_TYPE_CTRL_CLASS, "V4L2_CTRL_TYPE_CTRL_CLASS" },
> + { V4L2_CTRL_TYPE_STRING, "V4L2_CTRL_TYPE_STRING" },
> + { V4L2_CTRL_TYPE_BITMASK, "V4L2_CTRL_TYPE_BITMASK" },
> + { V4L2_CTRL_TYPE_INTEGER_MENU, "V4L2_CTRL_TYPE_INTEGER_MENU" },
> + { V4L2_CTRL_COMPOUND_TYPES, "V4L2_CTRL_COMPOUND_TYPES" },
> + { V4L2_CTRL_TYPE_U8, "V4L2_CTRL_TYPE_U8" },
> + { V4L2_CTRL_TYPE_U16, "V4L2_CTRL_TYPE_U16" },
> + { V4L2_CTRL_TYPE_U32, "V4L2_CTRL_TYPE_U32" },
> + { V4L2_CTRL_TYPE_AREA, "V4L2_CTRL_TYPE_AREA" },
> + { V4L2_CTRL_TYPE_HDR10_CLL_INFO, "V4L2_CTRL_TYPE_HDR10_CLL_INFO" },
> + { V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY, "V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY" },
> + { V4L2_CTRL_TYPE_H264_SPS, "V4L2_CTRL_TYPE_H264_SPS" },
> + { V4L2_CTRL_TYPE_H264_PPS, "V4L2_CTRL_TYPE_H264_PPS" },
> + { V4L2_CTRL_TYPE_H264_SCALING_MATRIX, "V4L2_CTRL_TYPE_H264_SCALING_MATRIX" },
> + { V4L2_CTRL_TYPE_H264_SLICE_PARAMS, "V4L2_CTRL_TYPE_H264_SLICE_PARAMS" },
> + { V4L2_CTRL_TYPE_H264_DECODE_PARAMS, "V4L2_CTRL_TYPE_H264_DECODE_PARAMS" },
> + { V4L2_CTRL_TYPE_H264_PRED_WEIGHTS, "V4L2_CTRL_TYPE_H264_PRED_WEIGHTS" },
> + { V4L2_CTRL_TYPE_FWHT_PARAMS, "V4L2_CTRL_TYPE_FWHT_PARAMS" },
> + { V4L2_CTRL_TYPE_VP8_FRAME, "V4L2_CTRL_TYPE_VP8_FRAME" },
> + { V4L2_CTRL_TYPE_MPEG2_QUANTISATION, "V4L2_CTRL_TYPE_MPEG2_QUANTISATION" },
> + { V4L2_CTRL_TYPE_MPEG2_SEQUENCE, "V4L2_CTRL_TYPE_MPEG2_SEQUENCE" },
> + { V4L2_CTRL_TYPE_MPEG2_PICTURE, "V4L2_CTRL_TYPE_MPEG2_PICTURE" },
> + { V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR, "V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR" },
> + { V4L2_CTRL_TYPE_VP9_FRAME, "V4L2_CTRL_TYPE_VP9_FRAME" },
> + { 0, nullptr }
> + };
> + return val2s(val, defs);
> +}
> +
> +std::string capflag2s(unsigned long cap)
> +{
> + static constexpr flag_def def[] = {
> + { V4L2_CAP_VIDEO_M2M_MPLANE, "V4L2_CAP_VIDEO_M2M_MPLANE" },
> + { V4L2_CAP_VIDEO_CAPTURE, "V4L2_CAP_VIDEO_CAPTURE" },
> + { V4L2_CAP_VIDEO_OUTPUT, "V4L2_CAP_VIDEO_OUTPUT" },
> + { V4L2_CAP_VIDEO_OVERLAY, "V4L2_CAP_VIDEO_OVERLAY" },
> + { V4L2_CAP_VBI_CAPTURE, "V4L2_CAP_VBI_CAPTURE" },
> + { V4L2_CAP_VBI_OUTPUT, "V4L2_CAP_VBI_OUTPUT" },
> + { V4L2_CAP_SLICED_VBI_CAPTURE, "V4L2_CAP_SLICED_VBI_CAPTURE" },
> + { V4L2_CAP_SLICED_VBI_OUTPUT, "V4L2_CAP_SLICED_VBI_OUTPUT" },
> + { V4L2_CAP_RDS_CAPTURE, "V4L2_CAP_RDS_CAPTURE" },
> + { V4L2_CAP_VIDEO_OUTPUT_OVERLAY, "V4L2_CAP_VIDEO_OUTPUT_OVERLAY" },
> + { V4L2_CAP_HW_FREQ_SEEK, "V4L2_CAP_HW_FREQ_SEEK" },
> + { V4L2_CAP_RDS_OUTPUT, "V4L2_CAP_RDS_OUTPUT" },
> + { V4L2_CAP_VIDEO_CAPTURE_MPLANE, "V4L2_CAP_VIDEO_CAPTURE_MPLANE" },
> + { V4L2_CAP_VIDEO_OUTPUT_MPLANE, "V4L2_CAP_VIDEO_OUTPUT_MPLANE" },
> + { V4L2_CAP_VIDEO_M2M_MPLANE, "V4L2_CAP_VIDEO_M2M_MPLANE" },
> + { V4L2_CAP_VIDEO_M2M, "V4L2_CAP_VIDEO_M2M" },
> + { V4L2_CAP_TUNER, "V4L2_CAP_TUNER" },
> + { V4L2_CAP_AUDIO, "V4L2_CAP_AUDIO" },
> + { V4L2_CAP_RADIO, "V4L2_CAP_RADIO" },
> + { V4L2_CAP_MODULATOR, "V4L2_CAP_MODULATOR" },
> + { V4L2_CAP_SDR_CAPTURE, "V4L2_CAP_SDR_CAPTURE" },
> + { V4L2_CAP_EXT_PIX_FORMAT, "V4L2_CAP_EXT_PIX_FORMAT" },
> + { V4L2_CAP_SDR_OUTPUT, "V4L2_CAP_SDR_OUTPUT" },
> + { V4L2_CAP_META_CAPTURE, "V4L2_CAP_META_CAPTURE" },
> + { V4L2_CAP_READWRITE, "V4L2_CAP_READWRITE" },
> + { V4L2_CAP_ASYNCIO, "V4L2_CAP_ASYNCIO" },
> + { V4L2_CAP_STREAMING, "V4L2_CAP_STREAMING" },
> + { V4L2_CAP_META_OUTPUT, "V4L2_CAP_META_OUTPUT" },
> + { V4L2_CAP_TOUCH, "V4L2_CAP_TOUCH" },
> + { V4L2_CAP_IO_MC, "V4L2_CAP_IO_MC" },
> + { V4L2_CAP_DEVICE_CAPS, "V4L2_CAP_DEVICE_CAPS" },
> + { 0, nullptr }
> + };
> + return flags2s(cap, def);
> +}
> +
> +std::string bufsyncflag2s(unsigned long flag)
> +{
> + static constexpr flag_def def[] = {
> + { DMA_BUF_SYNC_READ , "DMA_BUF_SYNC_READ " },
> + { DMA_BUF_SYNC_WRITE, "DMA_BUF_SYNC_WRITE" },
> + { DMA_BUF_SYNC_RW , "DMA_BUF_SYNC_RW " },
> + { DMA_BUF_SYNC_START , "DMA_BUF_SYNC_START" },
> + { DMA_BUF_SYNC_END, "DMA_BUF_SYNC_END" },
> + { 0, nullptr }
> + };
> + return flags2s(flag, def);
> +}
> +
> +std::string vp8_segment_flag2s(unsigned long flag)
> +{
> + static constexpr flag_def def[] = {
> + { V4L2_VP8_SEGMENT_FLAG_ENABLED, "V4L2_VP8_SEGMENT_FLAG_ENABLED" },
> + { V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP, "V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP" },
> + { V4L2_VP8_SEGMENT_FLAG_UPDATE_FEATURE_DATA, "V4L2_VP8_SEGMENT_FLAG_UPDATE_FEATURE_DATA" },
> + { V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE, "V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE" },
> + { 0, nullptr }
> + };
> + return flags2s(flag, def);
> +}
> +
> +std::string vp8_loop_filter_flag2s(unsigned long flag)
> +{
> + static constexpr flag_def def[] = {
> + { V4L2_VP8_LF_ADJ_ENABLE, "V4L2_VP8_LF_ADJ_ENABLE" },
> + { V4L2_VP8_LF_DELTA_UPDATE, "V4L2_VP8_LF_DELTA_UPDATE" },
> + { V4L2_VP8_LF_FILTER_TYPE_SIMPLE, "V4L2_VP8_LF_FILTER_TYPE_SIMPLE" },
> + { 0, nullptr }
> + };
> + return flags2s(flag, def);
> +}
> +
> +std::string vp8_frame_flag2s(unsigned long flag)
> +{
> + static constexpr flag_def def[] = {
> + { V4L2_VP8_FRAME_FLAG_KEY_FRAME, "V4L2_VP8_FRAME_FLAG_KEY_FRAME" },
> + { V4L2_VP8_FRAME_FLAG_EXPERIMENTAL, "V4L2_VP8_FRAME_FLAG_EXPERIMENTAL" },
> + { V4L2_VP8_FRAME_FLAG_SHOW_FRAME, "V4L2_VP8_FRAME_FLAG_SHOW_FRAME" },
> + { V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF, "V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF" },
> + { V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN, "V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN" },
> + { V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT, "V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT" },
> + { 0, nullptr }
> + };
> + return flags2s(flag, def);
> +}
> +
> +std::string ver2s(unsigned int version)
> +{
> + char buf[16];
> + sprintf(buf, "%d.%d.%d", version >> 16, (version >> 8) & 0xff, version & 0xff);
> + return buf;
> +}
> +
> +std::string which2s(unsigned long which)
> +{
> + std::string s = "unknown";
> +
> + switch (which) {
> + case V4L2_CTRL_WHICH_CUR_VAL:
> + s = "V4L2_CTRL_WHICH_CUR_VAL";
> + break;
> + case V4L2_CTRL_WHICH_DEF_VAL:
> + s= "V4L2_CTRL_WHICH_DEF_VAL";
> + break;
> + case V4L2_CTRL_WHICH_REQUEST_VAL:
> + s = "V4L2_CTRL_WHICH_REQUEST_VAL";
> + break;
> + default:
> + break;
> + }
> +
> + return s;
> +}
> +
> +std::string ctrlclass2s(__u32 id)
> +{
> + static constexpr definition defs[] = {
> + { V4L2_CTRL_CLASS_USER, "V4L2_CTRL_CLASS_USER" },
> + { V4L2_CTRL_CLASS_CODEC, "V4L2_CTRL_CLASS_CODEC" },
> + { V4L2_CTRL_CLASS_CAMERA, "V4L2_CTRL_CLASS_CAMERA" },
> + { V4L2_CTRL_CLASS_FM_TX, "V4L2_CTRL_CLASS_FM_TX" },
> + { V4L2_CTRL_CLASS_FLASH, "V4L2_CTRL_CLASS_FLASH" },
> + { V4L2_CTRL_CLASS_JPEG, "V4L2_CTRL_CLASS_JPEG" },
> + { V4L2_CTRL_CLASS_IMAGE_SOURCE, "V4L2_CTRL_CLASS_IMAGE_SOURCE" },
> + { V4L2_CTRL_CLASS_IMAGE_PROC, "V4L2_CTRL_CLASS_IMAGE_PROC" },
> + { V4L2_CTRL_CLASS_DV, "V4L2_CTRL_CLASS_DV" },
> + { V4L2_CTRL_CLASS_FM_RX, "V4L2_CTRL_CLASS_FM_RX" },
> + { V4L2_CTRL_CLASS_RF_TUNER, "V4L2_CTRL_CLASS_RF_TUNER" },
> + { V4L2_CTRL_CLASS_DETECT, "V4L2_CTRL_CLASS_DETECT" },
> + { V4L2_CTRL_CLASS_CODEC_STATELESS, "V4L2_CTRL_CLASS_CODEC_STATELESS" },
> + { V4L2_CTRL_CLASS_COLORIMETRY, "V4L2_CTRL_CLASS_COLORIMETRY" },
> + { 0, nullptr }
> + };
> + return val2s(id & 0xff0000, defs);
> +}
> +
> +std::string request_buffers_flag2s(unsigned int flag)
> +{
> + static constexpr flag_def def[] = {
> + { V4L2_MEMORY_FLAG_NON_COHERENT, "V4L2_MEMORY_FLAG_NON_COHERENT" },
> + { 0, nullptr }
> + };
> + return flags2s(flag, def);
> +}
> +
> +std::string v4l2_memory2s(__u32 id)
> +{
> + static constexpr definition defs[] = {
> + { V4L2_MEMORY_MMAP, "V4L2_MEMORY_MMAP" },
> + { V4L2_MEMORY_USERPTR, "V4L2_MEMORY_USERPTR" },
> + { V4L2_MEMORY_OVERLAY, "V4L2_MEMORY_OVERLAY" },
> + { V4L2_MEMORY_DMABUF, "V4L2_MEMORY_DMABUF" },
> + { 0, nullptr }
> + };
> + return val2s(id, defs);
> +}
> +
> +std::string tc_type2s(__u32 id)
> +{
> + static constexpr definition defs[] = {
> + { V4L2_TC_TYPE_24FPS, "V4L2_TC_TYPE_24FPS" },
> + { V4L2_TC_TYPE_25FPS, "V4L2_TC_TYPE_25FPS" },
> + { V4L2_TC_TYPE_30FPS, "V4L2_TC_TYPE_30FPS" },
> + { V4L2_TC_TYPE_50FPS, "V4L2_TC_TYPE_50FPS" },
> + { V4L2_TC_TYPE_60FPS, "V4L2_TC_TYPE_60FPS" },
> + { 0, nullptr }
> + };
> + return val2s(id, defs);
> +}
> +
> +std::string tc_flag2s(unsigned int flag)
> +{
> + static constexpr flag_def def[] = {
> + { V4L2_TC_FLAG_DROPFRAME, "V4L2_TC_FLAG_DROPFRAME" },
> + { V4L2_TC_FLAG_COLORFRAME, "V4L2_TC_FLAG_COLORFRAME" },
> + { V4L2_TC_USERBITS_field, "V4L2_TC_USERBITS_field" },
> + { V4L2_TC_USERBITS_USERDEFINED, "V4L2_TC_USERBITS_USERDEFINED" },
> + { V4L2_TC_USERBITS_8BITCHARS, "V4L2_TC_USERBITS_8BITCHARS" },
> + { 0, nullptr }
> + };
> + return flags2s(flag, def);
> +}
> diff --git a/utils/tracer/trace-info.h b/utils/tracer/trace-info.h
> new file mode 100644
> index 00000000..4c06190a
> --- /dev/null
> +++ b/utils/tracer/trace-info.h
> @@ -0,0 +1,72 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright 2022 Collabora Ltd.
> + */
> +
> +#ifndef TRACE_INFO_H
> +#define TRACE_INFO_H
> +
> +#include <cstring>
> +#include <linux/videodev2.h>
> +#include <linux/media.h>
> +#include <linux/dma-buf.h>
> +
> +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
> +
> +enum LIBTRACER_SYSCALL {
> + LIBTRACER_SYSCALL_IOCTL,
> + LIBTRACER_SYSCALL_OPEN,
> + LIBTRACER_SYSCALL_OPEN64,
> + LIBTRACER_SYSCALL_CLOSE,
> + LIBTRACER_SYSCALL_MMAP64,
> + LIBTRACER_SYSCALL_MUNMAP,
> + LIBTRACER_SYSCALL_SELECT,
> + LIBTRACER_SYSCALL_POLL,
> +};
> +
> +/* Convert an ioctl request of type 'V' to string. */
> +std::string ioctl2s_video(unsigned long request);
> +
> +/* Convert an ioctl request of type '|' to string. */
> +std::string ioctl2s_media(unsigned long request);
> +
> +/* Convert capability flags to common separated string. */
> +std::string capflag2s(unsigned long cap);
> +
> +/* Convert a decimal number to a string with kernel_version.major_revision.minor_revision. */
> +std::string ver2s(unsigned int version);
> +
> +/* Convert v4l2_ext_controls member "which" to string. */
> +std::string which2s(unsigned long which);
> +
> +/* Convert control class to string. */
> +std::string ctrlclass2s(__u32 id);
> +
> +/* Convert control type to string. */
> +std::string ctrltype2s(__u32 type);
> +
> +/* Convert struct dma_buf_sync flags to string. */
> +std::string bufsyncflag2s(unsigned long flag);
> +
> +/* Convert v4l2_vp8_segment flags to string. */
> +std::string vp8_segment_flag2s(unsigned long flag);
> +
> +/* Convert v4l2_vp8_loop_filter flags to string. */
> +std::string vp8_loop_filter_flag2s(unsigned long flag);
> +
> +/* Convert v4l2_ctrl_vp8_frame flags to string. */
> +std::string vp8_frame_flag2s(unsigned long flag);
> +
> +/* Convert v4l2_requestbuffers flags to string. */
> +std::string request_buffers_flag2s(unsigned int flag);
> +
> +/* Convert enum v4l2_memory type to string. */
> +std::string v4l2_memory2s(__u32 id);
> +
> +/* Convert v4l2_timecode type to string. */
> +std::string tc_type2s(__u32 id);
> +
> +/* Convert v4l2_timecode flags to string. */
> +std::string tc_flag2s(unsigned int flag);
> +
> +#endif
> diff --git a/utils/tracer/trace-vp8.cpp b/utils/tracer/trace-vp8.cpp
> new file mode 100644
> index 00000000..a0a8ccdd
> --- /dev/null
> +++ b/utils/tracer/trace-vp8.cpp
> @@ -0,0 +1,183 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright 2022 Collabora Ltd.
> + */
> +
> +#include "libtracer.h"
> +
> +json_object *trace_v4l2_vp8_segment(struct v4l2_vp8_segment segment)
> +{
> + json_object *vp8_segment_obj = json_object_new_object();
> +
> + /* __s8 quant_update[4] */
> + json_object *quant_update_obj = json_object_new_array();
> + for (size_t i = 0; i < ARRAY_SIZE(segment.quant_update); i++)
> + json_object_array_add(quant_update_obj, json_object_new_int(segment.quant_update[i]));
> + json_object_object_add(vp8_segment_obj, "quant_update", quant_update_obj);
> +
> + /* __s8 lf_update[4] */
> + json_object *lf_update_obj = json_object_new_array();
> + for (size_t i = 0; i < ARRAY_SIZE(segment.lf_update); i++)
> + json_object_array_add(lf_update_obj, json_object_new_int(segment.lf_update[i]));
> + json_object_object_add(vp8_segment_obj, "lf_update", lf_update_obj);
> +
> + /* __u8 segment_probs[3] */
> + json_object *segment_probs_obj = json_object_new_array();
> + for (size_t i = 0; i < ARRAY_SIZE(segment.segment_probs); i++)
> + json_object_array_add(segment_probs_obj, json_object_new_int(segment.segment_probs[i]));
> + json_object_object_add(vp8_segment_obj, "segment_probs", segment_probs_obj);
> +
> + json_object_object_add(vp8_segment_obj, "padding", json_object_new_int(segment.padding));
> + json_object_object_add(vp8_segment_obj, "flags", json_object_new_int(segment.flags));
> + json_object_object_add(vp8_segment_obj, "flags_str",
> + json_object_new_string(vp8_segment_flag2s(segment.flags).c_str()));
> +
> + return vp8_segment_obj;
> +}
> +
> +json_object *trace_v4l2_vp8_loop_filter(struct v4l2_vp8_loop_filter lf)
> +{
> + json_object *vp8_loop_filter_obj = json_object_new_object();
> +
> + /* __s8 ref_frm_delta[4] */
> + json_object *ref_frm_delta_obj = json_object_new_array();
> + for (size_t i = 0; i < ARRAY_SIZE(lf.ref_frm_delta); i++)
> + json_object_array_add(ref_frm_delta_obj, json_object_new_int(lf.ref_frm_delta[i]));
> + json_object_object_add(vp8_loop_filter_obj, "ref_frm_delta", ref_frm_delta_obj);
> +
> + /* __s8 mb_mode_delta[4] */
> + json_object *mb_mode_delta_obj = json_object_new_array();
> + for (size_t i = 0; i < ARRAY_SIZE(lf.mb_mode_delta); i++)
> + json_object_array_add(mb_mode_delta_obj, json_object_new_int(lf.mb_mode_delta[i]));
> + json_object_object_add(vp8_loop_filter_obj, "mb_mode_delta", mb_mode_delta_obj);
> +
> + json_object_object_add(vp8_loop_filter_obj, "sharpness_level", json_object_new_int(lf.sharpness_level));
> + json_object_object_add(vp8_loop_filter_obj, "level", json_object_new_int(lf.level));
> + json_object_object_add(vp8_loop_filter_obj, "padding", json_object_new_int(lf.padding));
> + json_object_object_add(vp8_loop_filter_obj, "flags", json_object_new_int(lf.flags));
> + json_object_object_add(vp8_loop_filter_obj, "flags_str", json_object_new_string(vp8_loop_filter_flag2s(lf.flags).c_str()));
> +
> + return vp8_loop_filter_obj;
> +}
> +
> +json_object *trace_v4l2_vp8_quantization(struct v4l2_vp8_quantization quant)
> +{
> + json_object *vp8_quantization_obj = json_object_new_object();
> +
> + json_object_object_add(vp8_quantization_obj, "y_ac_qi", json_object_new_int(quant.y_ac_qi));
> + json_object_object_add(vp8_quantization_obj, "y_dc_delta", json_object_new_int(quant.y_dc_delta));
> + json_object_object_add(vp8_quantization_obj, "y2_dc_delta", json_object_new_int(quant.y2_dc_delta));
> + json_object_object_add(vp8_quantization_obj, "y2_ac_delta", json_object_new_int(quant.y2_ac_delta));
> + json_object_object_add(vp8_quantization_obj, "uv_dc_delta", json_object_new_int(quant.uv_dc_delta));
> + json_object_object_add(vp8_quantization_obj, "uv_ac_delta", json_object_new_int(quant.uv_ac_delta));
> + json_object_object_add(vp8_quantization_obj, "padding", json_object_new_int(quant.padding));
> +
> + return vp8_quantization_obj;
> +}
> +
> +json_object *trace_v4l2_vp8_entropy(struct v4l2_vp8_entropy entropy)
> +{
> + json_object *vp8_entropy_obj = json_object_new_object();
> +
> + /*__u8 coeff_probs[4][8][3][V4L2_VP8_COEFF_PROB_CNT] */
> + json_object *coeff_probs_obj = json_object_new_array();
> + for (size_t i = 0; i < ARRAY_SIZE(entropy.coeff_probs); i++)
> + for (size_t j = 0; j < ARRAY_SIZE(entropy.coeff_probs[0]); j++)
> + for (size_t k = 0; k < ARRAY_SIZE(entropy.coeff_probs[0][0]); k++)
> + for (size_t l = 0; l < V4L2_VP8_COEFF_PROB_CNT; l++)
> + json_object_array_add(coeff_probs_obj,
> + json_object_new_int(entropy.coeff_probs[i][j][k][l]));
> + json_object_object_add(vp8_entropy_obj, "coeff_probs", coeff_probs_obj);
> +
> + /* __u8 y_mode_probs[4] */
> + json_object *y_mode_probs_obj = json_object_new_array();
> + for (size_t i = 0; i < ARRAY_SIZE(entropy.y_mode_probs); i++)
> + json_object_array_add(y_mode_probs_obj, json_object_new_int(entropy.y_mode_probs[i]));
> + json_object_object_add(vp8_entropy_obj, "y_mode_probs", y_mode_probs_obj);
> +
> + /* __u8 uv_mode_probs[3] */
> + json_object *uv_mode_probs_obj = json_object_new_array();
> + for (size_t i = 0; i < ARRAY_SIZE(entropy.uv_mode_probs); i++)
> + json_object_array_add(uv_mode_probs_obj, json_object_new_int(entropy.uv_mode_probs[i]));
> + json_object_object_add(vp8_entropy_obj, "uv_mode_probs", uv_mode_probs_obj);
> +
> + /* __u8 mv_probs[2][V4L2_VP8_MV_PROB_CNT] */
> + json_object *mv_probs_obj = json_object_new_array();
> + for (size_t i = 0; i < ARRAY_SIZE(entropy.mv_probs); i++)
> + for (size_t j = 0; j < V4L2_VP8_MV_PROB_CNT; j++)
> + json_object_array_add(mv_probs_obj, json_object_new_int(entropy.mv_probs[i][j]));
> + json_object_object_add(vp8_entropy_obj, "mv_probs", mv_probs_obj);
> +
> + /*__u8 padding[3] */
> + json_object *padding_obj = json_object_new_array();
> + for (size_t i = 0; i < ARRAY_SIZE(entropy.padding); i++)
> + json_object_array_add(padding_obj, json_object_new_int(entropy.padding[i]));
> + json_object_object_add(vp8_entropy_obj, "padding", padding_obj);
> +
> + return vp8_entropy_obj;
> +}
> +
> +json_object *trace_v4l2_vp8_entropy_coder_state(struct v4l2_vp8_entropy_coder_state coder_state)
> +{
> + json_object *vp8_entropy_coder_state_obj = json_object_new_object();
> +
> + json_object_object_add(vp8_entropy_coder_state_obj, "range", json_object_new_int(coder_state.range));
> + json_object_object_add(vp8_entropy_coder_state_obj, "value", json_object_new_int(coder_state.value));
> + json_object_object_add(vp8_entropy_coder_state_obj, "bit_count", json_object_new_int(coder_state.bit_count));
> + json_object_object_add(vp8_entropy_coder_state_obj, "padding", json_object_new_int(coder_state.padding));
> +
> + return vp8_entropy_coder_state_obj;
> +}
> +
> +void trace_v4l2_ctrl_vp8_frame(void *p_vp8_frame, json_object *ctrl_obj)
> +{
> + json_object *vp8_frame_obj = json_object_new_object();
> + struct v4l2_ctrl_vp8_frame *vp8_frame = static_cast<struct v4l2_ctrl_vp8_frame*>(p_vp8_frame);
> +
> + /* struct v4l2_vp8_segment segment */
> + json_object *v4l2_vp8_segment_obj = trace_v4l2_vp8_segment(vp8_frame->segment);
> + json_object_object_add(vp8_frame_obj, "segment", v4l2_vp8_segment_obj);
> +
> + /* struct v4l2_vp8_loop_filter lf */
> + json_object *v4l2_vp8_loop_filter_obj = trace_v4l2_vp8_loop_filter(vp8_frame->lf);
> + json_object_object_add(vp8_frame_obj, "lf", v4l2_vp8_loop_filter_obj);
> +
> + /* struct v4l2_vp8_quantization quant */
> + json_object *v4l2_vp8_quantization_obj = trace_v4l2_vp8_quantization(vp8_frame->quant);
> + json_object_object_add(vp8_frame_obj, "quant", v4l2_vp8_quantization_obj);
> +
> + /* struct v4l2_vp8_entropy entropy */
> + json_object *v4l2_vp8_entropy_obj = trace_v4l2_vp8_entropy(vp8_frame->entropy);
> + json_object_object_add(vp8_frame_obj, "entropy", v4l2_vp8_entropy_obj);
> +
> + /* struct v4l2_vp8_entropy_coder_state coder_state */
> + json_object *v4l2_vp8_entropy_coder_state_obj = trace_v4l2_vp8_entropy_coder_state(vp8_frame->coder_state);
> + json_object_object_add(vp8_frame_obj, "coder_state", v4l2_vp8_entropy_coder_state_obj);
> +
> + json_object_object_add(vp8_frame_obj, "width", json_object_new_int(vp8_frame->width));
> + json_object_object_add(vp8_frame_obj, "height", json_object_new_int(vp8_frame->height));
> + json_object_object_add(vp8_frame_obj, "horizontal_scale", json_object_new_int(vp8_frame->horizontal_scale));
> + json_object_object_add(vp8_frame_obj, "vertical_scale", json_object_new_int(vp8_frame->vertical_scale));
> + json_object_object_add(vp8_frame_obj, "version", json_object_new_int(vp8_frame->version));
> + json_object_object_add(vp8_frame_obj, "prob_skip_false", json_object_new_int(vp8_frame->prob_skip_false));
> + json_object_object_add(vp8_frame_obj, "prob_intra", json_object_new_int(vp8_frame->prob_intra));
> + json_object_object_add(vp8_frame_obj, "prob_last", json_object_new_int(vp8_frame->prob_last));
> + json_object_object_add(vp8_frame_obj, "prob_gf", json_object_new_int(vp8_frame->prob_gf));
> + json_object_object_add(vp8_frame_obj, "num_dct_parts", json_object_new_int(vp8_frame->num_dct_parts));
> + json_object_object_add(vp8_frame_obj, "first_part_size", json_object_new_int(vp8_frame->first_part_size));
> + json_object_object_add(vp8_frame_obj, "first_part_header_bits", json_object_new_int(vp8_frame->first_part_header_bits));
> +
> + /* __u32 dct_part_sizes[8] */
> + json_object *dct_part_sizes_obj = json_object_new_array();
> + for (size_t i = 0; i < ARRAY_SIZE(vp8_frame->dct_part_sizes); i++)
> + json_object_array_add(dct_part_sizes_obj, json_object_new_int(vp8_frame->dct_part_sizes[i]));
> + json_object_object_add(vp8_frame_obj, "dct_part_sizes", dct_part_sizes_obj);
> +
> + json_object_object_add(vp8_frame_obj, "last_frame_ts", json_object_new_int(vp8_frame->last_frame_ts));
> + json_object_object_add(vp8_frame_obj, "golden_frame_ts", json_object_new_int(vp8_frame->golden_frame_ts));
> + json_object_object_add(vp8_frame_obj, "alt_frame_ts", json_object_new_int(vp8_frame->alt_frame_ts));
> + json_object_object_add(vp8_frame_obj, "flags", json_object_new_int(vp8_frame->flags));
> + json_object_object_add(vp8_frame_obj, "flags_str", json_object_new_string(vp8_frame_flag2s(vp8_frame->flags).c_str()));
> +
> + json_object_object_add(ctrl_obj, "v4l2_ctrl_vp8_frame", vp8_frame_obj);
> +}
> diff --git a/utils/tracer/trace.cpp b/utils/tracer/trace.cpp
> new file mode 100644
> index 00000000..5e952a7b
> --- /dev/null
> +++ b/utils/tracer/trace.cpp
> @@ -0,0 +1,520 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright 2022 Collabora Ltd.
> + */
> +
> +#include "libtracer.h"
> +
> +json_object *trace_open(const char *path, int oflag, mode_t mode)
> +{
> + json_object *open_args = json_object_new_object();
> +
> + json_object_object_add(open_args, "path", json_object_new_string(path));
> + json_object_object_add(open_args, "oflag", json_object_new_int(oflag));
> + json_object_object_add(open_args, "mode", json_object_new_uint64(mode));
> +
> + return open_args;
> +}
> +
> +json_object *trace_mmap64(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
> +{
> + json_object *mmap_args = json_object_new_object();
> +
> + json_object_object_add(mmap_args, "addr", json_object_new_int64((int64_t)addr));
> + json_object_object_add(mmap_args, "len", json_object_new_uint64(len));
> + json_object_object_add(mmap_args, "prot", json_object_new_int(prot));
> + json_object_object_add(mmap_args, "flags", json_object_new_int(flags));
> + json_object_object_add(mmap_args, "fildes", json_object_new_int(fildes));
> + json_object_object_add(mmap_args, "off", json_object_new_int64(off));
> +
> + return mmap_args;
> +}
> +
> +/*
> + * Get a buffer from memory and convert it to a json string array.
> + * The bytes are displayed with the most-significant bits first.
> + */
> +json_object *trace_buffer(unsigned char *buffer_pointer, __u32 bytesused)
> +{
> + char buf[5];
> + std::string s;
> + int byte_count_per_line = 0;
> + json_object *mem_array_obj = json_object_new_array();
> +
> + for (__u32 i = 0; i < bytesused; i++) {
> + memset(buf, 0, sizeof(buf));
> +
> + /* Each byte e.g. D9 will write a string of two characters "D9". */
> + sprintf(buf, "%02x", buffer_pointer[i]);
> + s += buf;
> + byte_count_per_line++;
> +
> + /* Add a space every two bytes e.g. "012A 4001" and a newline every 16 bytes. */
> + if (byte_count_per_line == 16) {
> + byte_count_per_line = 0;
> + json_object_array_add(mem_array_obj, json_object_new_string(s.c_str()));
> + s.clear();
> + } else if (i % 2) {
> + s += " ";
> + }
> + }
> +
> + /* Trace the last line if it was less than a full 16 bytes. */
> + if (byte_count_per_line)
> + json_object_array_add(mem_array_obj, json_object_new_string(s.c_str()));
> +
> + return mem_array_obj;
> +}
> +
> +/* Get the decoded capture buffer from memory and write it to a binary yuv file. */
> +void write_decoded_frames_to_yuv_file(unsigned char *buffer_pointer, __u32 bytesused, std::string filename)
> +{
> + FILE *fp = fopen(filename.c_str(), "a");
> + for (__u32 i = 0; i < bytesused; i++)
> + fwrite(&buffer_pointer[i], sizeof(unsigned char), 1, fp);
> + fflush(fp);
> + fclose(fp);
> +}
> +
> +/* Trace an output or capture buffer. */
> +void trace_mem(int fd)
> +{
> + int index;
> + __u32 type;
> + __u32 bytesused;
> + unsigned char *buffer_pointer;
> + long int start = get_buffer_address_trace(fd);
> +
> + /* Don't trace unmapped memory. */
> + if (!start)
> + return;
> +
> + buffer_pointer = (unsigned char*) start;
> + type = get_buffer_type_trace(fd);
> + index = get_buffer_index_trace(fd);
> + bytesused = get_buffer_bytesused_trace(fd);
> +
> + json_object *mem_obj = json_object_new_object();
> + json_object_object_add(mem_obj, "mem_dump", json_object_new_string(buftype2s(type).c_str()));
> + json_object_object_add(mem_obj, "fd", json_object_new_int(fd));
> + json_object_object_add(mem_obj, "type", json_object_new_uint64(type));
> + json_object_object_add(mem_obj, "index", json_object_new_int(index));
> + json_object_object_add(mem_obj, "bytesused", json_object_new_uint64(bytesused));
> + json_object_object_add(mem_obj, "address", json_object_new_int64(start));
> +
> + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> + json_object *mem_array_obj = trace_buffer(buffer_pointer, bytesused);
> + json_object_object_add(mem_obj, "mem_array", mem_array_obj);
> + }
> +
> + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +
> + if (create_yuv_file()) {
> + std::string filename = getenv("TRACE_ID");
> + filename += ".yuv";
> + write_decoded_frames_to_yuv_file(buffer_pointer, bytesused, filename);
> + json_object_object_add(mem_obj, "filename", json_object_new_string(filename.c_str()));
> + }
> +
> + if (write_decoded_data_to_json()) {
> + json_object *mem_array_obj = trace_buffer(buffer_pointer, bytesused);
> + json_object_object_add(mem_obj, "mem_array_capture", mem_array_obj);
> + }
> +
> + }
> +
> + if (pretty_print_mem())
> + write_json_object_to_json_file(mem_obj, JSON_C_TO_STRING_PRETTY);
> + else
> + write_json_object_to_json_file(mem_obj);
> +
> + json_object_put(mem_obj);
> +}
> +
> +void trace_vidioc_querycap(void *arg, json_object *ioctl_args)
> +{
> + json_object *cap_obj = json_object_new_object();
> + struct v4l2_capability *cap = static_cast<struct v4l2_capability*>(arg);
> +
> + json_object_object_add(cap_obj, "driver", json_object_new_string((const char *)cap->driver));
> + json_object_object_add(cap_obj, "card", json_object_new_string((const char *)cap->card));
> + json_object_object_add(cap_obj, "bus_info", json_object_new_string((const char *)cap->bus_info));
> + json_object_object_add(cap_obj, "version", json_object_new_string(ver2s(cap->version).c_str()));
> + json_object_object_add(cap_obj, "capabilities", json_object_new_int64(cap->capabilities));
> + json_object_object_add(cap_obj, "capabilities_str",
> + json_object_new_string(capflag2s(cap->capabilities).c_str()));
> + json_object_object_add(cap_obj, "device_caps", json_object_new_int64(cap->device_caps));
> + json_object_object_add(ioctl_args, "v4l2_capability", cap_obj);
> +}
> +
> +void trace_vidioc_enum_fmt(void *arg, json_object *ioctl_args)
> +{
> + json_object *fmtdesc_obj = json_object_new_object();
> + struct v4l2_fmtdesc *fmtdesc = static_cast<struct v4l2_fmtdesc*>(arg);
> +
> + json_object_object_add(fmtdesc_obj, "index", json_object_new_uint64(fmtdesc->index));
> + json_object_object_add(fmtdesc_obj, "type_str", json_object_new_string(buftype2s(fmtdesc->type).c_str()));
> + json_object_object_add(fmtdesc_obj, "type", json_object_new_uint64(fmtdesc->type));
> + json_object_object_add(fmtdesc_obj, "flags", json_object_new_uint64(fmtdesc->flags));
> + json_object_object_add(fmtdesc_obj, "description", json_object_new_string((const char *)fmtdesc->description));
> + json_object_object_add(fmtdesc_obj, "pixelformat", json_object_new_uint64(fmtdesc->pixelformat));
> + json_object_object_add(fmtdesc_obj, "mbus_code", json_object_new_uint64(fmtdesc->mbus_code));
> + json_object_object_add(ioctl_args, "v4l2_fmtdesc", fmtdesc_obj);
> +}
> +
> +void trace_v4l2_plane_pix_format(json_object *pix_mp_obj, struct v4l2_plane_pix_format plane_fmt, int plane)
> +{
> + json_object *plane_fmt_obj = json_object_new_object();
> +
> + json_object_object_add(plane_fmt_obj, "sizeimage", json_object_new_uint64(plane_fmt.sizeimage));
> + json_object_object_add(plane_fmt_obj, "bytesperline", json_object_new_uint64(plane_fmt.bytesperline));
> +
> + /* Create a unique key name for each plane. */
> + std::string unique_key_for_plane = "v4l2_plane_pix_format_";
> + unique_key_for_plane += std::to_string(plane);
> +
> + json_object_object_add(pix_mp_obj, unique_key_for_plane.c_str(), plane_fmt_obj);
> +}
> +
> +void trace_v4l2_pix_format_mplane(json_object *format_obj, struct v4l2_pix_format_mplane pix_mp)
> +{
> + json_object *pix_mp_obj = json_object_new_object();
> +
> + json_object_object_add(pix_mp_obj, "width", json_object_new_uint64(pix_mp.width));
> + json_object_object_add(pix_mp_obj, "height", json_object_new_uint64(pix_mp.height));
> + json_object_object_add(pix_mp_obj, "pixelformat", json_object_new_uint64(pix_mp.pixelformat));
> + json_object_object_add(pix_mp_obj, "pixelformat_str", json_object_new_string(fcc2s(pix_mp.pixelformat).c_str()));
> + json_object_object_add(pix_mp_obj, "field", json_object_new_uint64(pix_mp.field));
> + json_object_object_add(pix_mp_obj, "field_str", json_object_new_string(field2s(pix_mp.field).c_str()));
> + json_object_object_add(pix_mp_obj, "colorspace", json_object_new_uint64(pix_mp.colorspace));
> + json_object_object_add(pix_mp_obj, "colorspace_str", json_object_new_string(colorspace2s(pix_mp.colorspace).c_str()));
> + json_object_object_add(pix_mp_obj, "num_planes", json_object_new_int(pix_mp.num_planes));
> + for (int i = 0; i < pix_mp.num_planes; i++)
> + trace_v4l2_plane_pix_format(pix_mp_obj, pix_mp.plane_fmt[i], i);
> +
> + json_object_object_add(pix_mp_obj, "flags", json_object_new_int(pix_mp.flags));
> + json_object_object_add(pix_mp_obj, "flags_str", json_object_new_string(pixflags2s(pix_mp.flags).c_str()));
> + json_object_object_add(pix_mp_obj, "ycbcr_enc", json_object_new_int(pix_mp.ycbcr_enc));
> + json_object_object_add(pix_mp_obj, "ycbcr_enc_str", json_object_new_string(ycbcr_enc2s(pix_mp.ycbcr_enc).c_str()));
> + json_object_object_add(pix_mp_obj, "quantization", json_object_new_int(pix_mp.quantization));
> + json_object_object_add(pix_mp_obj, "quantization_str", json_object_new_string(quantization2s(pix_mp.quantization).c_str()));
> + json_object_object_add(pix_mp_obj, "xfer_func", json_object_new_int(pix_mp.xfer_func));
> + json_object_object_add(pix_mp_obj, "xfer_func_str", json_object_new_string(xfer_func2s(pix_mp.xfer_func).c_str()));
> +
> + json_object_object_add(format_obj, "v4l2_pix_format_mplane", pix_mp_obj);
> +}
> +
> +void trace_v4l2_format(void *arg, json_object *ioctl_args)
> +{
> + json_object *format_obj = json_object_new_object();
> + struct v4l2_format *format = static_cast<struct v4l2_format*>(arg);
> +
> + json_object_object_add(format_obj, "type", json_object_new_uint64(format->type));
> + json_object_object_add(format_obj, "type_str", json_object_new_string(buftype2s(format->type).c_str()));
> +
> + switch (format->type) {
> + case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> + case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> + case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> + case V4L2_BUF_TYPE_VBI_CAPTURE:
> + case V4L2_BUF_TYPE_VBI_OUTPUT:
> + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
> + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> + break;
> + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> + trace_v4l2_pix_format_mplane(format_obj, format->fmt.pix_mp);
> + break;
> + case V4L2_BUF_TYPE_SDR_CAPTURE:
> + case V4L2_BUF_TYPE_SDR_OUTPUT:
> + case V4L2_BUF_TYPE_META_CAPTURE:
> + case V4L2_BUF_TYPE_META_OUTPUT:
> + break;
> + }
> + json_object_object_add(ioctl_args, "v4l2_format", format_obj);
> +}
> +
> +void trace_v4l2_requestbuffers(void *arg, json_object *ioctl_args)
> +{
> + json_object *request_buffers_obj = json_object_new_object();
> + struct v4l2_requestbuffers *request_buffers = static_cast<struct v4l2_requestbuffers*>(arg);
> +
> + json_object_object_add(request_buffers_obj, "count", json_object_new_uint64(request_buffers->count));
> + json_object_object_add(request_buffers_obj, "type", json_object_new_uint64(request_buffers->type));
> + json_object_object_add(request_buffers_obj, "type_str",
> + json_object_new_string(buftype2s(request_buffers->type).c_str()));
> + json_object_object_add(request_buffers_obj, "memory", json_object_new_uint64(request_buffers->memory));
> + json_object_object_add(request_buffers_obj, "memory_str",
> + json_object_new_string(v4l2_memory2s(request_buffers->memory).c_str()));
> + json_object_object_add(request_buffers_obj, "capabilities", json_object_new_uint64(request_buffers->capabilities));
> + json_object_object_add(request_buffers_obj, "capabilities_str",
> + json_object_new_string(bufcap2s(request_buffers->capabilities).c_str()));
> + json_object_object_add(request_buffers_obj, "flags", json_object_new_int(request_buffers->flags));
> + json_object_object_add(request_buffers_obj, "flags_str",
> + json_object_new_string(request_buffers_flag2s(request_buffers->flags).c_str()));
> +
> + json_object_object_add(ioctl_args, "v4l2_requestbuffers", request_buffers_obj);
> +}
> +
> +json_object *trace_v4l2_plane(struct v4l2_plane *p, __u32 memory)
> +{
> + json_object *plane_obj = json_object_new_object();
> +
> + json_object_object_add(plane_obj, "bytesused", json_object_new_int64(p->bytesused));
> + json_object_object_add(plane_obj, "length", json_object_new_uint64(p->length));
> +
> + json_object *m_obj = json_object_new_object();
> + if (memory == V4L2_MEMORY_MMAP)
> + json_object_object_add(m_obj, "mem_offset", json_object_new_int64(p->m.mem_offset));
> + json_object_object_add(plane_obj, "m", m_obj);
> +
> + json_object_object_add(plane_obj, "data_offset", json_object_new_int64(p->data_offset));
> +
> + return plane_obj;
> +}
> +
> +void trace_v4l2_buffer(void *arg, json_object *ioctl_args)
> +{
> + json_object *buf_obj = json_object_new_object();
> + struct v4l2_buffer *buf = static_cast<struct v4l2_buffer*>(arg);
> +
> + json_object_object_add(buf_obj, "index", json_object_new_uint64(buf->index));
> + json_object_object_add(buf_obj, "type", json_object_new_uint64(buf->type));
> + json_object_object_add(buf_obj, "type_str",
> + json_object_new_string(buftype2s(buf->type).c_str()));
> + json_object_object_add(buf_obj, "bytesused", json_object_new_uint64(buf->bytesused));
> + json_object_object_add(buf_obj, "flags", json_object_new_uint64(buf->flags));
> + json_object_object_add(buf_obj, "flags_str",
> + json_object_new_string(bufferflags2s(buf->flags).c_str()));
> + json_object_object_add(buf_obj, "field", json_object_new_uint64(buf->field));
> +
> + json_object *timestamp_obj = json_object_new_object();
> + json_object_object_add(timestamp_obj, "tv_sec", json_object_new_int64(buf->timestamp.tv_sec));
> + json_object_object_add(timestamp_obj, "tv_usec", json_object_new_int64(buf->timestamp.tv_usec));
> + json_object_object_add(buf_obj, "timestamp", timestamp_obj);
> + json_object_object_add(buf_obj, "sequence", json_object_new_uint64(buf->sequence));
> + json_object_object_add(buf_obj, "memory", json_object_new_uint64(buf->memory));
> + json_object_object_add(buf_obj, "memory_str",
> + json_object_new_string(v4l2_memory2s(buf->memory).c_str()));
> +
> + json_object *m_obj = json_object_new_object();
> + if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
> + buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +
> + json_object *planes_obj = json_object_new_array();
> + /* TODO tracer only works for decoded formats with one plane e.g. V4L2_PIX_FMT_NV12 */
> + json_object_array_add(planes_obj, trace_v4l2_plane(buf->m.planes, buf->memory));
> + json_object_object_add(m_obj, "planes", planes_obj);
> + }
> + json_object_object_add(buf_obj, "m", m_obj);
> + json_object_object_add(buf_obj, "length", json_object_new_uint64(buf->length));
> +
> + /* For memory-to-memory devices you can use requests only for output buffers. */
> + if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> + json_object_object_add(buf_obj, "request_fd", json_object_new_int(buf->request_fd));
> +
> + json_object_object_add(ioctl_args, "v4l2_buffer", buf_obj);
> +}
> +
> +void trace_v4l2_exportbuffer(void *arg, json_object *ioctl_args)
> +{
> + json_object *exportbuffer_obj = json_object_new_object();
> + struct v4l2_exportbuffer *export_buffer = static_cast<struct v4l2_exportbuffer*>(arg);
> +
> + json_object_object_add(exportbuffer_obj, "type", json_object_new_uint64(export_buffer->type));
> + json_object_object_add(exportbuffer_obj, "type_str",
> + json_object_new_string(buftype2s(export_buffer->type).c_str()));
> +
> + json_object_object_add(exportbuffer_obj, "index", json_object_new_uint64(export_buffer->index));
> + json_object_object_add(exportbuffer_obj, "plane", json_object_new_uint64(export_buffer->plane));
> + json_object_object_add(exportbuffer_obj, "flags", json_object_new_uint64(export_buffer->flags));
> + json_object_object_add(exportbuffer_obj, "fd", json_object_new_int(export_buffer->fd));
> +
> + json_object_object_add(ioctl_args, "v4l2_exportbuffer", exportbuffer_obj);
> +}
> +
> +void trace_vidioc_stream(void *arg, json_object *ioctl_args)
> +{
> + v4l2_buf_type buf_type = *(static_cast<v4l2_buf_type*>(arg));
> + json_object_object_add(ioctl_args, "buf_type", json_object_new_int(buf_type));
> + json_object_object_add(ioctl_args, "buf_type_str", json_object_new_string(buftype2s(buf_type).c_str()));
> +}
> +
> +void trace_v4l2_ext_control(json_object *ext_controls_obj, struct v4l2_ext_control ctrl, __u32 control_idx)
> +{
> + std::string unique_key_for_control;
> +
> + json_object *ctrl_obj = json_object_new_object();
> + json_object_object_add(ctrl_obj, "id", json_object_new_uint64(ctrl.id));
> + json_object_object_add(ctrl_obj, "control_class_str", json_object_new_string(ctrlclass2s(ctrl.id).c_str()));
> + json_object_object_add(ctrl_obj, "size", json_object_new_uint64(ctrl.size));
> +
> + if ((ctrl.id & V4L2_CID_CODEC_STATELESS_BASE) == V4L2_CID_CODEC_STATELESS_BASE) {
> + switch (ctrl.id) {
> + case V4L2_CID_STATELESS_VP8_FRAME:
> + trace_v4l2_ctrl_vp8_frame(ctrl.p_vp8_frame, ctrl_obj);
> + break;
> + default:
> + break;
> + }
> + }
> +
> + unique_key_for_control = "v4l2_ext_control_";
> + unique_key_for_control += std::to_string(control_idx);
> +
> + json_object_object_add(ext_controls_obj, unique_key_for_control.c_str(), ctrl_obj);
> +}
> +
> +void trace_v4l2_ext_controls(void *arg, json_object *ioctl_args)
> +{
> + json_object *ext_controls_obj = json_object_new_object();
> + struct v4l2_ext_controls *ext_controls = static_cast<struct v4l2_ext_controls*>(arg);
> +
> + json_object_object_add(ext_controls_obj, "which", json_object_new_int64(ext_controls->which));
> + json_object_object_add(ext_controls_obj, "which_str", json_object_new_string(which2s(ext_controls->which).c_str()));
> + json_object_object_add(ext_controls_obj, "count", json_object_new_uint64(ext_controls->count));
> +
> + /* error_idx is defined only if the ioctl returned an error */
> + if (errno)
> + json_object_object_add(ext_controls_obj, "error_idx", json_object_new_uint64(ext_controls->error_idx));
> +
> + /* request_fd is only valid when "which" == V4L2_CTRL_WHICH_REQUEST_VAL */
> + if (ext_controls->which == V4L2_CTRL_WHICH_REQUEST_VAL)
> + json_object_object_add(ext_controls_obj, "request_fd", json_object_new_int(ext_controls->request_fd));
> +
> + for (__u32 i = 0; i < ext_controls->count; i++)
> + trace_v4l2_ext_control(ext_controls_obj, ext_controls->controls[i], i);
> +
> + json_object_object_add(ioctl_args, "v4l2_ext_controls", ext_controls_obj);
> +}
> +
> +void trace_vidioc_query_ext_ctrl(void *arg, json_object *ioctl_args)
> +{
> + json_object *query_ext_ctrl_obj = json_object_new_object();
> + struct v4l2_query_ext_ctrl *queryextctrl = static_cast<struct v4l2_query_ext_ctrl*>(arg);
> +
> + json_object_object_add(query_ext_ctrl_obj, "id", json_object_new_uint64(queryextctrl->id));
> + json_object_object_add(query_ext_ctrl_obj, "control_class_str", json_object_new_string(ctrlclass2s(queryextctrl->id).c_str()));
> + json_object_object_add(query_ext_ctrl_obj, "type", json_object_new_uint64(queryextctrl->type));
> + json_object_object_add(query_ext_ctrl_obj, "type_str", json_object_new_string(ctrltype2s(queryextctrl->type).c_str()));
> + json_object_object_add(query_ext_ctrl_obj, "name", json_object_new_string(queryextctrl->name));
> + json_object_object_add(query_ext_ctrl_obj, "minimum", json_object_new_int64(queryextctrl->minimum));
> + json_object_object_add(query_ext_ctrl_obj, "maximum", json_object_new_int64(queryextctrl->maximum));
> + json_object_object_add(query_ext_ctrl_obj, "step", json_object_new_uint64(queryextctrl->step));
> + json_object_object_add(query_ext_ctrl_obj, "default_value", json_object_new_int64(queryextctrl->default_value));
> + json_object_object_add(query_ext_ctrl_obj, "flags", json_object_new_uint64(queryextctrl->flags));
> + json_object_object_add(query_ext_ctrl_obj, "flags_str", json_object_new_string(ctrlflags2s(queryextctrl->flags).c_str()));
> + json_object_object_add(query_ext_ctrl_obj, "elem_size", json_object_new_uint64(queryextctrl->elem_size));
> + json_object_object_add(query_ext_ctrl_obj, "elems", json_object_new_uint64(queryextctrl->elems));
> + json_object_object_add(query_ext_ctrl_obj, "nr_of_dims", json_object_new_uint64(queryextctrl->nr_of_dims));
> +
> + /* __u32 dims[V4L2_CTRL_MAX_DIMS] */
> + json_object *dim_obj = json_object_new_array();
> +
> + for (unsigned int i = 0; i < queryextctrl->nr_of_dims; i++)
> + json_object_array_add(dim_obj, json_object_new_uint64(queryextctrl->dims[i]));
> +
> + json_object_object_add(query_ext_ctrl_obj, "dims", dim_obj);
> + json_object_object_add(ioctl_args, "v4l2_query_ext_ctrl", query_ext_ctrl_obj);
> +}
> +
> +void trace_ioctl_media(unsigned long request, void *arg, json_object *ioctl_args)
> +{
> + if (request == MEDIA_IOC_REQUEST_ALLOC) {
> + __s32 *request_fd = static_cast<__s32*>(arg);
> + json_object_object_add(ioctl_args, "request_fd", json_object_new_int(*request_fd));
> + }
> +}
> +
> +void trace_ioctl_video(unsigned long int request, void *arg, json_object *ioctl_args, bool from_userspace)
> +{
> + switch (request) {
> + case VIDIOC_QUERYCAP:
> + if (!from_userspace)
> + trace_vidioc_querycap(arg, ioctl_args);
> + break;
> + case VIDIOC_ENUM_FMT:
> + trace_vidioc_enum_fmt(arg, ioctl_args);
> + break;
> + case VIDIOC_G_FMT:
> + case VIDIOC_S_FMT:
> + trace_v4l2_format(arg, ioctl_args);
> + break;
> + case VIDIOC_REQBUFS:
> + trace_v4l2_requestbuffers(arg, ioctl_args);
> + break;
> + case VIDIOC_QUERYBUF:
> + case VIDIOC_QBUF:
> + case VIDIOC_DQBUF:
> + trace_v4l2_buffer(arg, ioctl_args);
> + break;
> + case VIDIOC_EXPBUF:
> + trace_v4l2_exportbuffer(arg, ioctl_args);
> + break;
> + case VIDIOC_STREAMON:
> + case VIDIOC_STREAMOFF:
> + if (from_userspace)
> + trace_vidioc_stream(arg, ioctl_args);
> + break;
> + case VIDIOC_G_EXT_CTRLS:
> + case VIDIOC_S_EXT_CTRLS:
> + trace_v4l2_ext_controls(arg, ioctl_args);
> + break;
> + case VIDIOC_QUERY_EXT_CTRL:
> + trace_vidioc_query_ext_ctrl(arg, ioctl_args);
> + break;
> + default:
> + break;
> + }
> +}
> +
> +void trace_dma_buf_ioctl_sync(void *arg, json_object *ioctl_args)
> +{
> + struct dma_buf_sync *sync = static_cast<struct dma_buf_sync*>(arg);
> + json_object *sync_obj = json_object_new_object();
> + json_object_object_add(sync_obj, "flags", json_object_new_uint64(sync->flags));
> + json_object_object_add(sync_obj, "flags_str", json_object_new_string(bufsyncflag2s(sync->flags).c_str()));
> + json_object_object_add(ioctl_args, "dma_buf_sync", sync_obj);
> +}
> +
> +std::string get_ioctl_request_str(unsigned long request)
> +{
> + __u8 ioctl_type = _IOC_TYPE(request);
> + switch (ioctl_type) {
> + case 'V':
> + return ioctl2s_video(request);
> + case '|':
> + return ioctl2s_media(request);
> + case 'b':
> + if (request == DMA_BUF_IOCTL_SYNC)
> + return "DMA_BUF_IOCTL_SYNC";
> + break;
> + default:
> + break;
> + }
> + return "unknown ioctl";
> +}
> +
> +json_object *trace_ioctl_args(int fd, unsigned long request, void *arg, bool from_userspace)
> +{
> + json_object *ioctl_args = json_object_new_object();
> + __u8 ioctl_type = _IOC_TYPE(request);
> + switch (ioctl_type) {
> + case 'V':
> + trace_ioctl_video(request, arg, ioctl_args, from_userspace);
> + break;
> + case '|':
> + trace_ioctl_media(request, arg, ioctl_args);
> + break;
> + case 'b':
> + if (request == DMA_BUF_IOCTL_SYNC && from_userspace) {
> + trace_dma_buf_ioctl_sync(arg, ioctl_args);
> + }
> + break;
> + default:
> + break;
> + }
> +
> + return ioctl_args;
> +}
> diff --git a/utils/tracer/tracer.cpp b/utils/tracer/tracer.cpp
> new file mode 100644
> index 00000000..1b84f85d
> --- /dev/null
> +++ b/utils/tracer/tracer.cpp
> @@ -0,0 +1,91 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright 2022 Collabora Ltd.
> + */
> +
> +#include <fcntl.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <getopt.h>
> +#include <sys/wait.h>
> +#include <unistd.h>
> +#include <json.h>
> +#include <time.h>
> +#include <cstring>
> +#include <string>
> +
> +int main(int argc, char *argv[])
> +{
> + int ch;
> + char short_options[] = {'e', 'p', 'r', 'y'};
> +
> + if (argc <= 1) {
> + fprintf(stderr, "usage: tracer [-e] [-p] [-r] [-y] <tracee>\n");
> + return -1;
> + }
> +
> + do {
> + ch = getopt(argc, argv, short_options);
> + switch (ch){
> + case 'e':
> + setenv("TRACE_OPTION_PRETTY_PRINT_MEM", "true", 0);
> + break;
> + case 'p':
> + setenv("TRACE_OPTION_PRETTY_PRINT_ALL", "true", 0);
> + break;
> + case 'r':
> + setenv("TRACE_OPTION_DECODED_TO_JSON", "true", 0);
> + break;
> + case 'y':
> + setenv("TRACE_OPTION_CREATE_YUV_FILE", "true", 0);
> + break;
> + }
> + } while (ch != -1);
> +
> + /* Get the tracee from the command line. */
> + int count = 0;
> + char *exec_array[argc];
> + while (optind < argc)
> + exec_array[count++] = argv[optind++];
> + exec_array[count] = nullptr;
> +
> + /* Use a substring of the time to create a unique id for the trace. */
> + std::string trace_id = std::to_string(time(nullptr));
> + trace_id = trace_id.substr(5, trace_id.npos) + "_trace";
> +
> + /* Create the trace file to hold the json-objects as a large json array. */
> + std::string trace_filename = trace_id + ".json";
> + FILE *trace_file = fopen(trace_filename.c_str(), "w");
> + fputs("[\n", trace_file);
> + fflush(trace_file);
> +
> + setenv("TRACE_ID", trace_id.c_str(), 0);
> + setenv("LD_PRELOAD", ".libs/libtracer.so", 0);
While trying out the tracer, I stumbled across this being hardcoded. It is
rather inconvenient since you must be in the build tree to use it. I wonder how
we could fix this, or even hide the re-load library ? Have you checked if you
could preload using the ld library C interface ?
> +
> + if (fork() == 0) {
> + execvpe(exec_array[0], exec_array, environ);
> + perror("Could not execute tracee");
> + return -1;
> + }
> +
> + int tracee_result;
> + wait(&tracee_result);
> + unsetenv("TRACE_ID");
> + unsetenv("LD_PRELOAD");
> +
> + if (WEXITSTATUS(tracee_result)) {
> + fprintf(stderr, "Trace error: %s\n", trace_filename.c_str());
> + exit(EXIT_FAILURE);
> + }
> +
> + /* Close the json-array and the trace file. */
> + trace_file = fopen(trace_filename.c_str(), "r+");
> + fseek(trace_file, 0L, SEEK_END);
> + fseek(trace_file, (ftell(trace_file) - 2), SEEK_SET);
> + std::string end = "\n]\n";
> + fwrite(end.c_str(), sizeof(char), end.length(), trace_file);
> + fclose(trace_file);
> +
> + fprintf(stderr, "Trace complete: %s\n", trace_filename.c_str());
> + return 0;
> +}
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2022-08-26 19:18 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-08-20 0:50 [RFC 0/2] v4l2 stateless tracer/retracer utilities Deborah Brouwer
2022-08-20 0:50 ` [RFC 1/2] utils: add stateless tracer utility Deborah Brouwer
2022-08-26 19:18 ` Nicolas Dufresne
2022-08-20 0:50 ` [RFC 2/2] utils: add stateless retracer utility Deborah Brouwer
2022-08-26 19:05 ` [RFC 0/2] v4l2 stateless tracer/retracer utilities Nicolas Dufresne
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.