/* * Copyright (c) Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "lm_logger_impl.hpp" #include "lm_event_log_transmitter.hpp" #include "lm_log_buffer.hpp" #include "lm_log_packet_parser.hpp" #include "lm_log_getter_impl.hpp" #include "../impl/lm_log_packet_header.hpp" namespace ams::lm::srv { bool IsFlushAvailable(); bool g_is_logging_to_custom_sink = false; namespace { constinit u32 g_log_destination = lm::LogDestination_TargetManager; bool SetProcessId(const sf::InAutoSelectBuffer &message, u64 process_id) { /* Check the message. */ AMS_ASSERT(util::IsAligned(reinterpret_cast(message.GetPointer()), alignof(impl::LogPacketHeader))); /* Get a modifiable copy of the header. */ auto *header = const_cast(reinterpret_cast(message.GetPointer())); /* Check that the message size is correct. */ if (impl::LogPacketHeaderSize + header->GetPayloadSize() != message.GetSize()) { return false; } /* Set the header's process id. */ header->SetProcessId(process_id); return true; } void PutLogToTargetManager(const sf::InAutoSelectBuffer &message) { /* Try to push the message. */ bool success; if (IsFlushAvailable()) { success = LogBuffer::GetDefaultInstance().Push(message.GetPointer(), message.GetSize()); } else { success = LogBuffer::GetDefaultInstance().TryPush(message.GetPointer(), message.GetSize()); } /* If we fail, increment dropped packet count. */ if (!success) { EventLogTransmitter::GetDefaultInstance().IncreaseLogPacketDropCount(); } } void PutLogToUart(const sf::InAutoSelectBuffer &message) { #if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING) { /* Get header. */ auto *data = message.GetPointer(); auto data_size = message.GetSize(); const auto *header = reinterpret_cast(data); /* Get the module name. */ char module_name[0x10] = {}; LogPacketParser::ParseModuleName(module_name, sizeof(module_name), data, data_size); /* Create log metadata. */ const diag::LogMetaData log_meta = { .module_name = module_name, .severity = static_cast(header->GetSeverity()), .verbosity = header->GetVerbosity(), }; LogPacketParser::ParseTextLogWithContext(message.GetPointer(), message.GetSize(), [](const char *txt, size_t size, void *arg) { /* Get metadata. */ const auto &meta = *static_cast(arg); /* Put the message to uart. */ diag::impl::PutImpl(meta, txt, size); }, const_cast(std::addressof(log_meta))); } #else { AMS_UNUSED(message); } #endif } void PutLogToCustomSink(const sf::InAutoSelectBuffer &message) { LogPacketParser::ParseTextLogWithContext(message.GetPointer(), message.GetSize(), [](const char *txt, size_t size, void *) { /* Try to push the message. */ if (!LogGetterImpl::GetBuffer().TryPush(txt, size)) { LogGetterImpl::IncreaseLogPacketDropCount(); } }, nullptr); } } LoggerImpl::LoggerImpl(LogServiceImpl *parent, os::ProcessId process_id) : m_parent(parent), m_process_id(process_id.value) { /* Log start of session for process. */ EventLogTransmitter::GetDefaultInstance().PushLogSessionBegin(m_process_id); } LoggerImpl::~LoggerImpl() { /* Log end of session for process. */ EventLogTransmitter::GetDefaultInstance().PushLogSessionEnd(m_process_id); } Result LoggerImpl::Log(const sf::InAutoSelectBuffer &message) { /* Try to set the log process id. */ /* NOTE: Nintendo succeeds here, for whatever purpose, so we will as well. */ R_UNLESS(SetProcessId(message, m_process_id), ResultSuccess()); /* If we should, log to target manager. */ if (g_log_destination & lm::LogDestination_TargetManager) { PutLogToTargetManager(message); } /* If we should, log to uart. */ if ((g_log_destination & lm::LogDestination_Uart) || (IsFlushAvailable() && (g_log_destination & lm::LogDestination_UartIfSleep))) { PutLogToUart(message); } /* If we should, log to custom sink. */ if (g_is_logging_to_custom_sink) { PutLogToCustomSink(message); } return ResultSuccess(); } Result LoggerImpl::SetDestination(u32 destination) { /* Set the log destination. */ g_log_destination = destination; return ResultSuccess(); } }