From patchwork Fri Aug 6 11:51:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Umang Jain X-Patchwork-Id: 13248 X-Patchwork-Delegate: umang.jain@ideasonboard.com Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 44B83BD87D for ; Fri, 6 Aug 2021 11:51:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8CEE168822; Fri, 6 Aug 2021 13:51:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="W/K/FBmF"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2F35360266 for ; Fri, 6 Aug 2021 13:51:39 +0200 (CEST) Received: from perceval.ideasonboard.com (unknown [103.251.226.40]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 095FC4FB; Fri, 6 Aug 2021 13:51:37 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1628250698; bh=1GCFHLhPtMgQZ6RP0ra1LI0x/JvhR8csOYS6feUq1d8=; h=From:To:Cc:Subject:Date:From; b=W/K/FBmFQqDa9UQlrY87AVU7vYYCbKuwjJqlGzKveTA0TEvX5kdW9PPHpo7mVoCUt DD4umx/wakCE0CwAa1ChjayaMGhDnwFgK79LXCVzgyFHRt9eyiw9lHn7/2eCn2w+w3 GiTmhAYOP13KVNyUzLMfX6PEcbUFZ3j9oEo7wJVw= From: Umang Jain To: libcamera-devel@lists.libcamera.org Date: Fri, 6 Aug 2021 17:21:32 +0530 Message-Id: <20210806115132.353831-1-umang.jain@ideasonboard.com> X-Mailer: git-send-email 2.31.1 MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH] test: camera: Camera reconfiguration and fd-leak test X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" This tests basically checks for two things: - Camera reconfigurations without stopping CameraManager - Fd leaks across IPA IPC boundary. Currently, it uses vimc, but can be easily changed to using another platform (for e.g. IPU3) by changing CAM_ID and IPA_PROXY_NAME defines. This test is geared towards reproducing [1], issue noticed on IPU3. Ensure that test is run in isolated mode. Running standalone: $ LIBCAMERA_IPA_FORCE_ISOLATION=1 ./build/test/camera/camera_reconfigure [1] https://bugs.libcamera.org/show_bug.cgi?id=63 Signed-off-by: Umang Jain --- - still RFC because it's based on "Pass buffers to VIMC IPA" series - We probably need to set LIBCAMERA_IPA_FORCE_ISOLATION=1 from inside the test. Setting at run-time with setenv(), sets the env var but still the test is unable to run in isolated mode. For now, I only have it working, by running it standalone as mentioned in the commit message. OR any other ideas enforcing isolated mode on the test? - Some sprinkled prints are intentionally present, will be reworked in v1. Please ignore those for review. (log snippet: https://paste.debian.net/1206765/) --- test/camera/camera_reconfigure.cpp | 250 +++++++++++++++++++++++++++++ test/camera/meson.build | 1 + 2 files changed, 251 insertions(+) create mode 100644 test/camera/camera_reconfigure.cpp diff --git a/test/camera/camera_reconfigure.cpp b/test/camera/camera_reconfigure.cpp new file mode 100644 index 00000000..20a4a6a4 --- /dev/null +++ b/test/camera/camera_reconfigure.cpp @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * Test: + * - Camera's multiple reconfigurations without stopping CameraManager + * - leaking fds across IPA IPC boundary + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "camera_test.h" +#include "test.h" + +using namespace std; + +#define CAM_ID "platform/vimc.0 Sensor B" +#define IPA_PROXY_NAME "vimc_ipa_proxy" +#define NUM_OF_RECONFIGURES 20 + +namespace { + +class CameraReconfigure : public CameraTest, public Test +{ +public: + CameraReconfigure() + : CameraTest(CAM_ID) + { + } + +protected: + unsigned int configureRuns_; + unsigned int openFdsAtStart_; + string vimcProxyName_; + bool allocated_; + + vector> requests_; + + unique_ptr config_; + FrameBufferAllocator *allocator_; + + void requestComplete(Request *request) + { + if (request->status() != Request::RequestComplete) + return; + + const Request::BufferMap &buffers = request->buffers(); + + /* Create a new request. */ + const Stream *stream = buffers.begin()->first; + FrameBuffer *buffer = buffers.begin()->second; + + request->reuse(); + request->addBuffer(stream, buffer); + camera_->queueRequest(request); + } + + int startAndStop () + { + StreamConfiguration &cfg = config_->at(0); + + if (camera_->acquire()) { + cout << "Failed to acquire the camera" << endl; + return TestFail; + } + + if (camera_->configure(config_.get())) { + cout << "Failed to set default configuration" << endl; + return TestFail; + } + + Stream *stream = cfg.stream(); + + if (!allocated_) { + int ret = allocator_->allocate(stream); + if (ret < 0) + return TestFail; + allocated_ = true; + } + + for (const unique_ptr &buffer : allocator_->buffers(stream)) { + unique_ptr request = camera_->createRequest(); + if (!request) { + cout << "Failed to create request" << endl; + return TestFail; + } + + if (request->addBuffer(stream, buffer.get())) { + cout << "Failed to associating buffer with request" << endl; + return TestFail; + } + + requests_.push_back(move(request)); + } + + camera_->requestCompleted.connect(this, &CameraReconfigure::requestComplete); + + if (camera_->start()) { + cout << "Failed to start camera" << endl; + return TestFail; + } + + for (unique_ptr &request : requests_) { + if (camera_->queueRequest(request.get())) { + cout << "Failed to queue request" << endl; + return TestFail; + } + } + + EventDispatcher *dispatcher = Thread::current()->eventDispatcher(); + + Timer timer; + timer.start(1000); + while (timer.isRunning()) + dispatcher->processEvents(); + + if (camera_->stop()) { + cout << "Failed to stop camera" << endl; + return TestFail; + } + + if (camera_->release()) { + cout << "Failed to release camera" << endl; + return TestFail; + } + + camera_->requestCompleted.disconnect(this, &CameraReconfigure::requestComplete); + + requests_.clear(); + + return 0; + } + + int fdsOpen(string pid) + { + string proxyFdPath = "/proc/" + pid + "/fd"; + DIR *dir; + struct dirent *ptr; + unsigned int openFds = 0; + + dir = opendir(proxyFdPath.c_str()); + while((ptr = readdir(dir)) != nullptr) { + if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) + continue; + + openFds++; + } + closedir(dir); + + return openFds; + } + + string findProxyPid() + { + string proxyPid; + DIR *dir; + struct dirent *ptr; + + dir = opendir("/proc"); + while((ptr = readdir(dir)) != nullptr) { + if(ptr->d_type != DT_DIR) + continue; + + string pname("/proc/" + string(ptr->d_name) + "/comm"); + if(File::exists(pname.c_str())) { + ifstream pfile(pname.c_str()); + string comm; + getline (pfile, comm); + pfile.close(); + + proxyPid = comm == vimcProxyName_ ? + string(ptr->d_name) : + ""; + } + + if (!proxyPid.empty()) + break; + } + closedir(dir); + + return proxyPid; + } + + int init() override + { + if (status_ != TestPass) + return status_; + + config_ = camera_->generateConfiguration({ StreamRole::StillCapture }); + if (!config_ || config_->size() != 1) { + cout << "Failed to generate default configuration" << endl; + return TestFail; + } + + allocator_ = new FrameBufferAllocator(camera_); + + openFdsAtStart_ = 0; + configureRuns_ = NUM_OF_RECONFIGURES; + vimcProxyName_ = IPA_PROXY_NAME; + allocated_ = false; + + return TestPass; + } + + void cleanup() override + { + delete allocator_; + } + + int run() override + { + unsigned int openFds = 0; + /* Find pid of vimc_ipa_proxy */ + string proxyPid = findProxyPid(); + if (proxyPid.empty()) { + cout << "Not running in isolated mode, exiting" << endl; + return TestFail; + } + + /* Find number of open fds by vimc_ipa_proxy */ + openFdsAtStart_ = fdsOpen(proxyPid); + cout << IPA_PROXY_NAME << " PID:" << proxyPid + << " has open fds at start:" << openFdsAtStart_ << endl; + + for (unsigned int i = 0; i < configureRuns_; i++) + { + startAndStop(); + openFds = fdsOpen(proxyPid); + cout << "No. of open fds after #" << i << " runs: " << openFds << endl; + } + + if (openFds == openFdsAtStart_) + return TestPass; + else + return TestFail; + } +}; + +} /* namespace */ + +TEST_REGISTER(CameraReconfigure) diff --git a/test/camera/meson.build b/test/camera/meson.build index 002a87b5..668d5c03 100644 --- a/test/camera/meson.build +++ b/test/camera/meson.build @@ -8,6 +8,7 @@ camera_tests = [ ['buffer_import', 'buffer_import.cpp'], ['statemachine', 'statemachine.cpp'], ['capture', 'capture.cpp'], + ['camera_reconfigure', 'camera_reconfigure.cpp'], ] foreach t : camera_tests