diff options
| author | 2021-01-20 18:25:15 +1100 | |
|---|---|---|
| committer | 2021-01-20 18:25:15 +1100 | |
| commit | 83f8c1a25ef9a65eac0d000e41373bc212c5ba1c (patch) | |
| tree | 8dc5bd39652d0a3ada62e67fcae90f67564fd656 | |
| parent | Merge pull request #5755 from FearlessTobi/port-5344 (diff) | |
| download | yuzu-83f8c1a25ef9a65eac0d000e41373bc212c5ba1c.tar.gz yuzu-83f8c1a25ef9a65eac0d000e41373bc212c5ba1c.tar.xz yuzu-83f8c1a25ef9a65eac0d000e41373bc212c5ba1c.zip | |
lm: Recode LM service
Rework the service to spit out to logs instead of a seperate file as well as fix any crashes caused by lm.
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/core.cpp | 12 | ||||
| -rw-r--r-- | src/core/core.h | 7 | ||||
| -rw-r--r-- | src/core/hle/service/lm/lm.cpp | 317 | ||||
| -rw-r--r-- | src/core/hle/service/lm/manager.cpp | 134 | ||||
| -rw-r--r-- | src/core/hle/service/lm/manager.h | 106 | ||||
| -rw-r--r-- | src/core/reporter.cpp | 50 | ||||
| -rw-r--r-- | src/core/reporter.h | 3 |
8 files changed, 286 insertions, 345 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2f6b22747..9c9b3195b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -400,8 +400,6 @@ add_library(core STATIC | |||
| 400 | hle/service/ldr/ldr.h | 400 | hle/service/ldr/ldr.h |
| 401 | hle/service/lm/lm.cpp | 401 | hle/service/lm/lm.cpp |
| 402 | hle/service/lm/lm.h | 402 | hle/service/lm/lm.h |
| 403 | hle/service/lm/manager.cpp | ||
| 404 | hle/service/lm/manager.h | ||
| 405 | hle/service/mig/mig.cpp | 403 | hle/service/mig/mig.cpp |
| 406 | hle/service/mig/mig.h | 404 | hle/service/mig/mig.h |
| 407 | hle/service/mii/manager.cpp | 405 | hle/service/mii/manager.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 1a2002dec..86bdc7f6b 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -36,7 +36,6 @@ | |||
| 36 | #include "core/hle/service/apm/controller.h" | 36 | #include "core/hle/service/apm/controller.h" |
| 37 | #include "core/hle/service/filesystem/filesystem.h" | 37 | #include "core/hle/service/filesystem/filesystem.h" |
| 38 | #include "core/hle/service/glue/manager.h" | 38 | #include "core/hle/service/glue/manager.h" |
| 39 | #include "core/hle/service/lm/manager.h" | ||
| 40 | #include "core/hle/service/service.h" | 39 | #include "core/hle/service/service.h" |
| 41 | #include "core/hle/service/sm/sm.h" | 40 | #include "core/hle/service/sm/sm.h" |
| 42 | #include "core/hle/service/time/time_manager.h" | 41 | #include "core/hle/service/time/time_manager.h" |
| @@ -293,8 +292,6 @@ struct System::Impl { | |||
| 293 | perf_stats->GetMeanFrametime()); | 292 | perf_stats->GetMeanFrametime()); |
| 294 | } | 293 | } |
| 295 | 294 | ||
| 296 | lm_manager.Flush(); | ||
| 297 | |||
| 298 | is_powered_on = false; | 295 | is_powered_on = false; |
| 299 | exit_lock = false; | 296 | exit_lock = false; |
| 300 | 297 | ||
| @@ -398,7 +395,6 @@ struct System::Impl { | |||
| 398 | 395 | ||
| 399 | /// Service State | 396 | /// Service State |
| 400 | Service::Glue::ARPManager arp_manager; | 397 | Service::Glue::ARPManager arp_manager; |
| 401 | Service::LM::Manager lm_manager{reporter}; | ||
| 402 | Service::Time::TimeManager time_manager; | 398 | Service::Time::TimeManager time_manager; |
| 403 | 399 | ||
| 404 | /// Service manager | 400 | /// Service manager |
| @@ -720,14 +716,6 @@ const Service::APM::Controller& System::GetAPMController() const { | |||
| 720 | return impl->apm_controller; | 716 | return impl->apm_controller; |
| 721 | } | 717 | } |
| 722 | 718 | ||
| 723 | Service::LM::Manager& System::GetLogManager() { | ||
| 724 | return impl->lm_manager; | ||
| 725 | } | ||
| 726 | |||
| 727 | const Service::LM::Manager& System::GetLogManager() const { | ||
| 728 | return impl->lm_manager; | ||
| 729 | } | ||
| 730 | |||
| 731 | Service::Time::TimeManager& System::GetTimeManager() { | 719 | Service::Time::TimeManager& System::GetTimeManager() { |
| 732 | return impl->time_manager; | 720 | return impl->time_manager; |
| 733 | } | 721 | } |
diff --git a/src/core/core.h b/src/core/core.h index 579a774e4..3a8e040c1 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -62,10 +62,6 @@ namespace Glue { | |||
| 62 | class ARPManager; | 62 | class ARPManager; |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | namespace LM { | ||
| 66 | class Manager; | ||
| 67 | } // namespace LM | ||
| 68 | |||
| 69 | namespace SM { | 65 | namespace SM { |
| 70 | class ServiceManager; | 66 | class ServiceManager; |
| 71 | } // namespace SM | 67 | } // namespace SM |
| @@ -351,9 +347,6 @@ public: | |||
| 351 | [[nodiscard]] Service::APM::Controller& GetAPMController(); | 347 | [[nodiscard]] Service::APM::Controller& GetAPMController(); |
| 352 | [[nodiscard]] const Service::APM::Controller& GetAPMController() const; | 348 | [[nodiscard]] const Service::APM::Controller& GetAPMController() const; |
| 353 | 349 | ||
| 354 | [[nodiscard]] Service::LM::Manager& GetLogManager(); | ||
| 355 | [[nodiscard]] const Service::LM::Manager& GetLogManager() const; | ||
| 356 | |||
| 357 | [[nodiscard]] Service::Time::TimeManager& GetTimeManager(); | 350 | [[nodiscard]] Service::Time::TimeManager& GetTimeManager(); |
| 358 | [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; | 351 | [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; |
| 359 | 352 | ||
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 8e49b068c..f4f6533ad 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp | |||
| @@ -5,22 +5,71 @@ | |||
| 5 | #include <sstream> | 5 | #include <sstream> |
| 6 | #include <string> | 6 | #include <string> |
| 7 | 7 | ||
| 8 | #include <optional> | ||
| 9 | #include <unordered_map> | ||
| 10 | #include <boost/container_hash/hash.hpp> | ||
| 8 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 9 | #include "common/scope_exit.h" | 12 | #include "common/scope_exit.h" |
| 10 | #include "core/core.h" | 13 | #include "core/core.h" |
| 11 | #include "core/hle/ipc_helpers.h" | 14 | #include "core/hle/ipc_helpers.h" |
| 12 | #include "core/hle/service/lm/lm.h" | 15 | #include "core/hle/service/lm/lm.h" |
| 13 | #include "core/hle/service/lm/manager.h" | ||
| 14 | #include "core/hle/service/service.h" | 16 | #include "core/hle/service/service.h" |
| 15 | #include "core/memory.h" | 17 | #include "core/memory.h" |
| 16 | 18 | ||
| 17 | namespace Service::LM { | 19 | namespace Service::LM { |
| 20 | enum class LogSeverity : u8 { | ||
| 21 | Trace = 0, | ||
| 22 | Info = 1, | ||
| 23 | Warning = 2, | ||
| 24 | Error = 3, | ||
| 25 | Fatal = 4, | ||
| 26 | }; | ||
| 27 | |||
| 28 | // To keep flags out of hashing as well as the payload size | ||
| 29 | struct LogPacketHeaderEntry { | ||
| 30 | u64_le pid{}; | ||
| 31 | u64_le tid{}; | ||
| 32 | LogSeverity severity{}; | ||
| 33 | u8 verbosity{}; | ||
| 34 | |||
| 35 | auto operator<=>(const LogPacketHeaderEntry&) const = default; | ||
| 36 | }; | ||
| 37 | } // namespace Service::LM | ||
| 38 | |||
| 39 | namespace std { | ||
| 40 | template <> | ||
| 41 | struct hash<Service::LM::LogPacketHeaderEntry> { | ||
| 42 | std::size_t operator()(const Service::LM::LogPacketHeaderEntry& k) const { | ||
| 43 | std::size_t seed{}; | ||
| 44 | boost::hash_combine(seed, k.pid); | ||
| 45 | boost::hash_combine(seed, k.tid); | ||
| 46 | boost::hash_combine(seed, k.severity); | ||
| 47 | boost::hash_combine(seed, k.verbosity); | ||
| 48 | return seed; | ||
| 49 | }; | ||
| 50 | }; | ||
| 51 | } // namespace std | ||
| 52 | |||
| 53 | namespace Service::LM { | ||
| 54 | |||
| 55 | enum class LogDestination : u32 { | ||
| 56 | TargetManager = 1 << 0, | ||
| 57 | Uart = 1 << 1, | ||
| 58 | UartSleep = 1 << 2, | ||
| 59 | All = 0xffff, | ||
| 60 | }; | ||
| 61 | DECLARE_ENUM_FLAG_OPERATORS(LogDestination); | ||
| 62 | |||
| 63 | enum class LogPacketFlags : u8 { | ||
| 64 | Head = 1 << 0, | ||
| 65 | Tail = 1 << 1, | ||
| 66 | LittleEndian = 1 << 2, | ||
| 67 | }; | ||
| 68 | DECLARE_ENUM_FLAG_OPERATORS(LogPacketFlags); | ||
| 18 | 69 | ||
| 19 | class ILogger final : public ServiceFramework<ILogger> { | 70 | class ILogger final : public ServiceFramework<ILogger> { |
| 20 | public: | 71 | public: |
| 21 | explicit ILogger(Core::System& system_) | 72 | explicit ILogger(Core::System& system_) : ServiceFramework{system_, "ILogger"} { |
| 22 | : ServiceFramework{system_, "ILogger"}, manager{system_.GetLogManager()}, | ||
| 23 | memory{system_.Memory()} { | ||
| 24 | static const FunctionInfo functions[] = { | 73 | static const FunctionInfo functions[] = { |
| 25 | {0, &ILogger::Log, "Log"}, | 74 | {0, &ILogger::Log, "Log"}, |
| 26 | {1, &ILogger::SetDestination, "SetDestination"}, | 75 | {1, &ILogger::SetDestination, "SetDestination"}, |
| @@ -30,54 +79,260 @@ public: | |||
| 30 | 79 | ||
| 31 | private: | 80 | private: |
| 32 | void Log(Kernel::HLERequestContext& ctx) { | 81 | void Log(Kernel::HLERequestContext& ctx) { |
| 82 | std::size_t offset{}; | ||
| 83 | const auto data = ctx.ReadBuffer(); | ||
| 84 | |||
| 33 | // This function only succeeds - Get that out of the way | 85 | // This function only succeeds - Get that out of the way |
| 34 | IPC::ResponseBuilder rb{ctx, 2}; | 86 | IPC::ResponseBuilder rb{ctx, 2}; |
| 35 | rb.Push(RESULT_SUCCESS); | 87 | rb.Push(RESULT_SUCCESS); |
| 36 | 88 | ||
| 37 | // Read MessageHeader, despite not doing anything with it right now | 89 | if (data.size() < sizeof(LogPacketHeader)) { |
| 38 | MessageHeader header{}; | 90 | LOG_ERROR(Service_LM, "Data size is too small for header! size={}", data.size()); |
| 39 | VAddr addr{ctx.BufferDescriptorX()[0].Address()}; | 91 | return; |
| 40 | const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size}; | 92 | } |
| 41 | memory.ReadBlock(addr, &header, sizeof(MessageHeader)); | ||
| 42 | addr += sizeof(MessageHeader); | ||
| 43 | |||
| 44 | FieldMap fields; | ||
| 45 | while (addr < end_addr) { | ||
| 46 | const auto field = static_cast<Field>(memory.Read8(addr++)); | ||
| 47 | const auto length = memory.Read8(addr++); | ||
| 48 | 93 | ||
| 49 | if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) { | 94 | LogPacketHeader header{}; |
| 50 | ++addr; | 95 | std::memcpy(&header, data.data(), sizeof(LogPacketHeader)); |
| 51 | } | 96 | offset += sizeof(LogPacketHeader); |
| 52 | 97 | ||
| 53 | SCOPE_EXIT({ addr += length; }); | 98 | LogPacketHeaderEntry entry{ |
| 99 | .pid = header.pid, | ||
| 100 | .tid = header.tid, | ||
| 101 | .severity = header.severity, | ||
| 102 | .verbosity = header.verbosity, | ||
| 103 | }; | ||
| 54 | 104 | ||
| 55 | if (field == Field::Skip) { | 105 | if (True(header.flags & LogPacketFlags::Head)) { |
| 56 | continue; | 106 | std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader)); |
| 107 | std::memcpy(tmp.data(), data.data() + offset, tmp.size()); | ||
| 108 | entries[entry] = std::move(tmp); | ||
| 109 | } else { | ||
| 110 | // Append to existing entry | ||
| 111 | if (!entries.contains(entry)) { | ||
| 112 | LOG_ERROR(Service_LM, "Log entry does not exist!"); | ||
| 113 | return; | ||
| 57 | } | 114 | } |
| 115 | std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader)); | ||
| 58 | 116 | ||
| 59 | std::vector<u8> data(length); | 117 | auto& existing_entry = entries[entry]; |
| 60 | memory.ReadBlock(addr, data.data(), length); | 118 | const auto base = existing_entry.size(); |
| 61 | fields.emplace(field, std::move(data)); | 119 | existing_entry.resize(base + (data.size() - sizeof(LogPacketHeader))); |
| 120 | std::memcpy(existing_entry.data() + base, data.data() + offset, | ||
| 121 | (data.size() - sizeof(LogPacketHeader))); | ||
| 62 | } | 122 | } |
| 63 | 123 | ||
| 64 | manager.Log({header, std::move(fields)}); | 124 | if (True(header.flags & LogPacketFlags::Tail)) { |
| 125 | auto it = entries.find(entry); | ||
| 126 | if (it == entries.end()) { | ||
| 127 | LOG_ERROR(Service_LM, "Log entry does not exist!"); | ||
| 128 | return; | ||
| 129 | } | ||
| 130 | ParseLog(it->first, it->second); | ||
| 131 | entries.erase(it); | ||
| 132 | } | ||
| 65 | } | 133 | } |
| 66 | 134 | ||
| 67 | void SetDestination(Kernel::HLERequestContext& ctx) { | 135 | void SetDestination(Kernel::HLERequestContext& ctx) { |
| 68 | IPC::RequestParser rp{ctx}; | 136 | IPC::RequestParser rp{ctx}; |
| 69 | const auto destination = rp.PopEnum<DestinationFlag>(); | 137 | const auto log_destination = rp.PopEnum<LogDestination>(); |
| 70 | |||
| 71 | LOG_DEBUG(Service_LM, "called, destination={:08X}", destination); | ||
| 72 | 138 | ||
| 73 | manager.SetDestination(destination); | 139 | LOG_DEBUG(Service_LM, "called, destination={}", DestinationToString(log_destination)); |
| 140 | destination = log_destination; | ||
| 74 | 141 | ||
| 75 | IPC::ResponseBuilder rb{ctx, 2}; | 142 | IPC::ResponseBuilder rb{ctx, 2}; |
| 76 | rb.Push(RESULT_SUCCESS); | 143 | rb.Push(RESULT_SUCCESS); |
| 77 | } | 144 | } |
| 78 | 145 | ||
| 79 | Manager& manager; | 146 | u32 ReadLeb128(const std::vector<u8>& data, std::size_t& offset) { |
| 80 | Core::Memory::Memory& memory; | 147 | u32 result{}; |
| 148 | u32 shift{}; | ||
| 149 | do { | ||
| 150 | result |= (data[offset] & 0x7f) << shift; | ||
| 151 | shift += 7; | ||
| 152 | offset++; | ||
| 153 | if (offset >= data.size()) { | ||
| 154 | break; | ||
| 155 | } | ||
| 156 | } while ((data[offset] & 0x80) != 0); | ||
| 157 | return result; | ||
| 158 | } | ||
| 159 | |||
| 160 | std::optional<std::string> ReadString(const std::vector<u8>& data, std::size_t& offset, | ||
| 161 | std::size_t length) { | ||
| 162 | if (length == 0) { | ||
| 163 | return std::nullopt; | ||
| 164 | } | ||
| 165 | std::string output(length, '\0'); | ||
| 166 | std::memcpy(output.data(), data.data() + offset, length); | ||
| 167 | offset += length; | ||
| 168 | return output; | ||
| 169 | } | ||
| 170 | |||
| 171 | u32_le ReadAsU32(const std::vector<u8>& data, std::size_t& offset, std::size_t length) { | ||
| 172 | ASSERT(length == sizeof(u32)); | ||
| 173 | u32_le output{}; | ||
| 174 | std::memcpy(&output, data.data() + offset, sizeof(u32)); | ||
| 175 | offset += length; | ||
| 176 | return output; | ||
| 177 | } | ||
| 178 | |||
| 179 | u64_le ReadAsU64(const std::vector<u8>& data, std::size_t& offset, std::size_t length) { | ||
| 180 | ASSERT(length == sizeof(u64)); | ||
| 181 | u64_le output{}; | ||
| 182 | std::memcpy(&output, data.data() + offset, sizeof(u64)); | ||
| 183 | offset += length; | ||
| 184 | return output; | ||
| 185 | } | ||
| 186 | |||
| 187 | void ParseLog(const LogPacketHeaderEntry entry, const std::vector<u8>& log_data) { | ||
| 188 | // Possible entries | ||
| 189 | std::optional<std::string> text_log; | ||
| 190 | std::optional<u32> line_number; | ||
| 191 | std::optional<std::string> file_name; | ||
| 192 | std::optional<std::string> function_name; | ||
| 193 | std::optional<std::string> module_name; | ||
| 194 | std::optional<std::string> thread_name; | ||
| 195 | std::optional<u64> log_pack_drop_count; | ||
| 196 | std::optional<s64> user_system_clock; | ||
| 197 | std::optional<std::string> process_name; | ||
| 198 | |||
| 199 | std::size_t offset{}; | ||
| 200 | while (offset < log_data.size()) { | ||
| 201 | const auto key = static_cast<LogDataChunkKey>(ReadLeb128(log_data, offset)); | ||
| 202 | const auto chunk_size = ReadLeb128(log_data, offset); | ||
| 203 | |||
| 204 | switch (key) { | ||
| 205 | case LogDataChunkKey::LogSessionBegin: | ||
| 206 | case LogDataChunkKey::LogSessionEnd: | ||
| 207 | break; | ||
| 208 | case LogDataChunkKey::TextLog: | ||
| 209 | text_log = ReadString(log_data, offset, chunk_size); | ||
| 210 | break; | ||
| 211 | case LogDataChunkKey::LineNumber: | ||
| 212 | line_number = ReadAsU32(log_data, offset, chunk_size); | ||
| 213 | break; | ||
| 214 | case LogDataChunkKey::FileName: | ||
| 215 | file_name = ReadString(log_data, offset, chunk_size); | ||
| 216 | break; | ||
| 217 | case LogDataChunkKey::FunctionName: | ||
| 218 | function_name = ReadString(log_data, offset, chunk_size); | ||
| 219 | break; | ||
| 220 | case LogDataChunkKey::ModuleName: | ||
| 221 | module_name = ReadString(log_data, offset, chunk_size); | ||
| 222 | break; | ||
| 223 | case LogDataChunkKey::ThreadName: | ||
| 224 | thread_name = ReadString(log_data, offset, chunk_size); | ||
| 225 | break; | ||
| 226 | case LogDataChunkKey::LogPacketDropCount: | ||
| 227 | log_pack_drop_count = ReadAsU64(log_data, offset, chunk_size); | ||
| 228 | break; | ||
| 229 | case LogDataChunkKey::UserSystemClock: | ||
| 230 | user_system_clock = ReadAsU64(log_data, offset, chunk_size); | ||
| 231 | break; | ||
| 232 | case LogDataChunkKey::ProcessName: | ||
| 233 | process_name = ReadString(log_data, offset, chunk_size); | ||
| 234 | break; | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | std::string output_log{}; | ||
| 239 | if (process_name) { | ||
| 240 | output_log += fmt::format("Process: {}\n", *process_name); | ||
| 241 | } | ||
| 242 | if (module_name) { | ||
| 243 | output_log += fmt::format("Module: {}\n", *module_name); | ||
| 244 | } | ||
| 245 | if (file_name) { | ||
| 246 | output_log += fmt::format("File: {}\n", *file_name); | ||
| 247 | } | ||
| 248 | if (function_name) { | ||
| 249 | output_log += fmt::format("Function: {}\n", *function_name); | ||
| 250 | } | ||
| 251 | if (line_number && *line_number != 0) { | ||
| 252 | output_log += fmt::format("Line: {}\n", *line_number); | ||
| 253 | } | ||
| 254 | output_log += fmt::format("ProcessID: {}\n", entry.pid); | ||
| 255 | output_log += fmt::format("ThreadID: {}\n", entry.tid); | ||
| 256 | |||
| 257 | if (text_log) { | ||
| 258 | output_log += fmt::format("Log Text: {}\n", *text_log); | ||
| 259 | } | ||
| 260 | |||
| 261 | switch (entry.severity) { | ||
| 262 | case LogSeverity::Trace: | ||
| 263 | LOG_DEBUG(Service_LM, "LogManager DEBUG ({}):\n{}", DestinationToString(destination), | ||
| 264 | output_log); | ||
| 265 | break; | ||
| 266 | case LogSeverity::Info: | ||
| 267 | LOG_INFO(Service_LM, "LogManager INFO ({}):\n{}", DestinationToString(destination), | ||
| 268 | output_log); | ||
| 269 | break; | ||
| 270 | case LogSeverity::Warning: | ||
| 271 | LOG_WARNING(Service_LM, "LogManager WARNING ({}):\n{}", | ||
| 272 | DestinationToString(destination), output_log); | ||
| 273 | break; | ||
| 274 | case LogSeverity::Error: | ||
| 275 | LOG_ERROR(Service_LM, "LogManager ERROR ({}):\n{}", DestinationToString(destination), | ||
| 276 | output_log); | ||
| 277 | break; | ||
| 278 | case LogSeverity::Fatal: | ||
| 279 | LOG_CRITICAL(Service_LM, "LogManager FATAL ({}):\n{}", DestinationToString(destination), | ||
| 280 | output_log); | ||
| 281 | break; | ||
| 282 | default: | ||
| 283 | LOG_CRITICAL(Service_LM, "LogManager UNKNOWN ({}):\n{}", | ||
| 284 | DestinationToString(destination), output_log); | ||
| 285 | break; | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | std::string DestinationToString(LogDestination destination) { | ||
| 290 | if (True(destination & LogDestination::All)) { | ||
| 291 | return "TargetManager | Uart | UartSleep"; | ||
| 292 | } | ||
| 293 | std::string output{}; | ||
| 294 | if (True(destination & LogDestination::TargetManager)) { | ||
| 295 | output += "| TargetManager"; | ||
| 296 | } | ||
| 297 | if (True(destination & LogDestination::Uart)) { | ||
| 298 | output += "| Uart"; | ||
| 299 | } | ||
| 300 | if (True(destination & LogDestination::UartSleep)) { | ||
| 301 | output += "| UartSleep"; | ||
| 302 | } | ||
| 303 | if (output.length() > 0) { | ||
| 304 | return output.substr(2); | ||
| 305 | } | ||
| 306 | return "No Destination"; | ||
| 307 | } | ||
| 308 | |||
| 309 | enum class LogDataChunkKey : u32 { | ||
| 310 | LogSessionBegin = 0, | ||
| 311 | LogSessionEnd = 1, | ||
| 312 | TextLog = 2, | ||
| 313 | LineNumber = 3, | ||
| 314 | FileName = 4, | ||
| 315 | FunctionName = 5, | ||
| 316 | ModuleName = 6, | ||
| 317 | ThreadName = 7, | ||
| 318 | LogPacketDropCount = 8, | ||
| 319 | UserSystemClock = 9, | ||
| 320 | ProcessName = 10, | ||
| 321 | }; | ||
| 322 | |||
| 323 | struct LogPacketHeader { | ||
| 324 | u64_le pid{}; | ||
| 325 | u64_le tid{}; | ||
| 326 | LogPacketFlags flags{}; | ||
| 327 | INSERT_PADDING_BYTES(1); | ||
| 328 | LogSeverity severity{}; | ||
| 329 | u8 verbosity{}; | ||
| 330 | u32_le payload_size{}; | ||
| 331 | }; | ||
| 332 | static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size"); | ||
| 333 | |||
| 334 | std::unordered_map<LogPacketHeaderEntry, std::vector<u8>> entries{}; | ||
| 335 | LogDestination destination{LogDestination::All}; | ||
| 81 | }; | 336 | }; |
| 82 | 337 | ||
| 83 | class LM final : public ServiceFramework<LM> { | 338 | class LM final : public ServiceFramework<LM> { |
diff --git a/src/core/hle/service/lm/manager.cpp b/src/core/hle/service/lm/manager.cpp deleted file mode 100644 index 3ee2374e7..000000000 --- a/src/core/hle/service/lm/manager.cpp +++ /dev/null | |||
| @@ -1,134 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | #include "common/string_util.h" | ||
| 8 | #include "core/hle/service/lm/manager.h" | ||
| 9 | #include "core/reporter.h" | ||
| 10 | |||
| 11 | namespace Service::LM { | ||
| 12 | |||
| 13 | std::ostream& operator<<(std::ostream& os, DestinationFlag dest) { | ||
| 14 | std::vector<std::string> array; | ||
| 15 | const auto check_single_flag = [dest, &array](DestinationFlag check, std::string name) { | ||
| 16 | if ((static_cast<u32>(check) & static_cast<u32>(dest)) != 0) { | ||
| 17 | array.emplace_back(std::move(name)); | ||
| 18 | } | ||
| 19 | }; | ||
| 20 | |||
| 21 | check_single_flag(DestinationFlag::Default, "Default"); | ||
| 22 | check_single_flag(DestinationFlag::UART, "UART"); | ||
| 23 | check_single_flag(DestinationFlag::UARTSleeping, "UART (Sleeping)"); | ||
| 24 | |||
| 25 | os << "["; | ||
| 26 | for (const auto& entry : array) { | ||
| 27 | os << entry << ", "; | ||
| 28 | } | ||
| 29 | return os << "]"; | ||
| 30 | } | ||
| 31 | |||
| 32 | std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity) { | ||
| 33 | switch (severity) { | ||
| 34 | case MessageHeader::Severity::Trace: | ||
| 35 | return os << "Trace"; | ||
| 36 | case MessageHeader::Severity::Info: | ||
| 37 | return os << "Info"; | ||
| 38 | case MessageHeader::Severity::Warning: | ||
| 39 | return os << "Warning"; | ||
| 40 | case MessageHeader::Severity::Error: | ||
| 41 | return os << "Error"; | ||
| 42 | case MessageHeader::Severity::Critical: | ||
| 43 | return os << "Critical"; | ||
| 44 | default: | ||
| 45 | return os << fmt::format("{:08X}", static_cast<u32>(severity)); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | std::ostream& operator<<(std::ostream& os, Field field) { | ||
| 50 | switch (field) { | ||
| 51 | case Field::Skip: | ||
| 52 | return os << "Skip"; | ||
| 53 | case Field::Message: | ||
| 54 | return os << "Message"; | ||
| 55 | case Field::Line: | ||
| 56 | return os << "Line"; | ||
| 57 | case Field::Filename: | ||
| 58 | return os << "Filename"; | ||
| 59 | case Field::Function: | ||
| 60 | return os << "Function"; | ||
| 61 | case Field::Module: | ||
| 62 | return os << "Module"; | ||
| 63 | case Field::Thread: | ||
| 64 | return os << "Thread"; | ||
| 65 | default: | ||
| 66 | return os << fmt::format("{:08X}", static_cast<u32>(field)); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | std::string FormatField(Field type, const std::vector<u8>& data) { | ||
| 71 | switch (type) { | ||
| 72 | case Field::Skip: | ||
| 73 | return ""; | ||
| 74 | case Field::Line: | ||
| 75 | if (data.size() >= sizeof(u32)) { | ||
| 76 | u32 line; | ||
| 77 | std::memcpy(&line, data.data(), sizeof(u32)); | ||
| 78 | return fmt::format("{}", line); | ||
| 79 | } | ||
| 80 | return "[ERROR DECODING LINE NUMBER]"; | ||
| 81 | case Field::Message: | ||
| 82 | case Field::Filename: | ||
| 83 | case Field::Function: | ||
| 84 | case Field::Module: | ||
| 85 | case Field::Thread: | ||
| 86 | return Common::StringFromFixedZeroTerminatedBuffer( | ||
| 87 | reinterpret_cast<const char*>(data.data()), data.size()); | ||
| 88 | default: | ||
| 89 | UNIMPLEMENTED_MSG("Unimplemented field type={}", type); | ||
| 90 | return ""; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | Manager::Manager(Core::Reporter& reporter) : reporter(reporter) {} | ||
| 95 | |||
| 96 | Manager::~Manager() = default; | ||
| 97 | |||
| 98 | void Manager::SetEnabled(bool enabled) { | ||
| 99 | this->enabled = enabled; | ||
| 100 | } | ||
| 101 | |||
| 102 | void Manager::SetDestination(DestinationFlag destination) { | ||
| 103 | this->destination = destination; | ||
| 104 | } | ||
| 105 | |||
| 106 | void Manager::Log(LogMessage message) { | ||
| 107 | if (message.header.IsHeadLog()) { | ||
| 108 | InitializeLog(); | ||
| 109 | } | ||
| 110 | |||
| 111 | current_log.emplace_back(std::move(message)); | ||
| 112 | |||
| 113 | if (current_log.back().header.IsTailLog()) { | ||
| 114 | FinalizeLog(); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | void Manager::Flush() { | ||
| 119 | FinalizeLog(); | ||
| 120 | } | ||
| 121 | |||
| 122 | void Manager::InitializeLog() { | ||
| 123 | current_log.clear(); | ||
| 124 | |||
| 125 | LOG_INFO(Service_LM, "Initialized new log session"); | ||
| 126 | } | ||
| 127 | |||
| 128 | void Manager::FinalizeLog() { | ||
| 129 | reporter.SaveLogReport(static_cast<u32>(destination), std::move(current_log)); | ||
| 130 | |||
| 131 | LOG_INFO(Service_LM, "Finalized current log session"); | ||
| 132 | } | ||
| 133 | |||
| 134 | } // namespace Service::LM | ||
diff --git a/src/core/hle/service/lm/manager.h b/src/core/hle/service/lm/manager.h deleted file mode 100644 index 544e636ba..000000000 --- a/src/core/hle/service/lm/manager.h +++ /dev/null | |||
| @@ -1,106 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <map> | ||
| 8 | #include <ostream> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/bit_field.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/swap.h" | ||
| 13 | |||
| 14 | namespace Core { | ||
| 15 | class Reporter; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace Service::LM { | ||
| 19 | |||
| 20 | enum class DestinationFlag : u32 { | ||
| 21 | Default = 1, | ||
| 22 | UART = 2, | ||
| 23 | UARTSleeping = 4, | ||
| 24 | |||
| 25 | All = 0xFFFF, | ||
| 26 | }; | ||
| 27 | |||
| 28 | struct MessageHeader { | ||
| 29 | enum Flags : u32_le { | ||
| 30 | IsHead = 1, | ||
| 31 | IsTail = 2, | ||
| 32 | }; | ||
| 33 | enum Severity : u32_le { | ||
| 34 | Trace, | ||
| 35 | Info, | ||
| 36 | Warning, | ||
| 37 | Error, | ||
| 38 | Critical, | ||
| 39 | }; | ||
| 40 | |||
| 41 | u64_le pid; | ||
| 42 | u64_le thread_context; | ||
| 43 | union { | ||
| 44 | BitField<0, 16, Flags> flags; | ||
| 45 | BitField<16, 8, Severity> severity; | ||
| 46 | BitField<24, 8, u32> verbosity; | ||
| 47 | }; | ||
| 48 | u32_le payload_size; | ||
| 49 | |||
| 50 | bool IsHeadLog() const { | ||
| 51 | return flags & IsHead; | ||
| 52 | } | ||
| 53 | bool IsTailLog() const { | ||
| 54 | return flags & IsTail; | ||
| 55 | } | ||
| 56 | }; | ||
| 57 | static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size"); | ||
| 58 | |||
| 59 | enum class Field : u8 { | ||
| 60 | Skip = 1, | ||
| 61 | Message = 2, | ||
| 62 | Line = 3, | ||
| 63 | Filename = 4, | ||
| 64 | Function = 5, | ||
| 65 | Module = 6, | ||
| 66 | Thread = 7, | ||
| 67 | }; | ||
| 68 | |||
| 69 | std::ostream& operator<<(std::ostream& os, DestinationFlag dest); | ||
| 70 | std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity); | ||
| 71 | std::ostream& operator<<(std::ostream& os, Field field); | ||
| 72 | |||
| 73 | using FieldMap = std::map<Field, std::vector<u8>>; | ||
| 74 | |||
| 75 | struct LogMessage { | ||
| 76 | MessageHeader header; | ||
| 77 | FieldMap fields; | ||
| 78 | }; | ||
| 79 | |||
| 80 | std::string FormatField(Field type, const std::vector<u8>& data); | ||
| 81 | |||
| 82 | class Manager { | ||
| 83 | public: | ||
| 84 | explicit Manager(Core::Reporter& reporter); | ||
| 85 | ~Manager(); | ||
| 86 | |||
| 87 | void SetEnabled(bool enabled); | ||
| 88 | void SetDestination(DestinationFlag destination); | ||
| 89 | |||
| 90 | void Log(LogMessage message); | ||
| 91 | |||
| 92 | void Flush(); | ||
| 93 | |||
| 94 | private: | ||
| 95 | void InitializeLog(); | ||
| 96 | void FinalizeLog(); | ||
| 97 | |||
| 98 | bool enabled = true; | ||
| 99 | DestinationFlag destination = DestinationFlag::All; | ||
| 100 | |||
| 101 | std::vector<LogMessage> current_log; | ||
| 102 | |||
| 103 | Core::Reporter& reporter; | ||
| 104 | }; | ||
| 105 | |||
| 106 | } // namespace Service::LM | ||
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 0becdf642..f199c3362 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp | |||
| @@ -20,7 +20,6 @@ | |||
| 20 | #include "core/hle/kernel/memory/page_table.h" | 20 | #include "core/hle/kernel/memory/page_table.h" |
| 21 | #include "core/hle/kernel/process.h" | 21 | #include "core/hle/kernel/process.h" |
| 22 | #include "core/hle/result.h" | 22 | #include "core/hle/result.h" |
| 23 | #include "core/hle/service/lm/manager.h" | ||
| 24 | #include "core/memory.h" | 23 | #include "core/memory.h" |
| 25 | #include "core/reporter.h" | 24 | #include "core/reporter.h" |
| 26 | #include "core/settings.h" | 25 | #include "core/settings.h" |
| @@ -360,55 +359,6 @@ void Reporter::SaveErrorReport(u64 title_id, ResultCode result, | |||
| 360 | SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); | 359 | SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); |
| 361 | } | 360 | } |
| 362 | 361 | ||
| 363 | void Reporter::SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const { | ||
| 364 | if (!IsReportingEnabled()) { | ||
| 365 | return; | ||
| 366 | } | ||
| 367 | |||
| 368 | const auto timestamp = GetTimestamp(); | ||
| 369 | json out; | ||
| 370 | |||
| 371 | out["yuzu_version"] = GetYuzuVersionData(); | ||
| 372 | out["report_common"] = | ||
| 373 | GetReportCommonData(system.CurrentProcess()->GetTitleID(), RESULT_SUCCESS, timestamp); | ||
| 374 | |||
| 375 | out["log_destination"] = | ||
| 376 | fmt::format("{}", static_cast<Service::LM::DestinationFlag>(destination)); | ||
| 377 | |||
| 378 | auto json_messages = json::array(); | ||
| 379 | std::transform(messages.begin(), messages.end(), std::back_inserter(json_messages), | ||
| 380 | [](const Service::LM::LogMessage& message) { | ||
| 381 | json out; | ||
| 382 | out["is_head"] = fmt::format("{}", message.header.IsHeadLog()); | ||
| 383 | out["is_tail"] = fmt::format("{}", message.header.IsTailLog()); | ||
| 384 | out["pid"] = fmt::format("{:016X}", message.header.pid); | ||
| 385 | out["thread_context"] = | ||
| 386 | fmt::format("{:016X}", message.header.thread_context); | ||
| 387 | out["payload_size"] = fmt::format("{:016X}", message.header.payload_size); | ||
| 388 | out["flags"] = fmt::format("{:04X}", message.header.flags.Value()); | ||
| 389 | out["severity"] = fmt::format("{}", message.header.severity.Value()); | ||
| 390 | out["verbosity"] = fmt::format("{:02X}", message.header.verbosity); | ||
| 391 | |||
| 392 | auto fields = json::array(); | ||
| 393 | std::transform(message.fields.begin(), message.fields.end(), | ||
| 394 | std::back_inserter(fields), [](const auto& kv) { | ||
| 395 | json out; | ||
| 396 | out["type"] = fmt::format("{}", kv.first); | ||
| 397 | out["data"] = | ||
| 398 | Service::LM::FormatField(kv.first, kv.second); | ||
| 399 | return out; | ||
| 400 | }); | ||
| 401 | |||
| 402 | out["fields"] = std::move(fields); | ||
| 403 | return out; | ||
| 404 | }); | ||
| 405 | |||
| 406 | out["log_messages"] = std::move(json_messages); | ||
| 407 | |||
| 408 | SaveToFile(std::move(out), | ||
| 409 | GetPath("log_report", system.CurrentProcess()->GetTitleID(), timestamp)); | ||
| 410 | } | ||
| 411 | |||
| 412 | void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, | 362 | void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, |
| 413 | std::string log_message) const { | 363 | std::string log_message) const { |
| 414 | if (!IsReportingEnabled()) | 364 | if (!IsReportingEnabled()) |
diff --git a/src/core/reporter.h b/src/core/reporter.h index 86d760cf0..b2c2d9a2e 100644 --- a/src/core/reporter.h +++ b/src/core/reporter.h | |||
| @@ -72,9 +72,6 @@ public: | |||
| 72 | void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, | 72 | void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, |
| 73 | std::string log_message) const; | 73 | std::string log_message) const; |
| 74 | 74 | ||
| 75 | // Used by lm services | ||
| 76 | void SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const; | ||
| 77 | |||
| 78 | // Can be used anywhere to generate a backtrace and general info report at any point during | 75 | // Can be used anywhere to generate a backtrace and general info report at any point during |
| 79 | // execution. Not intended to be used for anything other than debugging or testing. | 76 | // execution. Not intended to be used for anything other than debugging or testing. |
| 80 | void SaveUserReport() const; | 77 | void SaveUserReport() const; |