// 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 #include #include #include #include #include #include #include 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 lenght of fidl message required to encode this msg object. class LogMessage { public: LogMessage(fbl::String msg, std::initializer_list 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(FIDL_ALLOC_PRESENT); lm->msg.size = msg_.length(); lm->msg.data = reinterpret_cast(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 tags_; uint64_t pid_; uint32_t dropped_logs_; }; // Encode |log_msg| and fills up fidl message object to be sent to LogListener // 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(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 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, int oridinal, const fbl::Vector& 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 (oridinal == fuchsia_logger_LogListenerLogManyOrdinal) { 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(msg); hdr->ordinal = oridinal; fuchsia_logger_LogMessage* fidl_log_msg = reinterpret_cast(hdr + 1); if (oridinal == fuchsia_logger_LogListenerLogManyOrdinal) { fidl_vector_t* vector = reinterpret_cast(hdr + 1); vector->count = n_msgs; vector->data = reinterpret_cast(FIDL_ALLOC_PRESENT); fidl_log_msg = reinterpret_cast(vector + 1); } fidl_string_t* strings = reinterpret_cast(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((reinterpret_cast(strings)) + offset); } } return listener.write(0, msg, static_cast(msg_len), NULL, 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 log_msgs; log_msgs.push_back(std::move(log_msg)); return SendLogMessagesHelper(listener, fuchsia_logger_LogListenerLogOrdinal, 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& log_msgs) { return SendLogMessagesHelper(listener, fuchsia_logger_LogListenerLogManyOrdinal, log_msgs); } bool TestLog() { BEGIN_TEST; 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 = fbl::make_unique(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))); LogMessage msg2("my message", {"tag123"}); ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg2))); ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); fflush(buf_file); ASSERT_STR_EQ(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()); fflush(buf_file); ASSERT_STR_EQ(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); END_TEST; } bool TestLogMany() { BEGIN_TEST; 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 = fbl::make_unique(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 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()); fflush(buf_file); ASSERT_STR_EQ(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()); fflush(buf_file); ASSERT_STR_EQ(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); END_TEST; } bool TestDroppedLogs() { BEGIN_TEST; 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 = fbl::make_unique(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))); LogMessage msg2("my message2", 1); ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg2))); LogMessage msg3("my message3", 1, 1011); ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg3))); LogMessage msg4("my message4", 1, 1011); ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg4))); LogMessage msg5("my message5", 2, 1011); ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg5))); LogMessage msg6("my message6", 2); ASSERT_EQ(ZX_OK, SendLogMessage(listener, std::move(msg6))); ASSERT_EQ(ZX_OK, log_listener->RunUntilIdle()); fflush(buf_file); ASSERT_STR_EQ(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); END_TEST; } BEGIN_TEST_CASE(LogListenerTests) RUN_TEST(TestLog) RUN_TEST(TestLogMany) RUN_TEST(TestDroppedLogs) END_TEST_CASE(LogListenerTests) } // namespace } // namespace runtests