mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-05-31 06:48:22 -04:00
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.
This commit is contained in:
parent
a1fb8a91c8
commit
e9849c74cf
94 changed files with 5595 additions and 45 deletions
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::lm::impl {
|
||||
|
||||
enum LogDataChunkKey {
|
||||
LogDataChunkKey_LogSessionBegin = 0,
|
||||
LogDataChunkKey_LogSessionEnd = 1,
|
||||
LogDataChunkKey_TextLog = 2,
|
||||
LogDataChunkKey_LineNumber = 3,
|
||||
LogDataChunkKey_FileName = 4,
|
||||
LogDataChunkKey_FunctionName = 5,
|
||||
LogDataChunkKey_ModuleName = 6,
|
||||
LogDataChunkKey_ThreadName = 7,
|
||||
LogDataChunkKey_LogPacketDropCount = 8,
|
||||
LogDataChunkKey_UserSystemClock = 9,
|
||||
LogDataChunkKey_ProcessName = 10,
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::lm::impl {
|
||||
|
||||
constexpr inline size_t LogPacketHeaderSize = 0x18;
|
||||
|
||||
class LogPacketHeader {
|
||||
private:
|
||||
u64 m_process_id;
|
||||
u64 m_thread_id;
|
||||
u8 m_flags;
|
||||
u8 m_padding;
|
||||
u8 m_severity;
|
||||
u8 m_verbosity;
|
||||
u32 m_payload_size;
|
||||
public:
|
||||
constexpr u64 GetProcessId() const { return m_process_id; }
|
||||
constexpr void SetProcessId(u64 v) { m_process_id = v; }
|
||||
|
||||
constexpr u64 GetThreadId() const { return m_thread_id; }
|
||||
constexpr void SetThreadId(u64 v) { m_thread_id = v; }
|
||||
|
||||
constexpr bool IsHead() const { return (m_flags & (1 << 0)) != 0; }
|
||||
constexpr void SetHead(bool v) { m_flags = (m_flags & ~(1 << 0)) | ((v ? 1 : 0) << 0); }
|
||||
|
||||
constexpr bool IsTail() const { return (m_flags & (1 << 1)) != 0; }
|
||||
constexpr void SetTail(bool v) { m_flags = (m_flags & ~(1 << 1)) | ((v ? 1 : 0) << 1); }
|
||||
|
||||
constexpr bool IsLittleEndian() const { return (m_flags & (1 << 2)) != 0; }
|
||||
constexpr void SetLittleEndian(bool v) { m_flags = (m_flags & ~(1 << 2)) | ((v ? 1 : 0) << 2); }
|
||||
|
||||
constexpr u8 GetSeverity() const { return m_severity; }
|
||||
constexpr void SetSeverity(u8 v) { m_severity = v; }
|
||||
|
||||
constexpr u8 GetVerbosity() const { return m_verbosity; }
|
||||
constexpr void SetVerbosity(u8 v) { m_verbosity = v; }
|
||||
|
||||
constexpr u32 GetPayloadSize() const { return m_payload_size; }
|
||||
constexpr void SetPayloadSize(u32 v) { m_payload_size = v; }
|
||||
};
|
||||
static_assert(sizeof(LogPacketHeader) == LogPacketHeaderSize);
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "lm_log_packet_transmitter_base.hpp"
|
||||
|
||||
namespace ams::lm::impl {
|
||||
|
||||
class LogPacketTransmitter : public LogPacketTransmitterBase {
|
||||
public:
|
||||
LogPacketTransmitter(void *buffer, size_t buffer_size, FlushFunction flush_func, u8 severity, u8 verbosity, u64 process_id, bool head, bool tail)
|
||||
: LogPacketTransmitterBase(buffer, buffer_size, flush_func, severity, verbosity, process_id, head, tail) { /* ... */ }
|
||||
|
||||
void PushLogSessionBegin() {
|
||||
bool value = true;
|
||||
this->PushDataChunk(LogDataChunkKey_LogSessionBegin, std::addressof(value), sizeof(value));
|
||||
}
|
||||
|
||||
void PushLogSessionEnd() {
|
||||
bool value = true;
|
||||
this->PushDataChunk(LogDataChunkKey_LogSessionEnd, std::addressof(value), sizeof(value));
|
||||
}
|
||||
|
||||
void PushTextLog(const char *str, size_t len) {
|
||||
this->PushDataChunk(LogDataChunkKey_TextLog, str, len);
|
||||
}
|
||||
|
||||
void PushLineNumber(u32 line) {
|
||||
this->PushDataChunk(LogDataChunkKey_LineNumber, std::addressof(line), sizeof(line));
|
||||
}
|
||||
|
||||
void PushFileName(const char *str, size_t len) {
|
||||
this->PushDataChunk(LogDataChunkKey_FileName, str, len);
|
||||
}
|
||||
|
||||
void PushFunctionName(const char *str, size_t len) {
|
||||
this->PushDataChunk(LogDataChunkKey_FunctionName, str, len);
|
||||
}
|
||||
|
||||
void PushModuleName(const char *str, size_t len) {
|
||||
this->PushDataChunk(LogDataChunkKey_ModuleName, str, len);
|
||||
}
|
||||
|
||||
void PushThreadName(const char *str, size_t len) {
|
||||
this->PushDataChunk(LogDataChunkKey_ThreadName, str, len);
|
||||
}
|
||||
|
||||
void PushLogPacketDropCount(u64 count) {
|
||||
this->PushDataChunk(LogDataChunkKey_LineNumber, std::addressof(count), sizeof(count));
|
||||
}
|
||||
|
||||
void PushUserSystemClock(s64 posix_time) {
|
||||
this->PushDataChunk(LogDataChunkKey_LineNumber, std::addressof(posix_time), sizeof(posix_time));
|
||||
}
|
||||
|
||||
void PushProcessName(const char *str, size_t len) {
|
||||
this->PushDataChunk(LogDataChunkKey_ProcessName, str, len);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* 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_log_packet_transmitter_base.hpp"
|
||||
|
||||
namespace ams::lm::impl {
|
||||
|
||||
LogPacketTransmitterBase::LogPacketTransmitterBase(void *buffer, size_t buffer_size, FlushFunction flush_func, u8 severity, u8 verbosity, u64 process_id, bool head, bool tail) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(buffer != nullptr);
|
||||
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(buffer), alignof(LogPacketHeader)));
|
||||
AMS_ASSERT(buffer_size >= LogPacketHeaderSize);
|
||||
AMS_ASSERT(flush_func != nullptr);
|
||||
|
||||
/* Construct log packet header. */
|
||||
m_header = std::construct_at(static_cast<LogPacketHeader *>(buffer));
|
||||
|
||||
/* Set fields. */
|
||||
m_start = static_cast<u8 *>(buffer);
|
||||
m_end = m_start + buffer_size;
|
||||
m_payload = m_start + LogPacketHeaderSize;
|
||||
m_current = m_payload;
|
||||
|
||||
m_is_tail = tail;
|
||||
|
||||
m_flush_function = flush_func;
|
||||
|
||||
/* Set header fields. */
|
||||
m_header->SetProcessId(process_id);
|
||||
m_header->SetThreadId(os::GetThreadId(os::GetCurrentThread()));
|
||||
m_header->SetHead(head);
|
||||
m_header->SetLittleEndian(util::IsLittleEndian());
|
||||
m_header->SetSeverity(severity);
|
||||
m_header->SetVerbosity(verbosity);
|
||||
}
|
||||
|
||||
bool LogPacketTransmitterBase::Flush(bool is_tail) {
|
||||
/* Check if we're already flushed. */
|
||||
if (m_current == m_payload) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Flush the data. */
|
||||
m_header->SetTail(is_tail);
|
||||
m_header->SetPayloadSize(static_cast<u32>(m_current - m_payload));
|
||||
const auto result = m_flush_function(m_start, static_cast<size_t>(m_current - m_start));
|
||||
m_header->SetHead(false);
|
||||
|
||||
/* Reset. */
|
||||
m_current = m_payload;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t LogPacketTransmitterBase::GetRemainSize() {
|
||||
return static_cast<size_t>(m_end - m_current);
|
||||
}
|
||||
|
||||
size_t LogPacketTransmitterBase::GetPushableDataSize(size_t uleb_size) {
|
||||
const size_t remain = this->GetRemainSize();
|
||||
if (remain < uleb_size + 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t cmp = remain - uleb_size;
|
||||
u64 mask = 0x7F;
|
||||
size_t n;
|
||||
for (n = 1; mask + n < cmp; ++n) {
|
||||
mask |= mask << 7;
|
||||
}
|
||||
|
||||
return cmp - n;
|
||||
}
|
||||
|
||||
size_t LogPacketTransmitterBase::GetRequiredSizeToPushUleb128(u64 v) {
|
||||
/* Determine bytes needed for uleb128 value. */
|
||||
size_t required = 0;
|
||||
do {
|
||||
++required;
|
||||
v >>= 7;
|
||||
} while (v > 0);
|
||||
|
||||
return required;
|
||||
}
|
||||
|
||||
void LogPacketTransmitterBase::PushUleb128(u64 v) {
|
||||
const u32 Mask = 0x7F;
|
||||
const u32 InverseMask = ~Mask;
|
||||
do {
|
||||
/* Check we're within bounds. */
|
||||
AMS_ASSERT(m_current < m_end);
|
||||
|
||||
/* Write byte. */
|
||||
*(m_current++) = static_cast<u8>(v & Mask) | (((v & InverseMask) != 0) ? 0x80 : 0x00);
|
||||
|
||||
/* Adjust remaining bit range. */
|
||||
v >>= 7;
|
||||
} while (v > 0);
|
||||
}
|
||||
|
||||
void LogPacketTransmitterBase::PushDataChunkImpl(LogDataChunkKey key, const void *data, size_t data_size, bool is_text) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(data != nullptr);
|
||||
|
||||
/* Push as much data as we can, until the chunk is complete. */
|
||||
const u8 *cur = static_cast<const u8 *>(data);
|
||||
const u8 * const end = cur + data_size;
|
||||
const size_t required_key = this->GetRequiredSizeToPushUleb128(key);
|
||||
do {
|
||||
/* Get the pushable size. */
|
||||
size_t pushable_size = this->GetPushableDataSize(required_key);
|
||||
size_t required_size = is_text ? 4 : 1;
|
||||
if (pushable_size < required_size) {
|
||||
this->Flush(false);
|
||||
pushable_size = this->GetPushableDataSize(required_key);
|
||||
}
|
||||
AMS_ASSERT(pushable_size >= required_size);
|
||||
|
||||
/* Determine the current size. */
|
||||
size_t current_size = std::min<size_t>(pushable_size, end - cur);
|
||||
if (is_text) {
|
||||
const auto valid_size = diag::impl::GetValidSizeAsUtf8String(reinterpret_cast<const char *>(cur), current_size);
|
||||
if (valid_size >= 0) {
|
||||
current_size = static_cast<size_t>(valid_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Push data. */
|
||||
this->PushUleb128(key);
|
||||
this->PushUleb128(current_size);
|
||||
this->PushData(cur, current_size);
|
||||
|
||||
/* Advance. */
|
||||
cur = cur + current_size;
|
||||
} while (cur < end);
|
||||
|
||||
/* Check that we pushed all the data. */
|
||||
AMS_ASSERT(cur == end);
|
||||
}
|
||||
|
||||
void LogPacketTransmitterBase::PushData(const void *data, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(data != nullptr);
|
||||
AMS_ASSERT(size <= this->GetRemainSize());
|
||||
|
||||
/* Push the data. */
|
||||
if (size > 0) {
|
||||
std::memcpy(m_current, data, size);
|
||||
m_current += size;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "lm_log_packet_header.hpp"
|
||||
#include "lm_log_data_chunk.hpp"
|
||||
|
||||
namespace ams::lm::impl {
|
||||
|
||||
class LogPacketTransmitterBase {
|
||||
public:
|
||||
using FlushFunction = bool (*)(const u8 *data, size_t size);
|
||||
private:
|
||||
LogPacketHeader *m_header;
|
||||
u8 *m_start;
|
||||
u8 *m_end;
|
||||
u8 *m_payload;
|
||||
u8 *m_current;
|
||||
bool m_is_tail;
|
||||
FlushFunction m_flush_function;
|
||||
protected:
|
||||
LogPacketTransmitterBase(void *buffer, size_t buffer_size, FlushFunction flush_func, u8 severity, u8 verbosity, u64 process_id, bool head, bool tail);
|
||||
|
||||
~LogPacketTransmitterBase() {
|
||||
this->Flush(m_is_tail);
|
||||
}
|
||||
|
||||
void PushDataChunk(LogDataChunkKey key, const void *data, size_t size) {
|
||||
this->PushDataChunkImpl(key, data, size, false);
|
||||
}
|
||||
|
||||
void PushDataChunk(LogDataChunkKey key, const char *str, size_t size) {
|
||||
this->PushDataChunkImpl(key, str, size, true);
|
||||
}
|
||||
public:
|
||||
bool Flush(bool is_tail);
|
||||
private:
|
||||
size_t GetRemainSize();
|
||||
size_t GetPushableDataSize(size_t uleb_size);
|
||||
size_t GetRequiredSizeToPushUleb128(u64 v);
|
||||
|
||||
void PushUleb128(u64 v);
|
||||
void PushDataChunkImpl(LogDataChunkKey key, const void *data, size_t size, bool is_text);
|
||||
void PushData(const void *data, size_t size);
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue