Merge pull request #5771 from ogniK5377/lm-rework
lm: Recode LM service
This commit is contained in:
		| @@ -400,8 +400,6 @@ add_library(core STATIC | ||||
|     hle/service/ldr/ldr.h | ||||
|     hle/service/lm/lm.cpp | ||||
|     hle/service/lm/lm.h | ||||
|     hle/service/lm/manager.cpp | ||||
|     hle/service/lm/manager.h | ||||
|     hle/service/mig/mig.cpp | ||||
|     hle/service/mig/mig.h | ||||
|     hle/service/mii/manager.cpp | ||||
|   | ||||
| @@ -36,7 +36,6 @@ | ||||
| #include "core/hle/service/apm/controller.h" | ||||
| #include "core/hle/service/filesystem/filesystem.h" | ||||
| #include "core/hle/service/glue/manager.h" | ||||
| #include "core/hle/service/lm/manager.h" | ||||
| #include "core/hle/service/service.h" | ||||
| #include "core/hle/service/sm/sm.h" | ||||
| #include "core/hle/service/time/time_manager.h" | ||||
| @@ -293,8 +292,6 @@ struct System::Impl { | ||||
|                                         perf_stats->GetMeanFrametime()); | ||||
|         } | ||||
|  | ||||
|         lm_manager.Flush(); | ||||
|  | ||||
|         is_powered_on = false; | ||||
|         exit_lock = false; | ||||
|  | ||||
| @@ -398,7 +395,6 @@ struct System::Impl { | ||||
|  | ||||
|     /// Service State | ||||
|     Service::Glue::ARPManager arp_manager; | ||||
|     Service::LM::Manager lm_manager{reporter}; | ||||
|     Service::Time::TimeManager time_manager; | ||||
|  | ||||
|     /// Service manager | ||||
| @@ -720,14 +716,6 @@ const Service::APM::Controller& System::GetAPMController() const { | ||||
|     return impl->apm_controller; | ||||
| } | ||||
|  | ||||
| Service::LM::Manager& System::GetLogManager() { | ||||
|     return impl->lm_manager; | ||||
| } | ||||
|  | ||||
| const Service::LM::Manager& System::GetLogManager() const { | ||||
|     return impl->lm_manager; | ||||
| } | ||||
|  | ||||
| Service::Time::TimeManager& System::GetTimeManager() { | ||||
|     return impl->time_manager; | ||||
| } | ||||
|   | ||||
| @@ -62,10 +62,6 @@ namespace Glue { | ||||
| class ARPManager; | ||||
| } | ||||
|  | ||||
| namespace LM { | ||||
| class Manager; | ||||
| } // namespace LM | ||||
|  | ||||
| namespace SM { | ||||
| class ServiceManager; | ||||
| } // namespace SM | ||||
| @@ -351,9 +347,6 @@ public: | ||||
|     [[nodiscard]] Service::APM::Controller& GetAPMController(); | ||||
|     [[nodiscard]] const Service::APM::Controller& GetAPMController() const; | ||||
|  | ||||
|     [[nodiscard]] Service::LM::Manager& GetLogManager(); | ||||
|     [[nodiscard]] const Service::LM::Manager& GetLogManager() const; | ||||
|  | ||||
|     [[nodiscard]] Service::Time::TimeManager& GetTimeManager(); | ||||
|     [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; | ||||
|  | ||||
|   | ||||
| @@ -5,22 +5,71 @@ | ||||
| #include <sstream> | ||||
| #include <string> | ||||
|  | ||||
| #include <optional> | ||||
| #include <unordered_map> | ||||
| #include <boost/container_hash/hash.hpp> | ||||
| #include "common/logging/log.h" | ||||
| #include "common/scope_exit.h" | ||||
| #include "core/core.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/service/lm/lm.h" | ||||
| #include "core/hle/service/lm/manager.h" | ||||
| #include "core/hle/service/service.h" | ||||
| #include "core/memory.h" | ||||
|  | ||||
| namespace Service::LM { | ||||
| enum class LogSeverity : u8 { | ||||
|     Trace = 0, | ||||
|     Info = 1, | ||||
|     Warning = 2, | ||||
|     Error = 3, | ||||
|     Fatal = 4, | ||||
| }; | ||||
|  | ||||
| // To keep flags out of hashing as well as the payload size | ||||
| struct LogPacketHeaderEntry { | ||||
|     u64_le pid{}; | ||||
|     u64_le tid{}; | ||||
|     LogSeverity severity{}; | ||||
|     u8 verbosity{}; | ||||
|  | ||||
|     auto operator<=>(const LogPacketHeaderEntry&) const = default; | ||||
| }; | ||||
| } // namespace Service::LM | ||||
|  | ||||
| namespace std { | ||||
| template <> | ||||
| struct hash<Service::LM::LogPacketHeaderEntry> { | ||||
|     std::size_t operator()(const Service::LM::LogPacketHeaderEntry& k) const noexcept { | ||||
|         std::size_t seed{}; | ||||
|         boost::hash_combine(seed, k.pid); | ||||
|         boost::hash_combine(seed, k.tid); | ||||
|         boost::hash_combine(seed, k.severity); | ||||
|         boost::hash_combine(seed, k.verbosity); | ||||
|         return seed; | ||||
|     }; | ||||
| }; | ||||
| } // namespace std | ||||
|  | ||||
| namespace Service::LM { | ||||
|  | ||||
| enum class LogDestination : u32 { | ||||
|     TargetManager = 1 << 0, | ||||
|     Uart = 1 << 1, | ||||
|     UartSleep = 1 << 2, | ||||
|     All = 0xffff, | ||||
| }; | ||||
| DECLARE_ENUM_FLAG_OPERATORS(LogDestination); | ||||
|  | ||||
| enum class LogPacketFlags : u8 { | ||||
|     Head = 1 << 0, | ||||
|     Tail = 1 << 1, | ||||
|     LittleEndian = 1 << 2, | ||||
| }; | ||||
| DECLARE_ENUM_FLAG_OPERATORS(LogPacketFlags); | ||||
|  | ||||
| class ILogger final : public ServiceFramework<ILogger> { | ||||
| public: | ||||
|     explicit ILogger(Core::System& system_) | ||||
|         : ServiceFramework{system_, "ILogger"}, manager{system_.GetLogManager()}, | ||||
|           memory{system_.Memory()} { | ||||
|     explicit ILogger(Core::System& system_) : ServiceFramework{system_, "ILogger"} { | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {0, &ILogger::Log, "Log"}, | ||||
|             {1, &ILogger::SetDestination, "SetDestination"}, | ||||
| @@ -30,54 +79,262 @@ public: | ||||
|  | ||||
| private: | ||||
|     void Log(Kernel::HLERequestContext& ctx) { | ||||
|         std::size_t offset{}; | ||||
|         const auto data = ctx.ReadBuffer(); | ||||
|  | ||||
|         // 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); | ||||
|  | ||||
|         FieldMap fields; | ||||
|         while (addr < end_addr) { | ||||
|             const auto field = static_cast<Field>(memory.Read8(addr++)); | ||||
|             const auto length = memory.Read8(addr++); | ||||
|  | ||||
|             if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) { | ||||
|                 ++addr; | ||||
|             } | ||||
|  | ||||
|             SCOPE_EXIT({ addr += length; }); | ||||
|  | ||||
|             if (field == Field::Skip) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             std::vector<u8> data(length); | ||||
|             memory.ReadBlock(addr, data.data(), length); | ||||
|             fields.emplace(field, std::move(data)); | ||||
|         if (data.size() < sizeof(LogPacketHeader)) { | ||||
|             LOG_ERROR(Service_LM, "Data size is too small for header! size={}", data.size()); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         manager.Log({header, std::move(fields)}); | ||||
|         LogPacketHeader header{}; | ||||
|         std::memcpy(&header, data.data(), sizeof(LogPacketHeader)); | ||||
|         offset += sizeof(LogPacketHeader); | ||||
|  | ||||
|         LogPacketHeaderEntry entry{ | ||||
|             .pid = header.pid, | ||||
|             .tid = header.tid, | ||||
|             .severity = header.severity, | ||||
|             .verbosity = header.verbosity, | ||||
|         }; | ||||
|  | ||||
|         if (True(header.flags & LogPacketFlags::Head)) { | ||||
|             std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader)); | ||||
|             std::memcpy(tmp.data(), data.data() + offset, tmp.size()); | ||||
|             entries[entry] = std::move(tmp); | ||||
|         } else { | ||||
|             // Append to existing entry | ||||
|             if (!entries.contains(entry)) { | ||||
|                 LOG_ERROR(Service_LM, "Log entry does not exist!"); | ||||
|                 return; | ||||
|             } | ||||
|             std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader)); | ||||
|  | ||||
|             auto& existing_entry = entries[entry]; | ||||
|             const auto base = existing_entry.size(); | ||||
|             existing_entry.resize(base + (data.size() - sizeof(LogPacketHeader))); | ||||
|             std::memcpy(existing_entry.data() + base, data.data() + offset, | ||||
|                         (data.size() - sizeof(LogPacketHeader))); | ||||
|         } | ||||
|  | ||||
|         if (True(header.flags & LogPacketFlags::Tail)) { | ||||
|             auto it = entries.find(entry); | ||||
|             if (it == entries.end()) { | ||||
|                 LOG_ERROR(Service_LM, "Log entry does not exist!"); | ||||
|                 return; | ||||
|             } | ||||
|             ParseLog(it->first, it->second); | ||||
|             entries.erase(it); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void SetDestination(Kernel::HLERequestContext& ctx) { | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|         const auto destination = rp.PopEnum<DestinationFlag>(); | ||||
|         const auto log_destination = rp.PopEnum<LogDestination>(); | ||||
|  | ||||
|         LOG_DEBUG(Service_LM, "called, destination={:08X}", destination); | ||||
|  | ||||
|         manager.SetDestination(destination); | ||||
|         LOG_DEBUG(Service_LM, "called, destination={}", DestinationToString(log_destination)); | ||||
|         destination = log_destination; | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
|     } | ||||
|  | ||||
|     Manager& manager; | ||||
|     Core::Memory::Memory& memory; | ||||
|     u32 ReadLeb128(const std::vector<u8>& data, std::size_t& offset) { | ||||
|         u32 result{}; | ||||
|         u32 shift{}; | ||||
|         do { | ||||
|             result |= (data[offset] & 0x7f) << shift; | ||||
|             shift += 7; | ||||
|             offset++; | ||||
|             if (offset >= data.size()) { | ||||
|                 break; | ||||
|             } | ||||
|         } while ((data[offset] & 0x80) != 0); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     std::optional<std::string> ReadString(const std::vector<u8>& data, std::size_t& offset, | ||||
|                                           std::size_t length) { | ||||
|         if (length == 0) { | ||||
|             return std::nullopt; | ||||
|         } | ||||
|         const auto length_to_read = std::min(length, data.size() - offset); | ||||
|  | ||||
|         std::string output(length_to_read, '\0'); | ||||
|         std::memcpy(output.data(), data.data() + offset, length_to_read); | ||||
|         offset += length_to_read; | ||||
|         return output; | ||||
|     } | ||||
|  | ||||
|     u32_le ReadAsU32(const std::vector<u8>& data, std::size_t& offset, std::size_t length) { | ||||
|         ASSERT(length == sizeof(u32)); | ||||
|         u32_le output{}; | ||||
|         std::memcpy(&output, data.data() + offset, sizeof(u32)); | ||||
|         offset += length; | ||||
|         return output; | ||||
|     } | ||||
|  | ||||
|     u64_le ReadAsU64(const std::vector<u8>& data, std::size_t& offset, std::size_t length) { | ||||
|         ASSERT(length == sizeof(u64)); | ||||
|         u64_le output{}; | ||||
|         std::memcpy(&output, data.data() + offset, sizeof(u64)); | ||||
|         offset += length; | ||||
|         return output; | ||||
|     } | ||||
|  | ||||
|     void ParseLog(const LogPacketHeaderEntry entry, const std::vector<u8>& log_data) { | ||||
|         // Possible entries | ||||
|         std::optional<std::string> text_log; | ||||
|         std::optional<u32> line_number; | ||||
|         std::optional<std::string> file_name; | ||||
|         std::optional<std::string> function_name; | ||||
|         std::optional<std::string> module_name; | ||||
|         std::optional<std::string> thread_name; | ||||
|         std::optional<u64> log_pack_drop_count; | ||||
|         std::optional<s64> user_system_clock; | ||||
|         std::optional<std::string> process_name; | ||||
|  | ||||
|         std::size_t offset{}; | ||||
|         while (offset < log_data.size()) { | ||||
|             const auto key = static_cast<LogDataChunkKey>(ReadLeb128(log_data, offset)); | ||||
|             const auto chunk_size = ReadLeb128(log_data, offset); | ||||
|  | ||||
|             switch (key) { | ||||
|             case LogDataChunkKey::LogSessionBegin: | ||||
|             case LogDataChunkKey::LogSessionEnd: | ||||
|                 break; | ||||
|             case LogDataChunkKey::TextLog: | ||||
|                 text_log = ReadString(log_data, offset, chunk_size); | ||||
|                 break; | ||||
|             case LogDataChunkKey::LineNumber: | ||||
|                 line_number = ReadAsU32(log_data, offset, chunk_size); | ||||
|                 break; | ||||
|             case LogDataChunkKey::FileName: | ||||
|                 file_name = ReadString(log_data, offset, chunk_size); | ||||
|                 break; | ||||
|             case LogDataChunkKey::FunctionName: | ||||
|                 function_name = ReadString(log_data, offset, chunk_size); | ||||
|                 break; | ||||
|             case LogDataChunkKey::ModuleName: | ||||
|                 module_name = ReadString(log_data, offset, chunk_size); | ||||
|                 break; | ||||
|             case LogDataChunkKey::ThreadName: | ||||
|                 thread_name = ReadString(log_data, offset, chunk_size); | ||||
|                 break; | ||||
|             case LogDataChunkKey::LogPacketDropCount: | ||||
|                 log_pack_drop_count = ReadAsU64(log_data, offset, chunk_size); | ||||
|                 break; | ||||
|             case LogDataChunkKey::UserSystemClock: | ||||
|                 user_system_clock = ReadAsU64(log_data, offset, chunk_size); | ||||
|                 break; | ||||
|             case LogDataChunkKey::ProcessName: | ||||
|                 process_name = ReadString(log_data, offset, chunk_size); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         std::string output_log{}; | ||||
|         if (process_name) { | ||||
|             output_log += fmt::format("Process: {}\n", *process_name); | ||||
|         } | ||||
|         if (module_name) { | ||||
|             output_log += fmt::format("Module: {}\n", *module_name); | ||||
|         } | ||||
|         if (file_name) { | ||||
|             output_log += fmt::format("File: {}\n", *file_name); | ||||
|         } | ||||
|         if (function_name) { | ||||
|             output_log += fmt::format("Function: {}\n", *function_name); | ||||
|         } | ||||
|         if (line_number && *line_number != 0) { | ||||
|             output_log += fmt::format("Line: {}\n", *line_number); | ||||
|         } | ||||
|         output_log += fmt::format("ProcessID: {:X}\n", entry.pid); | ||||
|         output_log += fmt::format("ThreadID: {:X}\n", entry.tid); | ||||
|  | ||||
|         if (text_log) { | ||||
|             output_log += fmt::format("Log Text: {}\n", *text_log); | ||||
|         } | ||||
|  | ||||
|         switch (entry.severity) { | ||||
|         case LogSeverity::Trace: | ||||
|             LOG_DEBUG(Service_LM, "LogManager DEBUG ({}):\n{}", DestinationToString(destination), | ||||
|                       output_log); | ||||
|             break; | ||||
|         case LogSeverity::Info: | ||||
|             LOG_INFO(Service_LM, "LogManager INFO ({}):\n{}", DestinationToString(destination), | ||||
|                      output_log); | ||||
|             break; | ||||
|         case LogSeverity::Warning: | ||||
|             LOG_WARNING(Service_LM, "LogManager WARNING ({}):\n{}", | ||||
|                         DestinationToString(destination), output_log); | ||||
|             break; | ||||
|         case LogSeverity::Error: | ||||
|             LOG_ERROR(Service_LM, "LogManager ERROR ({}):\n{}", DestinationToString(destination), | ||||
|                       output_log); | ||||
|             break; | ||||
|         case LogSeverity::Fatal: | ||||
|             LOG_CRITICAL(Service_LM, "LogManager FATAL ({}):\n{}", DestinationToString(destination), | ||||
|                          output_log); | ||||
|             break; | ||||
|         default: | ||||
|             LOG_CRITICAL(Service_LM, "LogManager UNKNOWN ({}):\n{}", | ||||
|                          DestinationToString(destination), output_log); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static std::string DestinationToString(LogDestination destination) { | ||||
|         if (True(destination & LogDestination::All)) { | ||||
|             return "TargetManager | Uart | UartSleep"; | ||||
|         } | ||||
|         std::string output{}; | ||||
|         if (True(destination & LogDestination::TargetManager)) { | ||||
|             output += "| TargetManager"; | ||||
|         } | ||||
|         if (True(destination & LogDestination::Uart)) { | ||||
|             output += "| Uart"; | ||||
|         } | ||||
|         if (True(destination & LogDestination::UartSleep)) { | ||||
|             output += "| UartSleep"; | ||||
|         } | ||||
|         if (output.length() > 0) { | ||||
|             return output.substr(2); | ||||
|         } | ||||
|         return "No Destination"; | ||||
|     } | ||||
|  | ||||
|     enum class LogDataChunkKey : u32 { | ||||
|         LogSessionBegin = 0, | ||||
|         LogSessionEnd = 1, | ||||
|         TextLog = 2, | ||||
|         LineNumber = 3, | ||||
|         FileName = 4, | ||||
|         FunctionName = 5, | ||||
|         ModuleName = 6, | ||||
|         ThreadName = 7, | ||||
|         LogPacketDropCount = 8, | ||||
|         UserSystemClock = 9, | ||||
|         ProcessName = 10, | ||||
|     }; | ||||
|  | ||||
|     struct LogPacketHeader { | ||||
|         u64_le pid{}; | ||||
|         u64_le tid{}; | ||||
|         LogPacketFlags flags{}; | ||||
|         INSERT_PADDING_BYTES(1); | ||||
|         LogSeverity severity{}; | ||||
|         u8 verbosity{}; | ||||
|         u32_le payload_size{}; | ||||
|     }; | ||||
|     static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size"); | ||||
|  | ||||
|     std::unordered_map<LogPacketHeaderEntry, std::vector<u8>> entries{}; | ||||
|     LogDestination destination{LogDestination::All}; | ||||
| }; | ||||
|  | ||||
| class LM final : public ServiceFramework<LM> { | ||||
|   | ||||
| @@ -1,134 +0,0 @@ | ||||
| // Copyright 2019 yuzu emulator team | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/string_util.h" | ||||
| #include "core/hle/service/lm/manager.h" | ||||
| #include "core/reporter.h" | ||||
|  | ||||
| namespace Service::LM { | ||||
|  | ||||
| std::ostream& operator<<(std::ostream& os, DestinationFlag dest) { | ||||
|     std::vector<std::string> array; | ||||
|     const auto check_single_flag = [dest, &array](DestinationFlag check, std::string name) { | ||||
|         if ((static_cast<u32>(check) & static_cast<u32>(dest)) != 0) { | ||||
|             array.emplace_back(std::move(name)); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     check_single_flag(DestinationFlag::Default, "Default"); | ||||
|     check_single_flag(DestinationFlag::UART, "UART"); | ||||
|     check_single_flag(DestinationFlag::UARTSleeping, "UART (Sleeping)"); | ||||
|  | ||||
|     os << "["; | ||||
|     for (const auto& entry : array) { | ||||
|         os << entry << ", "; | ||||
|     } | ||||
|     return os << "]"; | ||||
| } | ||||
|  | ||||
| std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity) { | ||||
|     switch (severity) { | ||||
|     case MessageHeader::Severity::Trace: | ||||
|         return os << "Trace"; | ||||
|     case MessageHeader::Severity::Info: | ||||
|         return os << "Info"; | ||||
|     case MessageHeader::Severity::Warning: | ||||
|         return os << "Warning"; | ||||
|     case MessageHeader::Severity::Error: | ||||
|         return os << "Error"; | ||||
|     case MessageHeader::Severity::Critical: | ||||
|         return os << "Critical"; | ||||
|     default: | ||||
|         return os << fmt::format("{:08X}", static_cast<u32>(severity)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| std::ostream& operator<<(std::ostream& os, Field field) { | ||||
|     switch (field) { | ||||
|     case Field::Skip: | ||||
|         return os << "Skip"; | ||||
|     case Field::Message: | ||||
|         return os << "Message"; | ||||
|     case Field::Line: | ||||
|         return os << "Line"; | ||||
|     case Field::Filename: | ||||
|         return os << "Filename"; | ||||
|     case Field::Function: | ||||
|         return os << "Function"; | ||||
|     case Field::Module: | ||||
|         return os << "Module"; | ||||
|     case Field::Thread: | ||||
|         return os << "Thread"; | ||||
|     default: | ||||
|         return os << fmt::format("{:08X}", static_cast<u32>(field)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| std::string FormatField(Field type, const std::vector<u8>& data) { | ||||
|     switch (type) { | ||||
|     case Field::Skip: | ||||
|         return ""; | ||||
|     case Field::Line: | ||||
|         if (data.size() >= sizeof(u32)) { | ||||
|             u32 line; | ||||
|             std::memcpy(&line, data.data(), sizeof(u32)); | ||||
|             return fmt::format("{}", line); | ||||
|         } | ||||
|         return "[ERROR DECODING LINE NUMBER]"; | ||||
|     case Field::Message: | ||||
|     case Field::Filename: | ||||
|     case Field::Function: | ||||
|     case Field::Module: | ||||
|     case Field::Thread: | ||||
|         return Common::StringFromFixedZeroTerminatedBuffer( | ||||
|             reinterpret_cast<const char*>(data.data()), data.size()); | ||||
|     default: | ||||
|         UNIMPLEMENTED_MSG("Unimplemented field type={}", type); | ||||
|         return ""; | ||||
|     } | ||||
| } | ||||
|  | ||||
| Manager::Manager(Core::Reporter& reporter) : reporter(reporter) {} | ||||
|  | ||||
| Manager::~Manager() = default; | ||||
|  | ||||
| void Manager::SetEnabled(bool enabled) { | ||||
|     this->enabled = enabled; | ||||
| } | ||||
|  | ||||
| void Manager::SetDestination(DestinationFlag destination) { | ||||
|     this->destination = destination; | ||||
| } | ||||
|  | ||||
| void Manager::Log(LogMessage message) { | ||||
|     if (message.header.IsHeadLog()) { | ||||
|         InitializeLog(); | ||||
|     } | ||||
|  | ||||
|     current_log.emplace_back(std::move(message)); | ||||
|  | ||||
|     if (current_log.back().header.IsTailLog()) { | ||||
|         FinalizeLog(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Manager::Flush() { | ||||
|     FinalizeLog(); | ||||
| } | ||||
|  | ||||
| void Manager::InitializeLog() { | ||||
|     current_log.clear(); | ||||
|  | ||||
|     LOG_INFO(Service_LM, "Initialized new log session"); | ||||
| } | ||||
|  | ||||
| void Manager::FinalizeLog() { | ||||
|     reporter.SaveLogReport(static_cast<u32>(destination), std::move(current_log)); | ||||
|  | ||||
|     LOG_INFO(Service_LM, "Finalized current log session"); | ||||
| } | ||||
|  | ||||
| } // namespace Service::LM | ||||
| @@ -1,106 +0,0 @@ | ||||
| // Copyright 2019 yuzu emulator team | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <map> | ||||
| #include <ostream> | ||||
| #include <vector> | ||||
| #include "common/bit_field.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/swap.h" | ||||
|  | ||||
| namespace Core { | ||||
| class Reporter; | ||||
| } | ||||
|  | ||||
| namespace Service::LM { | ||||
|  | ||||
| enum class DestinationFlag : u32 { | ||||
|     Default = 1, | ||||
|     UART = 2, | ||||
|     UARTSleeping = 4, | ||||
|  | ||||
|     All = 0xFFFF, | ||||
| }; | ||||
|  | ||||
| struct MessageHeader { | ||||
|     enum Flags : u32_le { | ||||
|         IsHead = 1, | ||||
|         IsTail = 2, | ||||
|     }; | ||||
|     enum Severity : u32_le { | ||||
|         Trace, | ||||
|         Info, | ||||
|         Warning, | ||||
|         Error, | ||||
|         Critical, | ||||
|     }; | ||||
|  | ||||
|     u64_le pid; | ||||
|     u64_le thread_context; | ||||
|     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 & IsHead; | ||||
|     } | ||||
|     bool IsTailLog() const { | ||||
|         return flags & IsTail; | ||||
|     } | ||||
| }; | ||||
| static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size"); | ||||
|  | ||||
| enum class Field : u8 { | ||||
|     Skip = 1, | ||||
|     Message = 2, | ||||
|     Line = 3, | ||||
|     Filename = 4, | ||||
|     Function = 5, | ||||
|     Module = 6, | ||||
|     Thread = 7, | ||||
| }; | ||||
|  | ||||
| std::ostream& operator<<(std::ostream& os, DestinationFlag dest); | ||||
| std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity); | ||||
| std::ostream& operator<<(std::ostream& os, Field field); | ||||
|  | ||||
| using FieldMap = std::map<Field, std::vector<u8>>; | ||||
|  | ||||
| struct LogMessage { | ||||
|     MessageHeader header; | ||||
|     FieldMap fields; | ||||
| }; | ||||
|  | ||||
| std::string FormatField(Field type, const std::vector<u8>& data); | ||||
|  | ||||
| class Manager { | ||||
| public: | ||||
|     explicit Manager(Core::Reporter& reporter); | ||||
|     ~Manager(); | ||||
|  | ||||
|     void SetEnabled(bool enabled); | ||||
|     void SetDestination(DestinationFlag destination); | ||||
|  | ||||
|     void Log(LogMessage message); | ||||
|  | ||||
|     void Flush(); | ||||
|  | ||||
| private: | ||||
|     void InitializeLog(); | ||||
|     void FinalizeLog(); | ||||
|  | ||||
|     bool enabled = true; | ||||
|     DestinationFlag destination = DestinationFlag::All; | ||||
|  | ||||
|     std::vector<LogMessage> current_log; | ||||
|  | ||||
|     Core::Reporter& reporter; | ||||
| }; | ||||
|  | ||||
| } // namespace Service::LM | ||||
| @@ -20,7 +20,6 @@ | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/hle/service/lm/manager.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/reporter.h" | ||||
| #include "core/settings.h" | ||||
| @@ -360,55 +359,6 @@ void Reporter::SaveErrorReport(u64 title_id, ResultCode result, | ||||
|     SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); | ||||
| } | ||||
|  | ||||
| void Reporter::SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const { | ||||
|     if (!IsReportingEnabled()) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const auto timestamp = GetTimestamp(); | ||||
|     json out; | ||||
|  | ||||
|     out["yuzu_version"] = GetYuzuVersionData(); | ||||
|     out["report_common"] = | ||||
|         GetReportCommonData(system.CurrentProcess()->GetTitleID(), RESULT_SUCCESS, timestamp); | ||||
|  | ||||
|     out["log_destination"] = | ||||
|         fmt::format("{}", static_cast<Service::LM::DestinationFlag>(destination)); | ||||
|  | ||||
|     auto json_messages = json::array(); | ||||
|     std::transform(messages.begin(), messages.end(), std::back_inserter(json_messages), | ||||
|                    [](const Service::LM::LogMessage& message) { | ||||
|                        json out; | ||||
|                        out["is_head"] = fmt::format("{}", message.header.IsHeadLog()); | ||||
|                        out["is_tail"] = fmt::format("{}", message.header.IsTailLog()); | ||||
|                        out["pid"] = fmt::format("{:016X}", message.header.pid); | ||||
|                        out["thread_context"] = | ||||
|                            fmt::format("{:016X}", message.header.thread_context); | ||||
|                        out["payload_size"] = fmt::format("{:016X}", message.header.payload_size); | ||||
|                        out["flags"] = fmt::format("{:04X}", message.header.flags.Value()); | ||||
|                        out["severity"] = fmt::format("{}", message.header.severity.Value()); | ||||
|                        out["verbosity"] = fmt::format("{:02X}", message.header.verbosity); | ||||
|  | ||||
|                        auto fields = json::array(); | ||||
|                        std::transform(message.fields.begin(), message.fields.end(), | ||||
|                                       std::back_inserter(fields), [](const auto& kv) { | ||||
|                                           json out; | ||||
|                                           out["type"] = fmt::format("{}", kv.first); | ||||
|                                           out["data"] = | ||||
|                                               Service::LM::FormatField(kv.first, kv.second); | ||||
|                                           return out; | ||||
|                                       }); | ||||
|  | ||||
|                        out["fields"] = std::move(fields); | ||||
|                        return out; | ||||
|                    }); | ||||
|  | ||||
|     out["log_messages"] = std::move(json_messages); | ||||
|  | ||||
|     SaveToFile(std::move(out), | ||||
|                GetPath("log_report", system.CurrentProcess()->GetTitleID(), timestamp)); | ||||
| } | ||||
|  | ||||
| void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, | ||||
|                                           std::string log_message) const { | ||||
|     if (!IsReportingEnabled()) | ||||
|   | ||||
| @@ -72,9 +72,6 @@ public: | ||||
|     void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, | ||||
|                                     std::string log_message) const; | ||||
|  | ||||
|     // Used by lm services | ||||
|     void SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const; | ||||
|  | ||||
|     // Can be used anywhere to generate a backtrace and general info report at any point during | ||||
|     // execution. Not intended to be used for anything other than debugging or testing. | ||||
|     void SaveUserReport() const; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 bunnei
					bunnei