Rewrite almost entirely

This commit is contained in:
XorTroll 2020-05-10 17:41:10 +02:00
parent ae54769e5b
commit 2d1d573333
9 changed files with 271 additions and 438 deletions

View File

@ -1,133 +0,0 @@
#include "lm_log_packet.hpp"
#include "lm_scoped_log_file.hpp"
namespace ams::lm::impl {
namespace {
bool can_access_fs = true;
os::Mutex fs_access_lock(false);
}
void SetCanAccessFs(bool can_access) {
std::scoped_lock lk(fs_access_lock);
can_access_fs = can_access;
}
void WriteLogPackets(const char *log_path, std::vector<LogPacket> &packet_list, u64 program_id, LogDestination destination) {
std::scoped_lock lk(fs_access_lock);
if(!can_access_fs) {
/* Only write log if we can access fs. */
return;
}
if(packet_list.empty()) {
/* This shouldn't happen... */
return;
}
/* For everything except the text log, use the first/head packet. */
auto &head_packet = packet_list.front();
const bool is_single_packet = packet_list.size() == 1;
ScopedLogFile log_file(log_path);
log_file.WriteFormat("\n");
log_file.WriteFormat("/----------------------------------------------------------------------------------------------------\\\n");
log_file.WriteFormat("|\n");
log_file.WriteFormat("| Log - %s (0x%016lX)\n", is_single_packet ? "single packet" : "multiple packets", program_id);
/* Log severity. */
log_file.WriteFormat("| Log severity (Trace, Info, Warn, Error, Fatal): ");
switch(static_cast<LogSeverity>(head_packet.header.severity)) {
case LogSeverity::Trace:
log_file.WriteFormat("Trace");
break;
case LogSeverity::Info:
log_file.WriteFormat("Info");
break;
case LogSeverity::Warn:
log_file.WriteFormat("Warn");
break;
case LogSeverity::Error:
log_file.WriteFormat("Error");
break;
case LogSeverity::Fatal:
log_file.WriteFormat("Fatal");
break;
default:
log_file.WriteFormat("Unknown");
break;
}
log_file.WriteFormat("\n");
/* Log verbosity. */
log_file.WriteFormat("| Log verbosity: %d\n", head_packet.header.verbosity);
/* Log destination. */
log_file.WriteFormat("| Log destination: ");
switch(destination) {
case LogDestination::TMA:
log_file.WriteFormat("TMA");
break;
case LogDestination::UART:
log_file.WriteFormat("UART");
break;
case LogDestination::UARTSleeping:
log_file.WriteFormat("UART (when sleeping)");
break;
case LogDestination::All:
log_file.WriteFormat("All (TMA and UART)");
break;
default:
log_file.WriteFormat("Unknown");
break;
}
log_file.WriteFormat("\n");
log_file.WriteFormat("|\n");
/* Process ID and thread ID. */
log_file.WriteFormat("| Process ID: 0x%016lX\n", head_packet.header.process_id);
log_file.WriteFormat("| Thread ID: 0x%016lX\n", head_packet.header.thread_id);
/* File name, line number, function name. */
if(!head_packet.payload.file_name.IsEmpty()) {
/* At <file-name> */
log_file.WriteFormat("| At %s", head_packet.payload.file_name.value);
if(!head_packet.payload.function_name.IsEmpty()) {
/* If function name is present, line number is present too. */
/* At <file-name>:<line-number> (<function-name>) */
log_file.WriteFormat(":%d (%s)", head_packet.payload.line_number.value, head_packet.payload.function_name.value);
}
log_file.WriteFormat("\n");
}
if(!head_packet.payload.process_name.IsEmpty()) {
log_file.WriteFormat("| Process name: %s\n", head_packet.payload.process_name.value);
}
if(!head_packet.payload.module_name.IsEmpty()) {
log_file.WriteFormat("| Module name: %s\n", head_packet.payload.module_name.value);
}
if(!head_packet.payload.thread_name.IsEmpty()) {
log_file.WriteFormat("| Thread name: %s\n", head_packet.payload.thread_name.value);
}
if(!head_packet.payload.log_packet_drop_count.IsEmpty()) {
/* Log packet drop count (what is this used for...?) */
log_file.WriteFormat("| Log packet drop count: %ld\n", head_packet.payload.log_packet_drop_count.value);
}
if(!head_packet.payload.user_system_clock.IsEmpty()) {
/* User system clock - seconds since the title has started. */
log_file.WriteFormat("| Time since title started: %lds\n", head_packet.payload.user_system_clock.value);
}
log_file.WriteFormat("|\n");
log_file.WriteFormat("\\----------------------------------------------------------------------------------------------------/\n");
if(!head_packet.payload.text_log.IsEmpty()) {
/* Concatenate all the packets' messages. */
for(auto &packet: packet_list) {
log_file.WriteFormat("%s", packet.payload.text_log.value);
}
log_file.WriteFormat("\n");
}
}
}

