yuzu/src/core/hle/service/lm/lm.cpp
Lioncash e0b0f4eece lm: Handle threads and modules within the logger
The thread field serves to indicate which thread a log is related to and
provides the length of the thread's name, so we can print that out,
ditto for modules.

Now we can know what threads are potentially spawning off logging
messages (for example Lydie & Suelle bounces between MainThread and
LoadingThread when initializing the game).
2018-08-15 01:05:50 -04:00

213 lines
6 KiB
C++

// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <sstream>
#include <string>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/lm/lm.h"
#include "core/hle/service/service.h"
#include "core/memory.h"
namespace Service::LM {
class ILogger final : public ServiceFramework<ILogger> {
public:
ILogger() : ServiceFramework("ILogger") {
static const FunctionInfo functions[] = {
{0x00000000, &ILogger::Initialize, "Initialize"},
{0x00000001, nullptr, "SetDestination"},
};
RegisterHandlers(functions);
}
private:
struct MessageHeader {
enum Flags : u32_le {
IsHead = 1,
IsTail = 2,
};
enum Severity : u32_le {
Trace,
Info,
Warning,
Error,
Critical,
};
u64_le pid;
u64_le threadContext;
union {
BitField<0, 16, Flags> flags;
BitField<16, 8, Severity> severity;
BitField<24, 8, u32_le> verbosity;
};
u32_le payload_size;
bool IsHeadLog() const {
return flags & Flags::IsHead;
}
bool IsTailLog() const {
return flags & Flags::IsTail;
}
};
static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
/// Log field type
enum class Field : u8 {
Skip = 1,
Message = 2,
Line = 3,
Filename = 4,
Function = 5,
Module = 6,
Thread = 7,
};
/**
* ILogger::Initialize service function
* Inputs:
* 0: 0x00000000
* Outputs:
* 0: ResultCode
*/
void Initialize(Kernel::HLERequestContext& ctx) {
// This function only succeeds - Get that out of the way
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
// Read MessageHeader, despite not doing anything with it right now
MessageHeader header{};
VAddr addr{ctx.BufferDescriptorX()[0].Address()};
const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size};
Memory::ReadBlock(addr, &header, sizeof(MessageHeader));
addr += sizeof(MessageHeader);
if (header.IsHeadLog()) {
log_stream.str("");
log_stream.clear();
}
// Parse out log metadata
u32 line{};
std::string module;
std::string message;
std::string filename;
std::string function;
std::string thread;
while (addr < end_addr) {
const Field field{static_cast<Field>(Memory::Read8(addr++))};
const size_t length{Memory::Read8(addr++)};
if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) {
++addr;
}
switch (field) {
case Field::Skip:
break;
case Field::Message:
message = Memory::ReadCString(addr, length);
break;
case Field::Line:
line = Memory::Read32(addr);
break;
case Field::Filename:
filename = Memory::ReadCString(addr, length);
break;
case Field::Function:
function = Memory::ReadCString(addr, length);
break;
case Field::Module:
module = Memory::ReadCString(addr, length);
break;
case Field::Thread:
thread = Memory::ReadCString(addr, length);
break;
}
addr += length;
}
// Empty log - nothing to do here
if (log_stream.str().empty() && message.empty()) {
return;
}
// Format a nicely printable string out of the log metadata
if (!filename.empty()) {
log_stream << filename << ':';
}
if (!module.empty()) {
log_stream << module << ':';
}
if (!function.empty()) {
log_stream << function << ':';
}
if (line) {
log_stream << std::to_string(line) << ':';
}
if (!thread.empty()) {
log_stream << thread << ':';
}
if (log_stream.str().length() > 0 && log_stream.str().back() == ':') {
log_stream << ' ';
}
log_stream << message;
if (header.IsTailLog()) {
switch (header.severity) {
case MessageHeader::Severity::Trace:
LOG_TRACE(Debug_Emulated, "{}", log_stream.str());
break;
case MessageHeader::Severity::Info:
LOG_INFO(Debug_Emulated, "{}", log_stream.str());
break;
case MessageHeader::Severity::Warning:
LOG_WARNING(Debug_Emulated, "{}", log_stream.str());
break;
case MessageHeader::Severity::Error:
LOG_ERROR(Debug_Emulated, "{}", log_stream.str());
break;
case MessageHeader::Severity::Critical:
LOG_CRITICAL(Debug_Emulated, "{}", log_stream.str());
break;
}
}
}
std::ostringstream log_stream;
};
class LM final : public ServiceFramework<LM> {
public:
explicit LM() : ServiceFramework{"lm"} {
static const FunctionInfo functions[] = {
{0x00000000, &LM::OpenLogger, "OpenLogger"},
};
RegisterHandlers(functions);
}
/**
* LM::OpenLogger service function
* Inputs:
* 0: 0x00000000
* Outputs:
* 0: ResultCode
*/
void OpenLogger(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILogger>();
LOG_DEBUG(Service_LM, "called");
}
};
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<LM>()->InstallAsService(service_manager);
}
} // namespace Service::LM