Atmosphere/libraries/libstratosphere/source/lm/srv/lm_logger_impl.cpp
SciresM e9849c74cf
LogManager: implement system module, client api, logging api (#1617)
Some notes:

* Unless `atmosphere!enable_log_manager` is true, Nintendo's log manager will be used instead.
  * This prevents paying memory costs for LM when not enabling logging.
  * To facilitate this, Atmosphere's log manager has a different program id from Nintendo's.
  * `atmosphere!enable_htc` implies `atmosphere!enable_log_manager`.
* LogManager logs to tma, and the SD card (if `lm!enable_sd_card_logging` is true, which it is by default).
* Binary logs are saved to `lm!sd_card_log_output_directory`, which is `atmosphere/binlogs` by default.
2021-09-11 19:32:14 -07:00

147 lines
5.7 KiB
C++

/*
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<uintptr_t>(message.GetPointer()), alignof(impl::LogPacketHeader)));
/* Get a modifiable copy of the header. */
auto *header = const_cast<impl::LogPacketHeader *>(reinterpret_cast<const impl::LogPacketHeader *>(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<const impl::LogPacketHeader *>(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<diag::LogSeverity>(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<const diag::LogMetaData *>(arg);
/* Put the message to uart. */
diag::impl::PutImpl(meta, txt, size);
}, const_cast<diag::LogMetaData *>(std::addressof(log_meta)));
}
#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();
}
}