View File

@ -1,184 +0,0 @@
#pragma once
#include <stratosphere.hpp>
#include "../lm_types.hpp"
namespace ams::lm::impl {
enum class LogDataChunkKey : u8 {
SessionBegin = 0,
SessionEnd = 1,
TextLog = 2,
LineNumber = 3,
FileName = 4,
FunctionName = 5,
ModuleName = 6,
ThreadName = 7,
LogPacketDropCount = 8,
UserSystemClock = 9,
ProcessName = 10,
};
enum class LogSeverity : u8 {
Trace = 0,
Info = 1,
Warn = 2,
Error = 3,
Fatal = 4,
};
enum LogPacketFlags : u8 {
LogPacketFlags_Head = BIT(0),
LogPacketFlags_Tail = BIT(1),
};
struct LogPacketHeader {
u64 process_id;
u64 thread_id;
u8 flags;
u8 pad;
u8 severity;
u8 verbosity;
u32 payload_size;
inline constexpr bool IsHead() {
/* Head -> a packet list is being sent, with this packet being the initial one. */
return this->flags & LogPacketFlags_Head;
}
inline constexpr bool IsTail() {
/* Tail -> this is the final packet of the packet list. */
return this->flags & LogPacketFlags_Tail;
}
} PACKED;
static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader definition");
/* Log data chunk base type. */
struct LogDataChunkTypeHeader {
u8 key;
u8 chunk_length;
} PACKED;
template<typename T>
struct LogDataChunkType {
LogDataChunkTypeHeader header;
T value;
inline constexpr bool IsEmpty() {
/* If it's not present, its length will not be set. */
return this->header.chunk_length == 0;
}
} PACKED;
/* Since the length is stored as a single byte, the string will never be larger than 0xFF */
struct LogDataChunkStringType : public LogDataChunkType<char[UINT8_MAX + 1]> {};
/* Actual chunk base types. */
struct LogDataChunkSessionBeginType : public LogDataChunkType<bool> {};
struct LogDataChunkSessionEndType : public LogDataChunkType<bool> {};
struct LogDataChunkTextLogType : public LogDataChunkStringType {};
struct LogDataChunkLineNumberType : public LogDataChunkType<u32> {};
struct LogDataChunkFileNameType : public LogDataChunkStringType {};
struct LogDataChunkFunctionNameType : public LogDataChunkStringType {};
struct LogDataChunkModuleNameType : public LogDataChunkStringType {};
struct LogDataChunkThreadNameType : public LogDataChunkStringType {};
struct LogDataChunkLogPacketDropCountLogType : public LogDataChunkType<u64> {};
struct LogDataChunkUserSystemClockType : public LogDataChunkType<u64> {};
struct LogDataChunkProcessNameType : public LogDataChunkStringType {};
/* One of each chunk type. */
struct LogPacketPayload {
LogDataChunkSessionBeginType session_begin;
LogDataChunkSessionEndType session_end;
LogDataChunkTextLogType text_log;
LogDataChunkLineNumberType line_number;
LogDataChunkFileNameType file_name;
LogDataChunkFunctionNameType function_name;
LogDataChunkModuleNameType module_name;
LogDataChunkThreadNameType thread_name;
LogDataChunkLogPacketDropCountLogType log_packet_drop_count;
LogDataChunkUserSystemClockType user_system_clock;
LogDataChunkProcessNameType process_name;
};
struct LogPacket {
LogPacketHeader header;
LogPacketPayload payload;
};
template<typename C>
inline C ParseStringChunkType(const u8 *chunk_buf) {
static_assert(std::is_base_of_v<LogDataChunkStringType, C>, "Invalid type");
auto chunk_header = *reinterpret_cast<const LogDataChunkTypeHeader*>(chunk_buf);
auto type = *reinterpret_cast<const C*>(chunk_buf);
auto chunk_str_buf = reinterpret_cast<const char*>(chunk_buf + sizeof(LogDataChunkTypeHeader));
/* Zero the string and copy it from the log buffer. */
/* This chunk type can't be directly read like the rest, since the string size isn't fixed like with the other types. */
__builtin_memset(type.value, 0, sizeof(type.value));
strncpy(type.value, chunk_str_buf, chunk_header.chunk_length);
return type;
}
inline LogPacket ParseLogPacket(const void *buf, size_t size) {
LogPacket packet = {};
auto buf8 = reinterpret_cast<const u8*>(buf);
packet.header = *reinterpret_cast<const LogPacketHeader*>(buf);
auto payload_buf = buf8 + sizeof(LogPacketHeader);
size_t offset = 0;
while(offset < packet.header.payload_size) {
/* Each chunk data consists on: u8 id, u8 length, u8 data[length]; */
auto chunk_buf = payload_buf + offset;
auto chunk_header = *reinterpret_cast<const LogDataChunkTypeHeader*>(chunk_buf);
/* Parse the chunk depending on the type (strings require special parsing) */
switch(static_cast<LogDataChunkKey>(chunk_header.key)) {
case LogDataChunkKey::SessionBegin:
packet.payload.session_begin = *reinterpret_cast<const LogDataChunkSessionBeginType*>(chunk_buf);
break;
case LogDataChunkKey::SessionEnd:
packet.payload.session_end = *reinterpret_cast<const LogDataChunkSessionEndType*>(chunk_buf);
break;
case LogDataChunkKey::TextLog:
packet.payload.text_log = ParseStringChunkType<LogDataChunkTextLogType>(chunk_buf);
break;
case LogDataChunkKey::LineNumber:
packet.payload.line_number = *reinterpret_cast<const LogDataChunkLineNumberType*>(chunk_buf);
break;
case LogDataChunkKey::FileName:
packet.payload.file_name = ParseStringChunkType<LogDataChunkFileNameType>(chunk_buf);
break;
case LogDataChunkKey::FunctionName:
packet.payload.function_name = ParseStringChunkType<LogDataChunkFunctionNameType>(chunk_buf);
break;
case LogDataChunkKey::ModuleName:
packet.payload.module_name = ParseStringChunkType<LogDataChunkModuleNameType>(chunk_buf);
break;
case LogDataChunkKey::ThreadName:
packet.payload.thread_name = ParseStringChunkType<LogDataChunkThreadNameType>(chunk_buf);
break;
case LogDataChunkKey::LogPacketDropCount:
packet.payload.log_packet_drop_count = *reinterpret_cast<const LogDataChunkLogPacketDropCountLogType*>(chunk_buf);
break;
case LogDataChunkKey::UserSystemClock:
packet.payload.user_system_clock = *reinterpret_cast<const LogDataChunkUserSystemClockType*>(chunk_buf);
break;
case LogDataChunkKey::ProcessName:
packet.payload.process_name = ParseStringChunkType<LogDataChunkProcessNameType>(chunk_buf);
break;
}
offset += sizeof(LogDataChunkTypeHeader);
offset += chunk_header.chunk_length;
}
return packet;
}
void SetCanAccessFs(bool can_access);
void WriteLogPackets(const char *log_path, std::vector<LogPacket> &packet_list, u64 program_id, LogDestination destination);
}

