From patchwork Sat Aug 20 00:50:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Deborah Brouwer X-Patchwork-Id: 12949430 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DBA7CC3F6B0 for ; Sat, 20 Aug 2022 00:50:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245633AbiHTAuT (ORCPT ); Fri, 19 Aug 2022 20:50:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36866 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S245644AbiHTAuR (ORCPT ); Fri, 19 Aug 2022 20:50:17 -0400 Received: from madras.collabora.co.uk (madras.collabora.co.uk [46.235.227.172]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 02B5E11830 for ; Fri, 19 Aug 2022 17:50:14 -0700 (PDT) Received: from localhost.localdomain (node-1w7jr9st5p2etziuntaazujnj.ipv6.telus.net [IPv6:2001:569:beb1:1500:c96f:992f:7c34:9ff]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: dbrouwer) by madras.collabora.co.uk (Postfix) with ESMTPSA id D285A6601DC8; Sat, 20 Aug 2022 01:50:10 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1660956612; bh=fHVoLTHrqX8MEKgxsu7Krxlbu7SdIhuIviRtxKOXl0M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=AyB+D49qQJZcqFqkLNfQZ8scFHdOkNW6+jRLin6Ok0z64YdxAoifIxVH9gFulQcCP 2pfhjc1Cg/5gcEo1moK8d6RCOq0RyJ/KOP9fblpMSDk4RpcAR3/tDPja21bMjvSIM/ Z4vC3wu7eZcC59m72QrmJd2KGdNkhdPq3B79nw4XDe9CDiDZN4x3pb2/Q4wBtV0rCR SXGKbj+Qt4Q4Ci4ussb9kLXducZgyGtwJ+MjVzMEI7x8XliZtWWBuF0O/UeKhg7Vtf 4QJctgyJnwfzvH2N4UDpylzPNJrEHtutmYym1rYM7N2isgVKHQVQYkD+++W58UNT+P qD1d4Bzukg1Ag== From: Deborah Brouwer To: linux-media@vger.kernel.org Cc: daniel.almeida@collabora.com, nfraprado@collabora.com, nicolas.dufresne@collabora.com, hverkuil-cisco@xs4all.nl, Deborah Brouwer Subject: [RFC 1/2] utils: add stateless tracer utility Date: Fri, 19 Aug 2022 17:50:18 -0700 Message-Id: <088185de3da0eaab21fc6def679e59033fd2fd24.1660955263.git.deborah.brouwer@collabora.com> X-Mailer: git-send-email 2.37.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org 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 --- 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 #include +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(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(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(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 +#include +#include +#include +#include +#include +#include +#include +#include +#include /* needed for close */ +#include +#include +#include +#include +#include +#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 devices; + /* List of output and capture buffers being traced. */ + std::list 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 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::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 +#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 +#include +#include +#include + +#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(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(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(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(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(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(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(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(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(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(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(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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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] \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; +} From patchwork Sat Aug 20 00:50:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Deborah Brouwer X-Patchwork-Id: 12949431 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B687EC32792 for ; Sat, 20 Aug 2022 00:50:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245638AbiHTAuW (ORCPT ); Fri, 19 Aug 2022 20:50:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36994 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243566AbiHTAuV (ORCPT ); Fri, 19 Aug 2022 20:50:21 -0400 Received: from madras.collabora.co.uk (madras.collabora.co.uk [46.235.227.172]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D092D6272 for ; Fri, 19 Aug 2022 17:50:17 -0700 (PDT) Received: from localhost.localdomain (node-1w7jr9st5p2etziuntaazujnj.ipv6.telus.net [IPv6:2001:569:beb1:1500:c96f:992f:7c34:9ff]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: dbrouwer) by madras.collabora.co.uk (Postfix) with ESMTPSA id CAE996601DC9; Sat, 20 Aug 2022 01:50:14 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1660956616; bh=Zaxx1NMvM4P1rynMnkyZWxUsv//6XSdvMIZrJI39UTQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=N7SNvWwizvs53zmiG5BGqKt4U5XC1tl2bd/a4p09UCN/8l/Gv3xmpYIgs5DPQwUjN nhzqPrrWjzm7N9h+Kfy+gvNAHXrFN251rgo4KVNU0YLNWnvY9CnNC17j+SmgpycwwZ +WxhRZPleHqKJDs7vko4KbdGpjtbQS1FGoqQ1Y0RgF/PY0ARnzTM3dmICTST5e99QK h41p4kBk5H/oxLVc0HoH+jjA/bk/dXNK7JgWmVGvhsZ3kqAaXjqO9EspIkmFOB9ASV IQdQx9Rzi4CQmfW/A+SUeaY4VdkGEca4T3WxEMeCPO0GtbN6XOnnVkQJoejNzjE6qU LRLJD56KDGMPg== From: Deborah Brouwer To: linux-media@vger.kernel.org Cc: daniel.almeida@collabora.com, nfraprado@collabora.com, nicolas.dufresne@collabora.com, hverkuil-cisco@xs4all.nl, Deborah Brouwer Subject: [RFC 2/2] utils: add stateless retracer utility Date: Fri, 19 Aug 2022 17:50:19 -0700 Message-Id: <94751365688c863124725b7d5276191b07838c56.1660955263.git.deborah.brouwer@collabora.com> X-Mailer: git-send-email 2.37.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org 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 --- 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 retrace_fds; + + /* List of output and capture buffers being retraced. */ + std::list 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 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::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 ] [-v