| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fuchsia/logger/c/fidl.h> |
| #include <lib/fidl/txn_header.h> |
| |
| #include <utility> |
| |
| #include <fbl/string.h> |
| #include <fbl/vector.h> |
| #include <runtests-utils/log-exporter.h> |
| #include <zxtest/zxtest.h> |
| |
| namespace runtests { |
| namespace { |
| |
| // This class is the helper class which will encapsulate a LogMessage and then |
| // fill default values for fidl log message object. It also helps in determining |
| // the length of fidl message required to encode this msg object. |
| class LogMessage { |
| public: |
| LogMessage(fbl::String msg, std::initializer_list<fbl::String> tags, uint32_t dropped_logs = 0, |
| uint64_t pid = 1024) |
| : msg_(msg), tags_(tags), pid_(pid), dropped_logs_(dropped_logs) {} |
| |
| LogMessage(fbl::String msg, uint32_t dropped_logs = 0, uint64_t pid = 1024) |
| : LogMessage(msg, {}, dropped_logs, pid) {} |
| |
| size_t TagsCount() const { return tags_.size(); } |
| |
| const fbl::String* tag(size_t index) const { return &tags_[index]; } |
| |
| const fbl::String* msg() const { return &msg_; } |
| |
| void SetFidlLogMessageValues(fuchsia_logger_LogMessage* lm) const { |
| lm->pid = pid_; |
| lm->tid = 1034; |
| lm->time = 93892493921; |
| lm->severity = fuchsia_logger_LogLevelFilter_INFO; |
| lm->dropped_logs = dropped_logs_; |
| lm->tags.count = tags_.size(); |
| lm->tags.data = reinterpret_cast<void*>(FIDL_ALLOC_PRESENT); |
| lm->msg.size = msg_.length(); |
| lm->msg.data = reinterpret_cast<char*>(FIDL_ALLOC_PRESENT); |
| } |
| |
| size_t GetFidlStringLen() const { |
| size_t len = 0; |
| for (size_t i = 0; i < tags_.size(); i++) { |
| len += FIDL_ALIGN(tags_[i].length()); |
| } |
| return len + FIDL_ALIGN(msg_.length()); |
| } |
| |
| private: |
| fbl::String msg_; |
| fbl::Vector<fbl::String> tags_; |
| uint64_t pid_; |
| uint32_t dropped_logs_; |
| }; |
| |
| // Encode |log_msg| and fills up fidl message object to be sent to |
| // LogListenerSafe implementation. |
| size_t FillLogMessagePayload(fuchsia_logger_LogMessage* lm, fidl_string_t* strings, |
| const LogMessage& log_msg) { |
| log_msg.SetFidlLogMessageValues(lm); |
| uint8_t* payload = reinterpret_cast<uint8_t*>(strings + log_msg.TagsCount()); |
| size_t offset = 0; |
| |
| // write tags |
| for (size_t i = 0; i < log_msg.TagsCount(); ++i) { |
| size_t size = log_msg.tag(i)->length(); |
| strings[i].size = size; |
| strings[i].data = reinterpret_cast<char*> FIDL_ALLOC_PRESENT; |
| memcpy(payload + offset, log_msg.tag(i)->data(), size); |
| offset += FIDL_ALIGN(size); |
| } |
| |
| // write msg |
| auto msg = log_msg.msg(); |
| memcpy(payload + offset, msg->data(), msg->length()); |
| offset += FIDL_ALIGN(msg->length()); |
| return log_msg.TagsCount() * sizeof(fidl_string_t) + offset; |
| } |
| |
| // Encode |log_msgs| with help from other helpers and writes the msg to |
| // |listener|. |
| zx_status_t SendLogMessagesHelper(const zx::channel& listener, uint64_t ordinal, |
| const fbl::Vector<LogMessage>& log_msgs) { |
| size_t n_msgs = log_msgs.size(); |
| if (n_msgs == 0) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| size_t tags_n_msgs = 0; |
| size_t len = 0; |
| for (size_t i = 0; i < n_msgs; i++) { |
| tags_n_msgs += log_msgs[i].TagsCount(); |
| len += log_msgs[i].GetFidlStringLen(); |
| } |
| |
| size_t msg_len = sizeof(fidl_message_header_t) + n_msgs * sizeof(fuchsia_logger_LogMessage) + |
| tags_n_msgs * sizeof(fidl_string_t) + FIDL_ALIGN(len); |
| if (ordinal == fuchsia_logger_LogListenerSafeLogManyOrdinal || |
| ordinal == fuchsia_logger_LogListenerSafeLogManyOrdinal) { |
| msg_len += sizeof(fidl_vector_t); |
| } |
| if (msg_len > UINT32_MAX) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| uint8_t msg[msg_len]; |
| memset(msg, 0, msg_len); |
| fidl_message_header_t* hdr = reinterpret_cast<fidl_message_header_t*>(msg); |
| fidl::InitTxnHeader(hdr, 0, ordinal, fidl::MessageDynamicFlags::kStrictMethod); |
| fuchsia_logger_LogMessage* fidl_log_msg = reinterpret_cast<fuchsia_logger_LogMessage*>(hdr + 1); |
| if (ordinal == fuchsia_logger_LogListenerSafeLogManyOrdinal || |
| ordinal == fuchsia_logger_LogListenerSafeLogManyOrdinal) { |
| fidl_vector_t* vector = reinterpret_cast<fidl_vector_t*>(hdr + 1); |
| vector->count = n_msgs; |
| vector->data = reinterpret_cast<void*>(FIDL_ALLOC_PRESENT); |
| fidl_log_msg = reinterpret_cast<fuchsia_logger_LogMessage*>(vector + 1); |
| } |
| fidl_string_t* strings = reinterpret_cast<fidl_string_t*>(fidl_log_msg + n_msgs); |
| |
| for (size_t i = 0; i < n_msgs; i++) { |
| size_t offset = FillLogMessagePayload(fidl_log_msg, strings, log_msgs[i]); |
| if (i < n_msgs - 1) { |
| fidl_log_msg++; |
| strings = reinterpret_cast<fidl_string_t*>((reinterpret_cast<uint8_t*>(strings)) + offset); |
| } |
| } |
| return listener.write(0, msg, static_cast<uint32_t>(msg_len), nullptr, 0); |
| } |
| |
| // Encodes and writes |log_msg| to |listener|. This will replicate behaviour of |
| // Log call in Log interface. |
| zx_status_t SendLogMessage(const zx::channel& listener, LogMessage log_msg) { |
| fbl::Vector<LogMessage> log_msgs; |
| log_msgs.push_back(std::move(log_msg)); |
| return SendLogMessagesHelper(listener, fuchsia_logger_LogListenerSafeLogOrdinal, log_msgs); |
| } |
| |
| // Encodes and writes |log_msgs| to |listener|. This will replicate behaviour of |
| // LogMany call in Log interface. |
| zx_status_t SendLogMessages(const zx::channel& listener, const fbl::Vector<LogMessage>& log_msgs) { |
| return SendLogMessagesHelper(listener, fuchsia_logger_LogListenerSafeLogManyOrdinal, log_msgs); |
| } |
| |
| zx_status_t ReceiveLogMessagesHelper(const zx::channel& listener, uint64_t ordinal) { |
| zx_status_t status; |
| status = listener.wait_one(ZX_CHANNEL_READABLE, zx::time(0), nullptr); |
| if (status != ZX_OK) { |
| return status; |
| } |
| constexpr uint32_t header_size = sizeof(fidl_message_header_t); |
| alignas(fidl_message_header_t) uint8_t bytes[header_size]; |
| fidl_message_header_t* header = reinterpret_cast<fidl_message_header_t*>(bytes); |
| uint32_t bytes_read = 0u; |
| status = listener.read(0, bytes, nullptr, header_size, 0, &bytes_read, nullptr); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (header->ordinal != ordinal) { |
| return ZX_ERR_WRONG_TYPE; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t ReceiveLogMessage(const zx::channel& listener) { |
| return ReceiveLogMessagesHelper(listener, fuchsia_logger_LogListenerSafeLogOrdinal); |
| } |
| |
| zx_status_t ReceiveLogMessages(const zx::channel& listener) { |
| return ReceiveLogMessagesHelper(listener, fuchsia_logger_LogListenerSafeLogManyOrdinal); |
| } |
| |
| TEST(LogListenerTests, TestLog) { |
| zx::channel listener, listener_request; |
| ASSERT_EQ(ZX_OK, zx::channel::create(0, &listener, &listener_request)); |
| |
| // We expect the log file to be much smaller than this. |
| char buf[1024]; |
| memset(buf, 0, sizeof(buf)); |
| FILE* buf_file = fmemopen(buf, sizeof(buf), "w"); |
| |
| // start listener |
| auto log_listener = std::make_unique<LogExporter>(std::move(listener_request), buf_file); |
| log_listener->set_error_handler([](zx_status_t status) { EXPECT_EQ(ZX_ERR_CANCELED, status); }); |
| |
| LogMessage msg1("my message"); |
| ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg1))); |
| ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); |
| ASSERT_EQ(ZX_OK, ReceiveLogMessage(listener)); |
| LogMessage msg2("my message", {"tag123"}); |
| ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg2))); |
| ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); |
| ASSERT_EQ(ZX_OK, ReceiveLogMessage(listener)); |
| |
| fflush(buf_file); |
| ASSERT_STREQ(R"([00093.892493][1024][1034][] INFO: my message |
| [00093.892493][1024][1034][tag123] INFO: my message |
| )", |
| buf); |
| |
| LogMessage msg3("my message", {"tag123", "tag2"}); |
| ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg3))); |
| ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); |
| ASSERT_EQ(ZX_OK, ReceiveLogMessage(listener)); |
| |
| fflush(buf_file); |
| ASSERT_STREQ(R"([00093.892493][1024][1034][] INFO: my message |
| [00093.892493][1024][1034][tag123] INFO: my message |
| [00093.892493][1024][1034][tag123, tag2] INFO: my message |
| )", |
| buf); |
| } |
| |
| TEST(LogListenerTests, TestLogMany) { |
| zx::channel listener, listener_request; |
| ASSERT_EQ(ZX_OK, zx::channel::create(0, &listener, &listener_request)); |
| |
| // We expect the log file to be much smaller than this. |
| char buf[1024]; |
| memset(buf, 0, sizeof(buf)); |
| FILE* buf_file = fmemopen(buf, sizeof(buf), "w"); |
| |
| // start listener |
| auto log_listener = std::make_unique<LogExporter>(std::move(listener_request), buf_file); |
| log_listener->set_error_handler([](zx_status_t status) { EXPECT_EQ(ZX_ERR_CANCELED, status); }); |
| |
| LogMessage msg1("my message"); |
| LogMessage msg2("my message2", {"tag1", "tag2"}); |
| fbl::Vector<LogMessage> msgs; |
| msgs.push_back(std::move(msg1)); |
| msgs.push_back(std::move(msg2)); |
| ASSERT_EQ(ZX_OK, SendLogMessages(listener, msgs)); |
| ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); |
| ASSERT_EQ(ZX_OK, ReceiveLogMessages(listener)); |
| fflush(buf_file); |
| |
| ASSERT_STREQ(R"([00093.892493][1024][1034][] INFO: my message |
| [00093.892493][1024][1034][tag1, tag2] INFO: my message2 |
| )", |
| buf); |
| LogMessage msg3("my message", {"tag1"}); |
| msgs.reset(); |
| msgs.push_back(std::move(msg3)); |
| ASSERT_EQ(ZX_OK, SendLogMessages(listener, msgs)); |
| ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); |
| ASSERT_EQ(ZX_OK, ReceiveLogMessages(listener)); |
| |
| fflush(buf_file); |
| ASSERT_STREQ(R"([00093.892493][1024][1034][] INFO: my message |
| [00093.892493][1024][1034][tag1, tag2] INFO: my message2 |
| [00093.892493][1024][1034][tag1] INFO: my message |
| )", |
| buf); |
| } |
| |
| TEST(LogListenerTests, TestDroppedLogs) { |
| zx::channel listener, listener_request; |
| ASSERT_EQ(ZX_OK, zx::channel::create(0, &listener, &listener_request)); |
| |
| // We expect the log file to be much smaller than this. |
| char buf[1024]; |
| memset(buf, 0, sizeof(buf)); |
| FILE* buf_file = fmemopen(buf, sizeof(buf), "w"); |
| |
| // start listener |
| auto log_listener = std::make_unique<LogExporter>(std::move(listener_request), buf_file); |
| log_listener->set_error_handler([](zx_status_t status) { EXPECT_EQ(ZX_ERR_CANCELED, status); }); |
| |
| LogMessage msg1("my message1", 1); |
| ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg1))); |
| ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); |
| ASSERT_EQ(ZX_OK, ReceiveLogMessage(listener)); |
| LogMessage msg2("my message2", 1); |
| ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg2))); |
| ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); |
| ASSERT_EQ(ZX_OK, ReceiveLogMessage(listener)); |
| LogMessage msg3("my message3", 1, 1011); |
| ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg3))); |
| ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); |
| ASSERT_EQ(ZX_OK, ReceiveLogMessage(listener)); |
| LogMessage msg4("my message4", 1, 1011); |
| ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg4))); |
| ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); |
| ASSERT_EQ(ZX_OK, ReceiveLogMessage(listener)); |
| LogMessage msg5("my message5", 2, 1011); |
| ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg5))); |
| ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); |
| ASSERT_EQ(ZX_OK, ReceiveLogMessage(listener)); |
| LogMessage msg6("my message6", 2); |
| ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg6))); |
| ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); |
| ASSERT_EQ(ZX_OK, ReceiveLogMessage(listener)); |
| |
| fflush(buf_file); |
| ASSERT_STREQ(R"([00093.892493][1024][1034][] INFO: my message1 |
| [00093.892493][1024][1034][] WARNING: Dropped logs count:1 |
| [00093.892493][1024][1034][] INFO: my message2 |
| [00093.892493][1011][1034][] INFO: my message3 |
| [00093.892493][1011][1034][] WARNING: Dropped logs count:1 |
| [00093.892493][1011][1034][] INFO: my message4 |
| [00093.892493][1011][1034][] INFO: my message5 |
| [00093.892493][1011][1034][] WARNING: Dropped logs count:2 |
| [00093.892493][1024][1034][] INFO: my message6 |
| [00093.892493][1024][1034][] WARNING: Dropped logs count:2 |
| )", |
| buf); |
| } |
| |
| TEST(LogListenerTests, TestBadOutputFile) { |
| zx::channel listener, listener_request; |
| ASSERT_EQ(ZX_OK, zx::channel::create(0, &listener, &listener_request)); |
| |
| char buf[1024]; |
| memset(buf, 0, sizeof(buf)); |
| FILE* buf_file = fmemopen(buf, sizeof(buf), "r"); |
| |
| // start listener |
| auto log_listener = std::make_unique<LogExporter>(std::move(listener_request), buf_file); |
| log_listener->set_error_handler( |
| [](zx_status_t status) { EXPECT_EQ(ZX_ERR_ACCESS_DENIED, status); }); |
| |
| LogMessage msg1("my message"); |
| ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg1))); |
| ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); |
| |
| ASSERT_STREQ("", buf); |
| } |
| |
| } // namespace |
| } // namespace runtests |