mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-07-19 07:02:14 +02:00
Rewrite almost entirely
This commit is contained in:
parent
ae54769e5b
commit
2d1d573333
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
91
stratosphere/LogManager/source/impl/lm_logging.cpp
Normal file
91
stratosphere/LogManager/source/impl/lm_logging.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
122
stratosphere/LogManager/source/impl/lm_logging.hpp
Normal file
122
stratosphere/LogManager/source/impl/lm_logging.hpp
Normal 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();
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
@ -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));
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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<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);
|
||||
@ -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<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),
|
||||
};
|
||||
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user