View File

@ -0,0 +1,91 @@
#include "lm_logging.hpp"
namespace ams::lm::impl {
namespace {
LogInfo g_last_log_info;
bool g_can_access_fs = true;
os::Mutex g_logging_lock(true);
os::SystemEvent g_logging_event(os::EventClearMode_AutoClear, true);
void UpdateLastLogInfo(LogInfo info) {
std::scoped_lock lk(g_logging_lock);
g_last_log_info = info;
}
bool CanAccessFs() {
std::scoped_lock lk(g_logging_lock);
return g_can_access_fs;
}
void SignalLogEvent() {
std::scoped_lock lk(g_logging_lock);
g_logging_event.Signal();
}
}
void SetCanAccessFs(bool can_access) {
std::scoped_lock lk(g_logging_lock);
g_can_access_fs = can_access;
}
Result WriteLogPackets(std::vector<LogPacketBuffer> &packet_list) {
std::scoped_lock lk(g_logging_lock);
if(CanAccessFs() && !packet_list.empty()) {
/* Ensure log directory exists. */
fs::CreateDirectory(DebugLogDirectory);
const auto program_id = packet_list.front().GetProgramId();
/* Ensure process's directory for debug logs exists. */
char process_dir[FS_MAX_PATH] = {};
std::snprintf(process_dir, sizeof(process_dir), "%s/0x%016lX", DebugLogDirectory, program_id);
fs::CreateDirectory(process_dir);
/* Use current system tick as the binary log file's name. */
const LogInfo info = {
.log_id = os::GetSystemTick().GetInt64Value(),
.program_id = program_id,
};
/* Generate binary log path. */
/* All current log packets will be written to the same file. */
char bin_log_path[FS_MAX_PATH] = {};
std::snprintf(bin_log_path, sizeof(bin_log_path), "%s/0x%016lX.bin", process_dir, info.log_id);
/* Ensure the file exists. */
fs::CreateFile(bin_log_path, 0);
/* Now, open the file. */
fs::FileHandle bin_log_file;
R_TRY(fs::OpenFile(&bin_log_file, bin_log_path, fs::OpenMode_Write | fs::OpenMode_AllowAppend));
ON_SCOPE_EXIT { fs::CloseFile(bin_log_file); };
s64 offset = 0;
for(const auto &packet_buf: packet_list) {
/* Write each packet. Don't write the entire buffer since it might have garbage from previous packets at the end. */
const size_t size = packet_buf.GetPacketSize();
R_TRY(fs::WriteFile(bin_log_file, offset, packet_buf.buf.get(), size, fs::WriteOption::Flush));
offset += size;
}
/* Update the last log's info and signal log event. */
UpdateLastLogInfo(info);
SignalLogEvent();
}
return ResultSuccess();
}
LogInfo GetLastLogInfo() {
std::scoped_lock lk(g_logging_lock);
return g_last_log_info;
}
Handle GetLogEventHandle() {
std::scoped_lock lk(g_logging_lock);
return g_logging_event.GetReadableHandle();
}
}

