diff --git a/stratosphere/LogManager/source/impl/lm_log_packet.cpp b/stratosphere/LogManager/source/impl/lm_log_packet.cpp deleted file mode 100644 index 12e8280e0..000000000 --- a/stratosphere/LogManager/source/impl/lm_log_packet.cpp +++ /dev/null @@ -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 &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(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 */ - 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 : () */ - 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"); - } - } -} \ No newline at end of file diff --git a/stratosphere/LogManager/source/impl/lm_log_packet.hpp b/stratosphere/LogManager/source/impl/lm_log_packet.hpp deleted file mode 100644 index 0c51f546f..000000000 --- a/stratosphere/LogManager/source/impl/lm_log_packet.hpp +++ /dev/null @@ -1,184 +0,0 @@ - -#pragma once -#include -#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 - 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 {}; - - /* Actual chunk base types. */ - - struct LogDataChunkSessionBeginType : public LogDataChunkType {}; - struct LogDataChunkSessionEndType : public LogDataChunkType {}; - struct LogDataChunkTextLogType : public LogDataChunkStringType {}; - struct LogDataChunkLineNumberType : public LogDataChunkType {}; - struct LogDataChunkFileNameType : public LogDataChunkStringType {}; - struct LogDataChunkFunctionNameType : public LogDataChunkStringType {}; - struct LogDataChunkModuleNameType : public LogDataChunkStringType {}; - struct LogDataChunkThreadNameType : public LogDataChunkStringType {}; - struct LogDataChunkLogPacketDropCountLogType : public LogDataChunkType {}; - struct LogDataChunkUserSystemClockType : public LogDataChunkType {}; - 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 - inline C ParseStringChunkType(const u8 *chunk_buf) { - static_assert(std::is_base_of_v, "Invalid type"); - - auto chunk_header = *reinterpret_cast(chunk_buf); - auto type = *reinterpret_cast(chunk_buf); - auto chunk_str_buf = reinterpret_cast(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(buf); - packet.header = *reinterpret_cast(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(chunk_buf); - /* Parse the chunk depending on the type (strings require special parsing) */ - switch(static_cast(chunk_header.key)) { - case LogDataChunkKey::SessionBegin: - packet.payload.session_begin = *reinterpret_cast(chunk_buf); - break; - case LogDataChunkKey::SessionEnd: - packet.payload.session_end = *reinterpret_cast(chunk_buf); - break; - case LogDataChunkKey::TextLog: - packet.payload.text_log = ParseStringChunkType(chunk_buf); - break; - case LogDataChunkKey::LineNumber: - packet.payload.line_number = *reinterpret_cast(chunk_buf); - break; - case LogDataChunkKey::FileName: - packet.payload.file_name = ParseStringChunkType(chunk_buf); - break; - case LogDataChunkKey::FunctionName: - packet.payload.function_name = ParseStringChunkType(chunk_buf); - break; - case LogDataChunkKey::ModuleName: - packet.payload.module_name = ParseStringChunkType(chunk_buf); - break; - case LogDataChunkKey::ThreadName: - packet.payload.thread_name = ParseStringChunkType(chunk_buf); - break; - case LogDataChunkKey::LogPacketDropCount: - packet.payload.log_packet_drop_count = *reinterpret_cast(chunk_buf); - break; - case LogDataChunkKey::UserSystemClock: - packet.payload.user_system_clock = *reinterpret_cast(chunk_buf); - break; - case LogDataChunkKey::ProcessName: - packet.payload.process_name = ParseStringChunkType(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 &packet_list, u64 program_id, LogDestination destination); - -} \ No newline at end of file diff --git a/stratosphere/LogManager/source/impl/lm_logging.cpp b/stratosphere/LogManager/source/impl/lm_logging.cpp new file mode 100644 index 000000000..2182df4af --- /dev/null +++ b/stratosphere/LogManager/source/impl/lm_logging.cpp @@ -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 &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(); + } + +} \ No newline at end of file diff --git a/stratosphere/LogManager/source/impl/lm_logging.hpp b/stratosphere/LogManager/source/impl/lm_logging.hpp new file mode 100644 index 000000000..8fb0cabf6 --- /dev/null +++ b/stratosphere/LogManager/source/impl/lm_logging.hpp @@ -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 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(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(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 &packet_list); + + LogInfo GetLastLogInfo(); + + Handle GetLogEventHandle(); + +} \ No newline at end of file diff --git a/stratosphere/LogManager/source/impl/lm_scoped_log_file.cpp b/stratosphere/LogManager/source/impl/lm_scoped_log_file.cpp deleted file mode 100644 index 1a6fc85bc..000000000 --- a/stratosphere/LogManager/source/impl/lm_scoped_log_file.cpp +++ /dev/null @@ -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; - } - } - -} \ No newline at end of file diff --git a/stratosphere/LogManager/source/impl/lm_scoped_log_file.hpp b/stratosphere/LogManager/source/impl/lm_scoped_log_file.hpp deleted file mode 100644 index e2f9e0e97..000000000 --- a/stratosphere/LogManager/source/impl/lm_scoped_log_file.hpp +++ /dev/null @@ -1,43 +0,0 @@ - -#pragma once -#include - -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); - }; - -} \ No newline at end of file diff --git a/stratosphere/LogManager/source/lm_main.cpp b/stratosphere/LogManager/source/lm_main.cpp index 95107377f..e54571f14 100644 --- a/stratosphere/LogManager/source/lm_main.cpp +++ b/stratosphere/LogManager/source/lm_main.cpp @@ -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(LmServiceName, LmMaxSessions)); diff --git a/stratosphere/LogManager/source/lm_service.cpp b/stratosphere/LogManager/source/lm_service.cpp index 9c64e8b05..5d7169148 100644 --- a/stratosphere/LogManager/source/lm_service.cpp +++ b/stratosphere/LogManager/source/lm_service.cpp @@ -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 out_log_id, sf::Out out_program_id) { + const auto info = impl::GetLastLogInfo(); + out_log_id.SetValue(info.log_id); + out_program_id.SetValue(info.program_id); + } + } \ No newline at end of file diff --git a/stratosphere/LogManager/source/lm_service.hpp b/stratosphere/LogManager/source/lm_service.hpp index 6181aa571..51750aea4 100644 --- a/stratosphere/LogManager/source/lm_service.hpp +++ b/stratosphere/LogManager/source/lm_service.hpp @@ -1,32 +1,25 @@ #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: enum class CommandId { - Log = 0, + Log = 0, SetDestination = 1, }; private: u64 program_id; LogDestination destination; - std::vector queued_packets; - char log_path[FS_MAX_PATH]; + std::vector 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); @@ -43,15 +36,24 @@ namespace ams::lm { class LogService : public sf::IServiceObject { private: enum class CommandId { - OpenLogger = 0, + OpenLogger = 0, + AtmosphereGetLogEvent = 65000, + AtmosphereGetLastLogInfo = 65001, }; private: + /* Official command. */ void OpenLogger(const sf::ClientProcessId &client_pid, sf::Out> out_logger); + /* Atmosphere commands. */ + void AtmosphereGetLogEvent(sf::OutCopyHandle out_event); + void AtmosphereGetLastLogInfo(sf::Out out_log_id, sf::Out out_program_id); + public: DEFINE_SERVICE_DISPATCH_TABLE { MAKE_SERVICE_COMMAND_META(OpenLogger), + MAKE_SERVICE_COMMAND_META(AtmosphereGetLogEvent), + MAKE_SERVICE_COMMAND_META(AtmosphereGetLastLogInfo), }; };