From patchwork Tue Jul 16 07:05:05 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 1707 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E234160C23 for ; Tue, 16 Jul 2019 09:05:19 +0200 (CEST) Received: from emerald.amanokami.net (unknown [IPv6:2a00:79e1:abc:3602:b57a:2dda:be67:ac6e]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A741A564; Tue, 16 Jul 2019 09:05:17 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1563260719; bh=yyP/Z0YQT3BezTem6N81/14JgN3D5U9TQDjD/Zu4G9Y=; h=From:To:Cc:Subject:Date:From; b=P+9oK3vlx+yi2+seTVR9oiOaVGPAhE/3Vne1vF94P/C78tAlGofSmV8pLt0jiRdjH lMMCZ5Et+CKPnB3cSjbZQAhtS3rHGLyOoZJh7Lg4UGtCG/KYXXNf+dXJiNFVcr67Py aRfakwRav9XAKb0amfYbKHfqgPdMB4HgLVG/85f0= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Tue, 16 Jul 2019 16:05:05 +0900 Message-Id: <20190716070508.24589-1-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.17.1 Subject: [libcamera-devel] [PATCH v2 1/4] libcamera: logging: add syslog, stream, and nowhere logging targets X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 16 Jul 2019 07:05:20 -0000 Allow logging to syslog, or any given ostream, or to nowhere. The logging API is updated to accomodate these new logging destinations. LogMessage is modified to allow this. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v2: - make LogMessage's message component getters return the data directly, and not as a string (eg. severity, category, timestamp) - move LogMessage's message component formatting (converting to string) to Logger - add LogOutput to abstract away log output destinations from the Logger, and to prevent anticipated concurrency issues (changing the log output between commencing and completing a previous write) - upgrade documentation - other minor changes include/libcamera/logging.h | 13 +- src/libcamera/include/log.h | 8 + src/libcamera/log.cpp | 458 +++++++++++++++++++++++++++++------- 3 files changed, 397 insertions(+), 82 deletions(-) diff --git a/include/libcamera/logging.h b/include/libcamera/logging.h index a8495cf..2b6dd3f 100644 --- a/include/libcamera/logging.h +++ b/include/libcamera/logging.h @@ -9,8 +9,17 @@ namespace libcamera { -void logSetFile(const char *file); -int logSetLevel(const char *category, const char *level); +enum LoggingTarget { + LoggingTargetNone, + LoggingTargetSyslog, + LoggingTargetFile, + LoggingTargetStream, +}; + +int logSetFile(const char *path); +int logSetStream(std::ostream *stream); +int logSetTarget(LoggingTarget target); +void logSetLevel(const char *category, const char *level); } /* namespace libcamera */ diff --git a/src/libcamera/include/log.h b/src/libcamera/include/log.h index 802836d..9b203f9 100644 --- a/src/libcamera/include/log.h +++ b/src/libcamera/include/log.h @@ -60,12 +60,20 @@ public: std::ostream &stream() { return msgStream_; } + const struct timespec ×tamp() const { return timestamp_; } + LogSeverity severity() const { return severity_; } + const LogCategory &category() const { return category_; } + const std::string &fileInfo() const { return fileInfo_; } + const std::string msg() const { return msgStream_.str(); } + private: void init(const char *fileName, unsigned int line); std::ostringstream msgStream_; const LogCategory &category_; LogSeverity severity_; + struct timespec timestamp_; + std::string fileInfo_; }; class Loggable diff --git a/src/libcamera/log.cpp b/src/libcamera/log.cpp index 709c669..3108ef3 100644 --- a/src/libcamera/log.cpp +++ b/src/libcamera/log.cpp @@ -15,8 +15,11 @@ #include #include #include +#include #include +#include + #include "utils.h" /** @@ -48,8 +51,172 @@ * to stderr. */ +/** + * \file logging.h + * \brief Logging management + * + * API to change the logging output destination and log levels programatically. + */ + namespace libcamera { +static int log_severity_to_syslog(LogSeverity severity) +{ + switch (severity) { + case LogDebug: + return LOG_DEBUG; + case LogInfo: + return LOG_INFO; + case LogWarning: + return LOG_WARNING; + case LogError: + return LOG_ERR; + case LogFatal: + return LOG_ALERT; + default: + return LOG_NOTICE; + } +} + +static std::string log_timespec_to_string(const struct timespec ×tamp) +{ + std::ostringstream ossTimestamp; + ossTimestamp.fill('0'); + ossTimestamp << "[" << timestamp.tv_sec / (60 * 60) << ":" + << std::setw(2) << (timestamp.tv_sec / 60) % 60 << ":" + << std::setw(2) << timestamp.tv_sec % 60 << "." + << std::setw(9) << timestamp.tv_nsec << "]"; + ossTimestamp.fill(' '); + return ossTimestamp.str(); +} + +static const char *log_severity_name(LogSeverity severity) +{ + static const char *const names[] = { + " DBG", + " INFO", + " WARN", + " ERR", + "FATAL", + }; + + if (static_cast(severity) < ARRAY_SIZE(names)) + return names[severity]; + else + return "UNKWN"; +} + +/** + * \brief Log output + * + * The LogOutput class models a log output desination + */ +class LogOutput +{ +public: + LogOutput(const char *path); + LogOutput(std::ostream *stream); + LogOutput(); + ~LogOutput(); + + bool good() const; + void write(const LogMessage &msg); + +private: + void writeSyslog(const LogMessage &msg); + void writeStream(const LogMessage &msg); + + std::ostream *stream_; + LoggingTarget target_; +}; + +/** + * \brief Construct a log output based on a file + * \param[in] path Full path to log file + */ +LogOutput::LogOutput(const char *path) + : target_(LoggingTargetFile) +{ + stream_ = new std::ofstream(path); +} + +/** + * \brief Construct a log output based on a stream + * \param[in] stream Stream to send log output to + */ +LogOutput::LogOutput(std::ostream *stream) + : stream_(stream), target_(LoggingTargetStream) +{ +} + +/** + * \brief Construct a log output to syslog + */ +LogOutput::LogOutput() + : stream_(nullptr), target_(LoggingTargetSyslog) +{ + openlog("libcamera", LOG_PID, 0); +} + +LogOutput::~LogOutput() +{ + switch (target_) { + case LoggingTargetFile: + delete stream_; + break; + case LoggingTargetSyslog: + closelog(); + break; + default: + break; + } +} + +/** + * \brief Check if log output is a valid stream + */ +bool LogOutput::good() const +{ + return stream_ && stream_->good(); +} + +/** + * \brief Write message to log output + * \param[in] msg Message to write + */ +void LogOutput::write(const LogMessage &msg) +{ + switch (target_) { + case LoggingTargetSyslog: + writeSyslog(msg); + break; + case LoggingTargetStream: + case LoggingTargetFile: + writeStream(msg); + break; + default: + break; + } +} + +void LogOutput::writeSyslog(const LogMessage &msg) +{ + std::string str = std::string(log_severity_name(msg.severity())) + " " + + msg.category().name() + " " + msg.fileInfo() + " " + + msg.msg(); + syslog(log_severity_to_syslog(msg.severity()), "%s", str.c_str()); +} + +void LogOutput::writeStream(const LogMessage &msg) +{ + std::string str = std::string(log_timespec_to_string(msg.timestamp()) + + log_severity_name(msg.severity()) + " " + + msg.category().name() + " " + msg.fileInfo() + " " + + msg.msg()); + stream_->write(str.c_str(), str.size()); + stream_->flush(); +} + /** * \brief Message logger * @@ -60,7 +227,12 @@ class Logger public: static Logger *instance(); - void write(const std::string &msg); + void write(const LogMessage &msg); + + int logSetFile(const char *path); + int logSetStream(std::ostream *stream); + int logSetTarget(LoggingTarget target); + void logSetLevel(const char *category, const char *level); private: Logger(); @@ -69,52 +241,91 @@ private: void parseLogLevels(); static LogSeverity parseLogLevel(const std::string &level); - friend int logSetFile(const char *file); - friend void logSetLevel(const char *category, const char *level); - friend LogCategory; void registerCategory(LogCategory *category); void unregisterCategory(LogCategory *category); + void writeSyslog(const LogMessage &msg); + void writeStream(const LogMessage &msg); + std::unordered_set categories_; std::list> levels_; - std::ofstream file_; - std::ostream *output_; + std::shared_ptr output_; }; /** - * \brief Set the log file - * \param[in] file Full path to the log file + * \enum LoggingTarget + * \brief Log destination type + * \var LoggingTargetNone + * \brief No logging destination + * \sa Logger::logSetTarget + * \var LoggingTargetSyslog + * \brief Log to syslog + * \sa Logger::logSetTarget + * \var LoggingTargetFile + * \brief Log to file + * \sa Logger::logSetFile + * \var LoggingTargetStream + * \brief Log to stream + * \sa Logger::logSetStream + */ + +/** + * \brief Direct logging to a file + * \param[in] path Full path to the log file * - * This function sets the logging output file to \a file. The previous log file, - * if any, is closed, and all new log messages will be written to the new log - * file. + * This function directs the log output to the file identified by \a path. The + * previous log target, if any, is closed, and all new log messages will be + * written to the new log file. * - * If \a file is a null pointer, the log is directed to stderr. If the - * function returns an error, the log file is not changed. + * If the function returns an error, the log target is not changed. * * \return Zero on success, or a negative error code otherwise. */ -int logSetFile(const char *file) +int logSetFile(const char *path) { - Logger *logger = Logger::instance(); - - if (!file) { - logger->output_ = &std::cerr; - logger->file_.close(); - return 0; - } + return Logger::instance()->logSetFile(path); +} - std::ofstream logFile(file); - if (!logFile.good()) - return -EINVAL; +/** + * \brief Direct logging to a stream + * \param[in] stream Stream to send log output to + * + * This function directs the log output to \a stream. The previous log target, + * if any, is closed, and all new log messages will be written to the new log + * stream. + * + * If the function returns an error, the log file is not changed. + * + * \return Zero on success, or a negative error code otherwise. + */ +int logSetStream(std::ostream *stream) +{ + return Logger::instance()->logSetStream(stream); +} - if (logger->output_ != &std::cerr) - logger->file_.close(); - logger->file_ = std::move(logFile); - logger->output_ = &logger->file_; - return 0; +/** + * \brief Set the logging target + * \param[in] target Logging destination + * + * This function sets the logging output to the target specified by \a target. + * The allowed values of \a target are LoggingTargetNone and + * LoggingTargetSyslog. LoggingTargetNone will send the log output to nowhere, + * and LoggingTargetSyslog will send the log output to syslog. The previous + * log target, if any, is closed, and all new log messages will be written to + * the new log destination. + * + * LoggingTargetFile and LoggingTargetStream are not valid values for \a target. + * Use logSetFile() and logSetStream() instead, respectively. + * + * If the function returns an error, the log file is not changed. + * + * \return Zero on success, or a negative error code otherwise. + */ +int logSetTarget(LoggingTarget target) +{ + return Logger::instance()->logSetTarget(target); } /** @@ -134,15 +345,7 @@ int logSetFile(const char *file) */ void logSetLevel(const char *category, const char *level) { - Logger *logger = Logger::instance(); - - LogSeverity severity = Logger::parseLogLevel(level); - if (severity == LogInvalid) - return; - - for (LogCategory *c : logger->categories_) - if (!strcmp(c->name(), category)) - c->setSeverity(severity); + Logger::instance()->logSetLevel(category, level); } /** @@ -161,19 +364,103 @@ Logger *Logger::instance() /** * \brief Write a message to the configured logger output - * \param[in] msg The message string + * \param[in] msg The message object + */ +void Logger::write(const LogMessage &msg) +{ + std::shared_ptr output = std::atomic_load(&output_); + if (!output) + return; + + output->write(msg); +} + +/** + * \brief Set the log file + * \param[in] path Full path to the log file + * + * \sa libcamera::logSetFile() + * + * \return Zero on success, or a negative error code otherwise. + */ +int Logger::logSetFile(const char *path) +{ + std::shared_ptr output = std::make_shared(path); + if (!output->good()) + return -EINVAL; + + std::atomic_store(&output_, output); + return 0; +} + +/** + * \brief Set the log stream + * \param[in] stream Stream to send log output to + * + * \sa libcamera::logSetStream() + * + * \return Zero on success, or a negative error code otherwise. */ -void Logger::write(const std::string &msg) +int Logger::logSetStream(std::ostream *stream) { - output_->write(msg.c_str(), msg.size()); - output_->flush(); + std::shared_ptr output = std::make_shared(stream); + std::atomic_store(&output_, output); + return 0; +} + +/** + * \brief Set the log target + * \param[in] target Log destination + * + * \sa libcamera::logSetTarget() + * + * \return Zero on success, or a negative error code otherwise. + */ +int Logger::logSetTarget(enum LoggingTarget target) +{ + std::shared_ptr output; + + switch (target) { + case LoggingTargetSyslog: + output = std::make_shared(); + std::atomic_store(&output_, output); + break; + case LoggingTargetNone: + output = nullptr; + std::atomic_store(&output_, output); + break; + default: + return -EINVAL; + } + + return 0; +} + +/** + * \brief Set the log level + * \param[in] category Logging category + * \param[in] level Log level + * + * \sa libcamera::logSetLevel() + */ +void Logger::logSetLevel(const char *category, const char *level) +{ + LogSeverity severity = parseLogLevel(level); + if (severity == LogInvalid) + return; + + for (LogCategory *c : categories_) { + if (!strcmp(c->name(), category)) { + c->setSeverity(severity); + break; + } + } } /** * \brief Construct a logger */ Logger::Logger() - : output_(&std::cerr) { parseLogFile(); parseLogLevels(); @@ -183,18 +470,24 @@ Logger::Logger() * \brief Parse the log output file from the environment * * If the LIBCAMERA_LOG_FILE environment variable is set, open the file it - * points to and redirect the logger output to it. Errors are silently ignored - * and don't affect the logger output (set to stderr). + * points to and redirect the logger output to it. If environment variable is + * set to "syslog", then the logger output will be directed to syslog. Errors + * are silently ignored and don't affect the logger output (set to stderr). */ void Logger::parseLogFile() { const char *file = utils::secure_getenv("LIBCAMERA_LOG_FILE"); - if (!file) + if (!file) { + logSetStream(&std::cerr); return; + } + + if (!strcmp(file, "syslog")) { + logSetTarget(LoggingTargetSyslog); + return; + } - file_.open(file); - if (file_.good()) - output_ = &file_; + logSetFile(file); } /** @@ -408,22 +701,6 @@ const LogCategory &LogCategory::defaultCategory() return category; } -static const char *log_severity_name(LogSeverity severity) -{ - static const char *const names[] = { - " DBG", - " INFO", - " WARN", - " ERR", - "FATAL", - }; - - if (static_cast(severity) < ARRAY_SIZE(names)) - return names[severity]; - else - return "UNKWN"; -} - /** * \class LogMessage * \brief Internal log message representation. @@ -493,18 +770,11 @@ LogMessage::LogMessage(LogMessage &&other) void LogMessage::init(const char *fileName, unsigned int line) { /* Log the timestamp, severity and file information. */ - struct timespec timestamp; - clock_gettime(CLOCK_MONOTONIC, ×tamp); - msgStream_.fill('0'); - msgStream_ << "[" << timestamp.tv_sec / (60 * 60) << ":" - << std::setw(2) << (timestamp.tv_sec / 60) % 60 << ":" - << std::setw(2) << timestamp.tv_sec % 60 << "." - << std::setw(9) << timestamp.tv_nsec << "]"; - msgStream_.fill(' '); + clock_gettime(CLOCK_MONOTONIC, ×tamp_); - msgStream_ << " " << log_severity_name(severity_); - msgStream_ << " " << category_.name(); - msgStream_ << " " << utils::basename(fileName) << ":" << line << " "; + std::ostringstream ossFileInfo; + ossFileInfo << utils::basename(fileName) << ":" << line; + fileInfo_ = ossFileInfo.str(); } LogMessage::~LogMessage() @@ -515,10 +785,8 @@ LogMessage::~LogMessage() msgStream_ << std::endl; - if (severity_ >= category_.severity()) { - std::string msg(msgStream_.str()); - Logger::instance()->write(msg); - } + if (severity_ >= category_.severity()) + Logger::instance()->write(*this); if (severity_ == LogSeverity::LogFatal) std::abort(); @@ -534,6 +802,36 @@ LogMessage::~LogMessage() * \return A reference to the log message stream */ +/** + * \fn LogMessage::timestamp() + * \brief Retrieve the timestamp of the log message + * \return The timestamp of the message + */ + +/** + * \fn LogMessage::severity() + * \brief Retrieve the severity of the log message + * \return The severity of the message + */ + +/** + * \fn LogMessage::category() + * \brief Retrieve the category of the log message + * \return The category of the message + */ + +/** + * \fn LogMessage::fileInfo() + * \brief Retrieve the file info of the log message + * \return The file info of the message + */ + +/** + * \fn LogMessage::msg() + * \brief Getter for the message text of the log message + * \return The message text of the message, as a string + */ + /** * \class Loggable * \brief Base class to support log message extensions From patchwork Tue Jul 16 07:05:06 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 1708 Return-Path: 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 673EE61AA8 for ; Tue, 16 Jul 2019 09:05:21 +0200 (CEST) Received: from emerald.amanokami.net (unknown [IPv6:2a00:79e1:abc:3602:b57a:2dda:be67:ac6e]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1C6C6564; Tue, 16 Jul 2019 09:05:19 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1563260721; bh=vodG6dCzfOrde6CqMZrEEa+sgZMXVDKMDloBFibbjUg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hMvpUtnMa93apSTey7/s6wETbOUsGwldCbveabwl7UxxhaOd+W5aTyzNlMr9dlcrT UNQStuhkTbD4ZCso9Zim7pdIbU4PH41hEMc8tQV7HxIwugBDAHHtN7No5L/1FLXhtn mxu4IFfcX+Z1upEh3MVCkPBHDXxA2OU2cfP9Sg44= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Tue, 16 Jul 2019 16:05:06 +0900 Message-Id: <20190716070508.24589-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190716070508.24589-1-paul.elder@ideasonboard.com> References: <20190716070508.24589-1-paul.elder@ideasonboard.com> Subject: [libcamera-devel] [PATCH v2 2/4] test: logging: add logSetStream test X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 16 Jul 2019 07:05:21 -0000 Test the new logSetStream logging API call. Reorganize the logging API tests at the same time. logSetTarget for the syslog logging destination is not tested. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v2: - add testing for logSetTarget, to test that writing to none doesn't crash, and to test that setting to file or stream fail - make verifyOutput() take an istream (instead of a string) - make total test passage checking nicer test/log.cpp | 106 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 81 insertions(+), 25 deletions(-) diff --git a/test/log.cpp b/test/log.cpp index 89fb5ca..33622f8 100644 --- a/test/log.cpp +++ b/test/log.cpp @@ -29,19 +29,8 @@ LOG_DEFINE_CATEGORY(LogAPITest) class LogAPITest : public Test { protected: - int run() override + void doLogging() { - int fd = open("/tmp", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR); - if (fd < 0) { - cerr << "Failed to open tmp log file" << endl; - return TestFail; - } - - char path[32]; - snprintf(path, sizeof(path), "/proc/self/fd/%u", fd); - - logSetFile(path); - logSetLevel("LogAPITest", "DEBUG"); LOG(LogAPITest, Info) << "good 1"; @@ -55,20 +44,13 @@ protected: logSetLevel("LogAPITest", "WARN"); LOG(LogAPITest, Warning) << "good 5"; LOG(LogAPITest, Info) << "bad"; + } - char buf[1000]; - memset(buf, 0, sizeof(buf)); - lseek(fd, 0, SEEK_SET); - if (read(fd, buf, sizeof(buf)) < 0) { - cerr << "Failed to read tmp log file" << endl; - return TestFail; - } - close(fd); - - std::list goodList = { 1, 3, 5 }; - std::basic_istringstream iss((std::string(buf))); - std::string line; - while (getline(iss, line)) { + int verifyOutput(istream &is) + { + list goodList = { 1, 3, 5 }; + string line; + while (getline(is, line)) { if (goodList.empty()) { cout << "Too many log lines" << endl; return TestFail; @@ -90,6 +72,80 @@ protected: return TestPass; } + + int testFile() + { + int fd = open("/tmp", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR); + if (fd < 0) { + cerr << "Failed to open tmp log file" << endl; + return TestFail; + } + + char path[32]; + snprintf(path, sizeof(path), "/proc/self/fd/%u", fd); + + if (logSetFile(path) < 0) { + cerr << "Failed to set log file" << endl; + return TestFail; + } + + doLogging(); + + char buf[1000]; + memset(buf, 0, sizeof(buf)); + lseek(fd, 0, SEEK_SET); + if (read(fd, buf, sizeof(buf)) < 0) { + cerr << "Failed to read tmp log file" << endl; + return TestFail; + } + close(fd); + + istringstream iss(buf); + return verifyOutput(iss); + } + + int testStream() + { + stringstream log; + /* Never fails, so no need to check return value */ + logSetStream(&log); + + doLogging(); + + return verifyOutput(log); + } + + int testTarget() + { + logSetTarget(LoggingTargetNone); + logSetLevel("LogAPITest", "DEBUG"); + LOG(LogAPITest, Info) << "don't crash please"; + + if (!logSetTarget(LoggingTargetFile)) + return TestFail; + + if (!logSetTarget(LoggingTargetStream)) + return TestFail; + + return TestPass; + } + + int run() override + { + int ret = testFile(); + if (ret != TestPass) + return TestFail; + + ret = testStream(); + if (ret != TestPass) + return TestFail; + + ret = testTarget(); + if (ret != TestPass) + return TestFail; + + return TestPass; + } }; TEST_REGISTER(LogAPITest) From patchwork Tue Jul 16 07:05:07 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 1709 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5511C61AC4 for ; Tue, 16 Jul 2019 09:05:23 +0200 (CEST) Received: from emerald.amanokami.net (unknown [IPv6:2a00:79e1:abc:3602:b57a:2dda:be67:ac6e]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D1E51564; Tue, 16 Jul 2019 09:05:21 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1563260722; bh=PXEJGWmjtr53BuygWSH12LWZDyh1PzFNMoJSnjKf3G0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=o2/elEmEFMP612VBTk4FAPGQTK3QZQgXDsDY/gX+WTToC7JS/Zc3Rr5fvArurNAV8 nbx4qfLzHs/LQqE5vkJlwUJiV+Q56HVKhGfSk+cOJVJc0CfvGEH8GaF07EHyU81I4Z zrYwhIQdmis52XSWwKpcfFRvRas2muIBEu5Bb8w8= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Tue, 16 Jul 2019 16:05:07 +0900 Message-Id: <20190716070508.24589-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190716070508.24589-1-paul.elder@ideasonboard.com> References: <20190716070508.24589-1-paul.elder@ideasonboard.com> Subject: [libcamera-devel] [PATCH v2 3/4] test: logging: add logging process test X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 16 Jul 2019 07:05:23 -0000 Add a test to test that logging works in isolated child processes, Only logSetFile is tested, because stdout and stderr are closed for isolated child processes, and syslog and the none logging destinations are expected to be the same as non-isolated processes. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v2: - unlink the log file - move random number generation and process completion connection to init - other minor changes test/log_process.cpp | 153 +++++++++++++++++++++++++++++++++++++++++++ test/meson.build | 1 + 2 files changed, 154 insertions(+) create mode 100644 test/log_process.cpp diff --git a/test/log_process.cpp b/test/log_process.cpp new file mode 100644 index 0000000..499a5a8 --- /dev/null +++ b/test/log_process.cpp @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * log_process.cpp - Logging in isolated child process test + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "log.h" +#include "process.h" +#include "test.h" +#include "utils.h" + +using namespace std; +using namespace libcamera; + +static string message("hello from the child"); + +LOG_DEFINE_CATEGORY(LogProcessTest) + +class LogProcessTestChild +{ +public: + int run(int status, int num) + { + usleep(50000); + + string logPath = "/tmp/libcamera.worker.test." + + to_string(num) + ".log"; + if (logSetFile(logPath.c_str()) < 0) + return TestSkip; + + LOG(LogProcessTest, Warning) << message; + + return status; + } +}; + +class LogProcessTest : public Test +{ +protected: + int init() + { + random_device random; + num_ = random(); + + proc_.finished.connect(this, &LogProcessTest::procFinished); + return 0; + } + + int run() + { + EventDispatcher *dispatcher = CameraManager::instance()->eventDispatcher(); + Timer timeout; + + int exitCode = 42; + vector args; + args.push_back(to_string(exitCode)); + args.push_back(to_string(num_)); + int ret = proc_.start("/proc/self/exe", args); + if (ret) { + cerr << "failed to start process" << endl; + return TestFail; + } + + timeout.start(200); + while (timeout.isRunning()) + dispatcher->processEvents(); + + if (exitStatus_ != Process::NormalExit) { + cerr << "process did not exit normally" << endl; + return TestFail; + } + + if (exitCode_ == TestSkip) + return TestSkip; + + if (exitCode_ != exitCode) { + cerr << "exit code should be " << exitCode + << ", actual is " << exitCode_ << endl; + return TestFail; + } + + logPath_ = "/tmp/libcamera.worker.test." + + to_string(num_) + ".log"; + int fd = open(logPath_.c_str(), O_RDONLY, S_IRUSR); + if (fd < 0) { + cerr << "failed to open tmp log file" << endl; + return TestFail; + } + + char buf[200]; + memset(buf, 0, sizeof(buf)); + if (read(fd, buf, sizeof(buf)) < 0) { + cerr << "Failed to read tmp log file" << endl; + return TestFail; + } + close(fd); + + string str(buf); + if (str.find(message) == string::npos) + return TestFail; + + return TestPass; + } + + void cleanup() + { + unlink(logPath_.c_str()); + } + +private: + void procFinished(Process *proc, enum Process::ExitStatus exitStatus, int exitCode) + { + exitStatus_ = exitStatus; + exitCode_ = exitCode; + } + + Process proc_; + enum Process::ExitStatus exitStatus_; + string logPath_; + int exitCode_; + int num_; +}; + +/* + * Can't use TEST_REGISTER() as single binary needs to act as both + * parent and child processes. + */ +int main(int argc, char **argv) +{ + if (argc == 3) { + int status = std::stoi(argv[1]); + int num = std::stoi(argv[2]); + LogProcessTestChild child; + return child.run(status, num); + } + + return LogProcessTest().execute(); +} diff --git a/test/meson.build b/test/meson.build index ad1a2f2..658f283 100644 --- a/test/meson.build +++ b/test/meson.build @@ -23,6 +23,7 @@ public_tests = [ internal_tests = [ ['camera-sensor', 'camera-sensor.cpp'], ['log', 'log.cpp'], + ['log_process', 'log_process.cpp'], ['message', 'message.cpp'], ['signal-threads', 'signal-threads.cpp'], ['threads', 'threads.cpp'], From patchwork Tue Jul 16 07:05:08 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 1710 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E0A6C61AC4 for ; Tue, 16 Jul 2019 09:05:24 +0200 (CEST) Received: from emerald.amanokami.net (unknown [IPv6:2a00:79e1:abc:3602:b57a:2dda:be67:ac6e]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 91E6F564; Tue, 16 Jul 2019 09:05:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1563260724; bh=sJiOclYpNmyTcwbor8cfXt0467JFUMamxaau7g2iDCM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RvU49q9RsCWrNss/lEUfEB5i7Bdck0iNSnSSOztYVJzdqZ03b75L4tixvHmSnaTBO xW4W6p39x1QLtYRJNHRx+0xLyyxLSKSTq5hOGJDss/cuwXCcwEqey5RFy9z/2UM4G8 Sd0WsxE8yXRQjhpM9PaJlgPorzcxeR8jRpJBYXAI= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Tue, 16 Jul 2019 16:05:08 +0900 Message-Id: <20190716070508.24589-4-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190716070508.24589-1-paul.elder@ideasonboard.com> References: <20190716070508.24589-1-paul.elder@ideasonboard.com> Subject: [libcamera-devel] [PATCH v2 4/4] test: logging: move logging tests to a subdirectory X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 16 Jul 2019 07:05:25 -0000 Since there are two logging tests now, move them to their own subdirectory. Update meson as necessary. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- No changes in v2 test/{log.cpp => log/log_api.cpp} | 0 test/{ => log}/log_process.cpp | 0 test/log/meson.build | 13 +++++++++++++ test/meson.build | 3 +-- 4 files changed, 14 insertions(+), 2 deletions(-) rename test/{log.cpp => log/log_api.cpp} (100%) rename test/{ => log}/log_process.cpp (100%) create mode 100644 test/log/meson.build diff --git a/test/log.cpp b/test/log/log_api.cpp similarity index 100% rename from test/log.cpp rename to test/log/log_api.cpp diff --git a/test/log_process.cpp b/test/log/log_process.cpp similarity index 100% rename from test/log_process.cpp rename to test/log/log_process.cpp diff --git a/test/log/meson.build b/test/log/meson.build new file mode 100644 index 0000000..95f6c1a --- /dev/null +++ b/test/log/meson.build @@ -0,0 +1,13 @@ +log_test = [ + ['log_api', 'log_api.cpp'], + ['log_process', 'log_process.cpp'], +] + +foreach t : log_test + exe = executable(t[0], t[1], + dependencies : libcamera_dep, + link_with : test_libraries, + include_directories : test_includes_internal) + + test(t[0], exe, suite : 'log') +endforeach diff --git a/test/meson.build b/test/meson.build index 658f283..b2f809e 100644 --- a/test/meson.build +++ b/test/meson.build @@ -4,6 +4,7 @@ subdir('camera') subdir('controls') subdir('ipa') subdir('ipc') +subdir('log') subdir('media_device') subdir('pipeline') subdir('process') @@ -22,8 +23,6 @@ public_tests = [ internal_tests = [ ['camera-sensor', 'camera-sensor.cpp'], - ['log', 'log.cpp'], - ['log_process', 'log_process.cpp'], ['message', 'message.cpp'], ['signal-threads', 'signal-threads.cpp'], ['threads', 'threads.cpp'],