| // Copyright 2019 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 "console.h" |
| |
| #include <fidl/fuchsia.io/cpp/wire.h> |
| #include <lib/fdio/vfs.h> |
| #include <lib/fidl-async/cpp/bind.h> |
| #include <lib/zx/channel.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <threads.h> |
| #include <zircon/status.h> |
| #include <zircon/types.h> |
| |
| #include <algorithm> |
| #include <thread> |
| |
| #include <fbl/algorithm.h> |
| #include <fbl/string_buffer.h> |
| |
| zx_status_t Console::Create(RxSource rx_source, TxSink tx_sink, |
| std::vector<std::string> denied_log_tags, |
| fbl::RefPtr<Console>* console) { |
| zx::eventpair event1, event2; |
| zx_status_t status = zx::eventpair::create(0, &event1, &event2); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| *console = |
| fbl::MakeRefCounted<Console>(std::move(event1), std::move(event2), std::move(rx_source), |
| std::move(tx_sink), std::move(denied_log_tags)); |
| return ZX_OK; |
| } |
| |
| Console::Console(zx::eventpair event1, zx::eventpair event2, RxSource rx_source, TxSink tx_sink, |
| std::vector<std::string> denied_log_tags) |
| : rx_fifo_(std::move(event1)), |
| rx_event_(std::move(event2)), |
| rx_source_(std::move(rx_source)), |
| tx_sink_(std::move(tx_sink)), |
| denied_log_tags_(std::move(denied_log_tags)), |
| rx_thread_(std::thread([this]() { DebugReaderThread(); })) {} |
| |
| Console::~Console() { rx_thread_.join(); } |
| |
| zx_status_t Console::Read(void* data, size_t len, size_t* out_actual) { |
| // Don't try to read more than the FIFO can hold. |
| uint64_t to_read = std::min<uint64_t>(len, Fifo::kFifoSize); |
| return rx_fifo_.Read(reinterpret_cast<uint8_t*>(data), to_read, out_actual); |
| } |
| |
| zx_status_t Console::Write(const void* data, size_t len, size_t* out_actual) { |
| zx_status_t status = ZX_OK; |
| size_t total_written = 0; |
| |
| const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data); |
| size_t count = len; |
| while (count > 0) { |
| size_t xfer = std::min(count, kMaxWriteSize); |
| if ((status = tx_sink_(ptr, xfer)) != ZX_OK) { |
| break; |
| } |
| ptr += xfer; |
| count -= xfer; |
| total_written += xfer; |
| } |
| if (total_written > 0) { |
| status = ZX_OK; |
| } |
| *out_actual = total_written; |
| return status; |
| } |
| |
| zx_status_t Console::GetEvent(zx::eventpair* event) const { |
| return rx_event_.duplicate(ZX_RIGHTS_BASIC, event); |
| } |
| |
| void Console::DebugReaderThread() { |
| while (true) { |
| uint8_t ch; |
| zx_status_t status = rx_source_(&ch); |
| if (status != ZX_OK) { |
| return; |
| } |
| size_t actual; |
| rx_fifo_.Write(&ch, 1, &actual); |
| } |
| } |
| |
| zx_status_t Console::Log(fuchsia_logger::wire::LogMessage log) { |
| fbl::StringBuffer<kMaxWriteSize> buffer; |
| auto time = zx::nsec(log.time); |
| buffer.AppendPrintf("[%05ld.%03ld] %05lu:%05lu> [", time.to_secs(), time.to_msecs() % 1000, |
| log.pid, log.tid); |
| auto count = log.tags.count(); |
| for (auto& tag : log.tags) { |
| for (auto& denied_log_tag : denied_log_tags_) { |
| if (strncmp(denied_log_tag.data(), tag.data(), tag.size()) == 0) { |
| return ZX_OK; |
| } |
| } |
| buffer.Append(tag.data(), tag.size()); |
| if (--count > 0) { |
| buffer.Append(", "); |
| } |
| } |
| switch (log.severity) { |
| case static_cast<int32_t>(fuchsia_logger::LogLevelFilter::kTrace): |
| buffer.Append("] TRACE: "); |
| break; |
| case static_cast<int32_t>(fuchsia_logger::LogLevelFilter::kDebug): |
| buffer.Append("] DEBUG: "); |
| break; |
| case static_cast<int32_t>(fuchsia_logger::LogLevelFilter::kInfo): |
| buffer.Append("] INFO: "); |
| break; |
| case static_cast<int32_t>(fuchsia_logger::LogLevelFilter::kWarn): |
| buffer.Append("] WARNING: "); |
| break; |
| case static_cast<int32_t>(fuchsia_logger::LogLevelFilter::kError): |
| buffer.Append("] ERROR: "); |
| break; |
| case static_cast<int32_t>(fuchsia_logger::LogLevelFilter::kFatal): |
| buffer.Append("] FATAL: "); |
| break; |
| default: |
| buffer.AppendPrintf( |
| "] VLOG(%d): ", |
| static_cast<int32_t>(fuchsia_logger::LogLevelFilter::kInfo) - log.severity); |
| } |
| buffer.Append(log.msg.data(), log.msg.size()).Append('\n'); |
| return tx_sink_(reinterpret_cast<const uint8_t*>(buffer.data()), buffer.size()); |
| } |
| |
| void Console::Log(LogRequestView request, LogCompleter::Sync& completer) { |
| zx_status_t status = Log(std::move(request->log)); |
| if (status != ZX_OK) { |
| completer.Close(status); |
| return; |
| } |
| completer.Reply(); |
| } |
| |
| void Console::LogMany(LogManyRequestView request, LogManyCompleter::Sync& completer) { |
| for (auto& log : request->log) { |
| zx_status_t status = Log(std::move(log)); |
| if (status != ZX_OK) { |
| completer.Close(status); |
| return; |
| } |
| } |
| completer.Reply(); |
| } |
| |
| void Console::Done(DoneRequestView request, DoneCompleter::Sync& completer) {} |