diff options
-rw-r--r-- | test/camera/camera_reconfigure.cpp | 262 | ||||
-rw-r--r-- | test/camera/meson.build | 1 |
2 files changed, 263 insertions, 0 deletions
diff --git a/test/camera/camera_reconfigure.cpp b/test/camera/camera_reconfigure.cpp new file mode 100644 index 00000000..5adef16e --- /dev/null +++ b/test/camera/camera_reconfigure.cpp @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * Test: + * - Multiple reconfigurations of the Camera without stopping the CameraManager + * - Validate there are no file descriptor leaks when using IPC + */ + +#include <dirent.h> +#include <fstream> +#include <iostream> + +#include <libcamera/base/event_dispatcher.h> +#include <libcamera/base/file.h> +#include <libcamera/base/thread.h> +#include <libcamera/base/timer.h> + +#include <libcamera/framebuffer_allocator.h> + +#include "camera_test.h" +#include "test.h" + +using namespace std; + +namespace { + +class CameraReconfigure : public CameraTest, public Test +{ +public: + /* Initialize CameraTest with isolated IPA */ + CameraReconfigure() + : CameraTest(kCamId_, true) + { + } + +private: + static constexpr const char *kCamId_ = "platform/vimc.0 Sensor B"; + static constexpr const char *kIpaProxyName_ = "vimc_ipa_proxy"; + static constexpr unsigned int kNumOfReconfigures_ = 10; + + void requestComplete(Request *request) + { + if (request->status() != Request::RequestComplete) + return; + + const Request::BufferMap &buffers = request->buffers(); + + const Stream *stream = buffers.begin()->first; + FrameBuffer *buffer = buffers.begin()->second; + + /* Reuse the request and re-queue it with the same buffers. */ + request->reuse(); + request->addBuffer(stream, buffer); + camera_->queueRequest(request); + } + + int startAndStop() + { + StreamConfiguration &cfg = config_->at(0); + + if (camera_->acquire()) { + cerr << "Failed to acquire the camera" << endl; + return TestFail; + } + + if (camera_->configure(config_.get())) { + cerr << "Failed to set default configuration" << endl; + return TestFail; + } + + Stream *stream = cfg.stream(); + + /* + * The configuration is consistent so we can re-use the + * same buffer allocation for each run. + */ + if (!allocated_) { + int ret = allocator_->allocate(stream); + if (ret < 0) { + cerr << "Failed to allocate buffers" << endl; + return TestFail; + } + allocated_ = true; + } + + for (const unique_ptr<FrameBuffer> &buffer : allocator_->buffers(stream)) { + unique_ptr<Request> request = camera_->createRequest(); + if (!request) { + cerr << "Failed to create request" << endl; + return TestFail; + } + + if (request->addBuffer(stream, buffer.get())) { + cerr << "Failed to associate buffer with request" << endl; + return TestFail; + } + + requests_.push_back(move(request)); + } + + camera_->requestCompleted.connect(this, &CameraReconfigure::requestComplete); + + if (camera_->start()) { + cerr << "Failed to start camera" << endl; + return TestFail; + } + + for (unique_ptr<Request> &request : requests_) { + if (camera_->queueRequest(request.get())) { + cerr << "Failed to queue request" << endl; + return TestFail; + } + } + + EventDispatcher *dispatcher = Thread::current()->eventDispatcher(); + + Timer timer; + timer.start(100); + while (timer.isRunning()) + dispatcher->processEvents(); + + if (camera_->stop()) { + cerr << "Failed to stop camera" << endl; + return TestFail; + } + + if (camera_->release()) { + cerr << "Failed to release camera" << endl; + return TestFail; + } + + camera_->requestCompleted.disconnect(this, &CameraReconfigure::requestComplete); + + requests_.clear(); + + return 0; + } + + int fdsOpen(pid_t pid) + { + string proxyFdPath = "/proc/" + to_string(pid) + "/fd"; + DIR *dir; + struct dirent *ptr; + unsigned int openFds = 0; + + dir = opendir(proxyFdPath.c_str()); + if (dir == nullptr) { + int err = errno; + cerr << "Error opening " << proxyFdPath << ": " + << strerror(-err) << endl; + return 0; + } + + while ((ptr = readdir(dir)) != nullptr) { + if ((strcmp(ptr->d_name, ".") == 0) || + (strcmp(ptr->d_name, "..") == 0)) + continue; + + openFds++; + } + closedir(dir); + + return openFds; + } + + pid_t findProxyPid() + { + string proxyPid; + string proxyName(kIpaProxyName_); + 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 == proxyName ? string(ptr->d_name) : ""; + } + + if (!proxyPid.empty()) + break; + } + closedir(dir); + + if (!proxyPid.empty()) + return atoi(proxyPid.c_str()); + + return -1; + } + + int init() override + { + if (status_ != TestPass) + return status_; + + config_ = camera_->generateConfiguration({ StreamRole::StillCapture }); + if (!config_ || config_->size() != 1) { + cerr << "Failed to generate default configuration" << endl; + return TestFail; + } + + allocator_ = make_unique<FrameBufferAllocator>(camera_); + allocated_ = false; + + return TestPass; + } + + int run() override + { + unsigned int openFdsAtStart = 0; + unsigned int openFds = 0; + + pid_t proxyPid = findProxyPid(); + if (proxyPid < 0) { + cerr << "Cannot find " << kIpaProxyName_ + << " pid, exiting" << endl; + return TestFail; + } + + openFdsAtStart = fdsOpen(proxyPid); + for (unsigned int i = 0; i < kNumOfReconfigures_; i++) { + startAndStop(); + openFds = fdsOpen(proxyPid); + if (openFds == 0) { + cerr << "No open fds found whereas " + << "open fds at start: " << openFdsAtStart + << endl; + return TestFail; + } + + if (openFds != openFdsAtStart) { + cerr << "Leaking fds for " << kIpaProxyName_ + << " - Open fds: " << openFds << " vs " + << "Open fds at start: " << openFdsAtStart + << endl; + return TestFail; + } + } + + return TestPass; + } + + bool allocated_; + + vector<unique_ptr<Request>> requests_; + + unique_ptr<CameraConfiguration> config_; + unique_ptr<FrameBufferAllocator> allocator_; +}; + +} /* 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 |