View File

@ -0,0 +1,122 @@
#pragma once
#include "../lm_types.hpp"
namespace ams::lm::impl {
constexpr const char DebugLogDirectory[] = "sdmc:/atmosphere/debug_logs";
enum LogPacketFlags : u8 {
LogPacketFlags_Head = BIT(0),
LogPacketFlags_Tail = BIT(1),
LogPacketFlags_LittleEndian = BIT(2), /* Is this used anywhere? */
};
struct LogPacketHeader {
u64 process_id;
u64 thread_id;
u8 flags;
u8 pad;
u8 severity;
u8 verbosity;
u32 payload_size;
inline constexpr bool IsHead() const {
/* Head -> a packet list is being sent, with this packet being the initial one. */
return this->flags & LogPacketFlags_Head;
}
inline constexpr bool IsTail() const {
/* Tail -> this is the final packet of the packet list. */
return this->flags & LogPacketFlags_Tail;
}
};
static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader definition");
struct LogInfo {
s64 log_id;
u64 program_id;
};
/* Store log packet buffers as a unique_ptr, so that they automatically get disposed after they're used. */
struct LogPacketBuffer {
u64 program_id;
std::unique_ptr<u8[]> buf;
size_t buf_size;
LogPacketBuffer() : program_id(0), buf(nullptr), buf_size(0) {}
LogPacketBuffer(u64 program_id, const void *buf, size_t size) : program_id(program_id), buf(std::make_unique<u8[]>(size)), buf_size(size) {
if(this->buf != nullptr) {
std::memcpy(this->buf.get(), buf, size);
}
}
inline const LogPacketHeader *GetHeader() const {
if(this->buf == nullptr) {
return nullptr;
}
return reinterpret_cast<const LogPacketHeader*>(this->buf.get());
}
inline size_t GetPacketSize() const {
if(this->buf == nullptr) {
return 0;
}
auto header = this->GetHeader();
return sizeof(LogPacketHeader) + header->payload_size;
}
inline size_t GetPacketBufferSize() const {
if(this->buf == nullptr) {
return 0;
}
/* We also send the program ID in the buffer, so include sizeof(u64). */
return this->GetPacketSize() + sizeof(u64);
}
inline bool ValidatePacket() const {
if(this->buf == nullptr) {
return false;
}
/* Ensure that the buffer size is big enough to properly hold the packet. */
/* Input buffers might be bigger than the actual packet size. */
return this->buf_size >= this->GetPacketSize();
}
inline bool IsHead() const {
if(this->buf == nullptr) {
return false;
}
auto header = this->GetHeader();
return header->IsHead();
}
inline bool IsTail() const {
if(this->buf == nullptr) {
return false;
}
auto header = this->GetHeader();
return header->IsTail();
}
inline u64 GetProgramId() const {
return this->program_id;
}
};
inline void ClearDebugLogs() {
fs::DeleteDirectoryRecursively(DebugLogDirectory);
}
void SetCanAccessFs(bool can_access);
Result WriteLogPackets(std::vector<LogPacketBuffer> &packet_list);
LogInfo GetLastLogInfo();
Handle GetLogEventHandle();
}

