lm: Rename Initialize to Log and implement with manager/reporter

Allows saving and clearer output of data.
This commit is contained in:
Zach Hilman 2019-06-29 17:18:33 -04:00
parent ef9a7fa1f5
commit fa6531ab0f
1 changed files with 22 additions and 140 deletions

View File

@ -17,65 +17,16 @@ namespace Service::LM {
class ILogger final : public ServiceFramework<ILogger> { class ILogger final : public ServiceFramework<ILogger> {
public: public:
ILogger() : ServiceFramework("ILogger") { ILogger(Manager& manager) : ServiceFramework("ILogger"), manager(manager) {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0x00000000, &ILogger::Initialize, "Initialize"}, {0, &ILogger::Log, "Log"},
{0x00000001, &ILogger::SetDestination, "SetDestination"}, {1, &ILogger::SetDestination, "SetDestination"},
}; };
RegisterHandlers(functions); RegisterHandlers(functions);
} }
private: private:
struct MessageHeader { void Log(Kernel::HLERequestContext& ctx) {
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> 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 // This function only succeeds - Get that out of the way
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
@ -87,111 +38,42 @@ private:
Memory::ReadBlock(addr, &header, sizeof(MessageHeader)); Memory::ReadBlock(addr, &header, sizeof(MessageHeader));
addr += sizeof(MessageHeader); addr += sizeof(MessageHeader);
if (header.IsHeadLog()) { FieldMap fields;
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) { while (addr < end_addr) {
const Field field{static_cast<Field>(Memory::Read8(addr++))}; const auto field = static_cast<Field>(Memory::Read8(addr++));
const std::size_t length{Memory::Read8(addr++)}; const auto length = Memory::Read8(addr++);
if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) {
++addr; ++addr;
} }
switch (field) { SCOPE_EXIT({ addr += length; });
case Field::Skip:
break; if (field == Field::Skip) {
case Field::Message: continue;
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; std::vector<u8> data(length);
Memory::ReadBlock(addr, data.data(), length);
fields.emplace(field, std::move(data));
} }
// Empty log - nothing to do here manager.Log({header, std::move(fields)});
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_DEBUG(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;
}
}
}
// This service function is intended to be used as a way to
// redirect logging output to different destinations, however,
// given we always want to see the logging output, it's sufficient
// to do nothing and return success here.
void SetDestination(Kernel::HLERequestContext& ctx) { void SetDestination(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LM, "called"); IPC::RequestParser rp{ctx};
const auto destination = rp.PopEnum<DestinationFlag>();
LOG_DEBUG(Service_LM, "called, destination={:08X}", static_cast<u32>(destination));
manager.SetDestination(destination);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
} }
std::ostringstream log_stream; Manager& manager;
}; };
class LM final : public ServiceFramework<LM> { class LM final : public ServiceFramework<LM> {