View File

@ -1,49 +0,0 @@
#include "lm_scoped_log_file.hpp"
namespace ams::lm::impl {
namespace {
os::Mutex g_log_lock(true);
/* Longest strings will be slightly bigger than 0x100 bytes, so this will be enough. */
char g_log_format_buffer[0x400];
}
void ScopedLogFile::WriteString(const char *str) {
std::scoped_lock lk(g_log_lock);
this->Write(str, std::strlen(str));
}
void ScopedLogFile::WriteFormat(const char *fmt, ...) {
std::scoped_lock lk(g_log_lock);
/* Format to the buffer. */
{
std::va_list vl;
va_start(vl, fmt);
std::vsnprintf(g_log_format_buffer, sizeof(g_log_format_buffer), fmt, vl);
va_end(vl);
}
/* Write data. */
this->WriteString(g_log_format_buffer);
}
void ScopedLogFile::Write(const void *data, size_t size) {
std::scoped_lock lk(g_log_lock);
/* If we're not open, we can't write. */
if (!this->IsOpen()) {
return;
}
/* Advance, if we write successfully. */
if (R_SUCCEEDED(fs::WriteFile(this->file, this->offset, data, size, fs::WriteOption::Flush))) {
this->offset += size;
}
}
}

View File

@ -1,43 +0,0 @@
#pragma once
#include <stratosphere.hpp>
namespace ams::lm::impl {
/* Based on creport's ScopedFile. */
class ScopedLogFile {
NON_COPYABLE(ScopedLogFile);
NON_MOVEABLE(ScopedLogFile);
private:
fs::FileHandle file;
s64 offset;
bool opened;
public:
ScopedLogFile(const char *path) : file(), offset(), opened(false) {
/* Ensure that the file exists. */
fs::CreateFile(path, 0);
this->opened = R_SUCCEEDED(fs::OpenFile(std::addressof(this->file), path, fs::OpenMode_Write | fs::OpenMode_AllowAppend));
if(this->opened) {
/* Set current file size as offset to properly append the logs after each other. */
fs::GetFileSize(&this->offset, this->file);
}
}
~ScopedLogFile() {
if(this->opened) {
fs::CloseFile(this->file);
}
}
bool IsOpen() const {
return this->opened;
}
void WriteString(const char *str);
void WriteFormat(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
void Write(const void *data, size_t size);
};
}

View File

@ -6,7 +6,7 @@ extern "C" {
u32 __nx_applet_type = AppletType_None;
u32 __nx_fs_num_sessions = 1;
#define INNER_HEAP_SIZE 0x10000
#define INNER_HEAP_SIZE 0x20000
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
char nx_inner_heap[INNER_HEAP_SIZE];
@ -67,7 +67,7 @@ void __appExit(void) {
namespace {
/* TODO: this domain/domain object amounts work fine, but which ones does N's LogManager actually use? */
/* TODO: these domain/domain object amounts work fine, but which ones does N's LogManager actually use? */
struct ServerOptions {
static constexpr size_t PointerBufferSize = 0;
@ -76,7 +76,7 @@ namespace {
};
constexpr sm::ServiceName LmServiceName = sm::ServiceName::Encode("lm");
constexpr size_t LmMaxSessions = 30;
constexpr size_t LmMaxSessions = 42;
/* lm */
constexpr size_t NumServers = 1;
@ -134,6 +134,9 @@ int main(int argc, char **argv) {
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(lm, IpcServer));
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(lm, IpcServer));
/* Clear logs directory. */
lm::impl::ClearDebugLogs();
/* Add service to manager. */
R_ABORT_UNLESS(g_server_manager.RegisterServer<lm::LogService>(LmServiceName, LmMaxSessions));

View File

@ -2,29 +2,43 @@
namespace ams::lm {
void Logger::WriteAndClearQueuedPackets() {
impl::WriteLogPackets(this->log_path, this->queued_packets, this->program_id, this->destination);
this->queued_packets.clear();
void Logger::WriteQueuedPackets() {
impl::WriteLogPackets(this->queued_packets);
}
void Logger::Log(const sf::InAutoSelectBuffer &buf) {
auto packet = impl::ParseLogPacket(buf.GetPointer(), buf.GetSize());
impl::LogPacketBuffer log_packet_buf(this->program_id, buf.GetPointer(), buf.GetSize());
/* Check if there's a queue already started. */
const bool has_queued_packets = !this->queued_packets.empty();
/* Initially always push the packet. */
this->queued_packets.push_back(packet);
if(has_queued_packets && packet.header.IsTail()) {
/* This is the final packet of the queue - write and clear it. */
this->WriteAndClearQueuedPackets();
if(log_packet_buf.IsHead() && log_packet_buf.IsTail()) {
/* Single packet to be logged - ensure the queue is empty, push it alone on the queue and log it. */
this->queued_packets.clear();
this->queued_packets.push_back(std::move(log_packet_buf));
impl::WriteLogPackets(this->queued_packets);
}
else if(!has_queued_packets && !packet.header.IsHead()) {
/* No head flag and queue not started, so just log the packet and don't start a queue. */
this->WriteAndClearQueuedPackets();
else if(log_packet_buf.IsHead()) {
/* This is the initial packet of a queue - ensure the queue is empty and push it. */
this->queued_packets.clear();
this->queued_packets.push_back(std::move(log_packet_buf));
}
else if(log_packet_buf.IsTail()) {
/* This is the last packet of the queue - push it and log the queue. */
this->queued_packets.push_back(std::move(log_packet_buf));
impl::WriteLogPackets(this->queued_packets);
}
else if(has_queued_packets) {
/* Another packet of the queue - push it. */
this->queued_packets.push_back(std::move(log_packet_buf));
}
else {
/* Invalid packet - but lm always must succeed on this call. */
}
/* Otherwise, the packet is a regular packet of a list, so push it and continue. */
}
void Logger::SetDestination(LogDestination destination) {
/* TODO: shall we make use of this value? */
this->destination = destination;
}
@ -36,4 +50,14 @@ namespace ams::lm {
out_logger.SetValue(std::move(logger));
}
void LogService::AtmosphereGetLogEvent(sf::OutCopyHandle out_event) {
out_event.SetValue(impl::GetLogEventHandle());
}
void LogService::AtmosphereGetLastLogInfo(sf::Out<s64> out_log_id, sf::Out<u64> out_program_id) {
const auto info = impl::GetLastLogInfo();
out_log_id.SetValue(info.log_id);
out_program_id.SetValue(info.program_id);
}
}

View File

@ -1,11 +1,9 @@
#pragma once
#include "impl/lm_log_packet.hpp"
#include "impl/lm_logging.hpp"
namespace ams::lm {
static constexpr const char LogsDirectory[] = "sdmc:/atmosphere/debug_logs";
class Logger : public sf::IServiceObject {
private:
@ -17,16 +15,11 @@ namespace ams::lm {
private:
u64 program_id;
LogDestination destination;
std::vector<impl::LogPacket> queued_packets;
char log_path[FS_MAX_PATH];
std::vector<impl::LogPacketBuffer> queued_packets;
void WriteAndClearQueuedPackets();
void WriteQueuedPackets();
public:
Logger(u64 program_id) : program_id(program_id), destination(LogDestination::TMA), queued_packets(), log_path() {
/* Ensure that the logs directory exists. */
fs::CreateDirectory(LogsDirectory);
sprintf(this->log_path, "%s/%016lX.log", LogsDirectory, program_id);
}
Logger(u64 program_id) : program_id(program_id), destination(LogDestination::TMA), queued_packets() {}
private:
void Log(const sf::InAutoSelectBuffer &buf);
@ -44,14 +37,23 @@ namespace ams::lm {
private:
enum class CommandId {
OpenLogger = 0,
AtmosphereGetLogEvent = 65000,
AtmosphereGetLastLogInfo = 65001,
};
private:
/* Official command. */
void OpenLogger(const sf::ClientProcessId &client_pid, sf::Out<std::shared_ptr<Logger>> out_logger);
/* Atmosphere commands. */
void AtmosphereGetLogEvent(sf::OutCopyHandle out_event);
void AtmosphereGetLastLogInfo(sf::Out<s64> out_log_id, sf::Out<u64> out_program_id);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(OpenLogger),
MAKE_SERVICE_COMMAND_META(AtmosphereGetLogEvent),
MAKE_SERVICE_COMMAND_META(AtmosphereGetLastLogInfo),
};
};