diff options
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/core.cpp | 17 | ||||
| -rw-r--r-- | src/core/core.h | 8 | ||||
| -rw-r--r-- | src/core/hle/service/lm/lm.cpp | 187 | ||||
| -rw-r--r-- | src/core/hle/service/lm/lm.h | 6 | ||||
| -rw-r--r-- | src/core/hle/service/lm/manager.cpp | 133 | ||||
| -rw-r--r-- | src/core/hle/service/lm/manager.h | 106 | ||||
| -rw-r--r-- | src/core/hle/service/service.cpp | 2 | ||||
| -rw-r--r-- | src/core/reporter.cpp | 51 | ||||
| -rw-r--r-- | src/core/reporter.h | 14 |
10 files changed, 367 insertions, 159 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3416854db..3b1d72cf9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -335,6 +335,8 @@ add_library(core STATIC | |||
| 335 | hle/service/ldr/ldr.h | 335 | hle/service/ldr/ldr.h |
| 336 | hle/service/lm/lm.cpp | 336 | hle/service/lm/lm.cpp |
| 337 | hle/service/lm/lm.h | 337 | hle/service/lm/lm.h |
| 338 | hle/service/lm/manager.cpp | ||
| 339 | hle/service/lm/manager.h | ||
| 338 | hle/service/mig/mig.cpp | 340 | hle/service/mig/mig.cpp |
| 339 | hle/service/mig/mig.h | 341 | hle/service/mig/mig.h |
| 340 | hle/service/mii/mii.cpp | 342 | hle/service/mii/mii.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index a58ceb703..4d0ac72a5 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | #include "core/hle/service/apm/controller.h" | 35 | #include "core/hle/service/apm/controller.h" |
| 36 | #include "core/hle/service/filesystem/filesystem.h" | 36 | #include "core/hle/service/filesystem/filesystem.h" |
| 37 | #include "core/hle/service/glue/manager.h" | 37 | #include "core/hle/service/glue/manager.h" |
| 38 | #include "core/hle/service/lm/manager.h" | ||
| 38 | #include "core/hle/service/service.h" | 39 | #include "core/hle/service/service.h" |
| 39 | #include "core/hle/service/sm/sm.h" | 40 | #include "core/hle/service/sm/sm.h" |
| 40 | #include "core/loader/loader.h" | 41 | #include "core/loader/loader.h" |
| @@ -250,6 +251,8 @@ struct System::Impl { | |||
| 250 | telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS", | 251 | telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS", |
| 251 | perf_stats->GetMeanFrametime()); | 252 | perf_stats->GetMeanFrametime()); |
| 252 | 253 | ||
| 254 | lm_manager.Flush(); | ||
| 255 | |||
| 253 | is_powered_on = false; | 256 | is_powered_on = false; |
| 254 | exit_lock = false; | 257 | exit_lock = false; |
| 255 | 258 | ||
| @@ -338,6 +341,7 @@ struct System::Impl { | |||
| 338 | bool is_powered_on = false; | 341 | bool is_powered_on = false; |
| 339 | bool exit_lock = false; | 342 | bool exit_lock = false; |
| 340 | 343 | ||
| 344 | Reporter reporter; | ||
| 341 | std::unique_ptr<Memory::CheatEngine> cheat_engine; | 345 | std::unique_ptr<Memory::CheatEngine> cheat_engine; |
| 342 | std::unique_ptr<Tools::Freezer> memory_freezer; | 346 | std::unique_ptr<Tools::Freezer> memory_freezer; |
| 343 | std::array<u8, 0x20> build_id{}; | 347 | std::array<u8, 0x20> build_id{}; |
| @@ -348,8 +352,9 @@ struct System::Impl { | |||
| 348 | /// APM (Performance) services | 352 | /// APM (Performance) services |
| 349 | Service::APM::Controller apm_controller{core_timing}; | 353 | Service::APM::Controller apm_controller{core_timing}; |
| 350 | 354 | ||
| 351 | /// Glue services | 355 | /// Service State |
| 352 | Service::Glue::ARPManager arp_manager; | 356 | Service::Glue::ARPManager arp_manager; |
| 357 | Service::LM::Manager lm_manager{reporter}; | ||
| 353 | 358 | ||
| 354 | /// Service manager | 359 | /// Service manager |
| 355 | std::shared_ptr<Service::SM::ServiceManager> service_manager; | 360 | std::shared_ptr<Service::SM::ServiceManager> service_manager; |
| @@ -357,8 +362,6 @@ struct System::Impl { | |||
| 357 | /// Telemetry session for this emulation session | 362 | /// Telemetry session for this emulation session |
| 358 | std::unique_ptr<Core::TelemetrySession> telemetry_session; | 363 | std::unique_ptr<Core::TelemetrySession> telemetry_session; |
| 359 | 364 | ||
| 360 | Reporter reporter; | ||
| 361 | |||
| 362 | ResultStatus status = ResultStatus::Success; | 365 | ResultStatus status = ResultStatus::Success; |
| 363 | std::string status_details = ""; | 366 | std::string status_details = ""; |
| 364 | 367 | ||
| @@ -634,6 +637,14 @@ const Service::APM::Controller& System::GetAPMController() const { | |||
| 634 | return impl->apm_controller; | 637 | return impl->apm_controller; |
| 635 | } | 638 | } |
| 636 | 639 | ||
| 640 | Service::LM::Manager& System::GetLogManager() { | ||
| 641 | return impl->lm_manager; | ||
| 642 | } | ||
| 643 | |||
| 644 | const Service::LM::Manager& System::GetLogManager() const { | ||
| 645 | return impl->lm_manager; | ||
| 646 | } | ||
| 647 | |||
| 637 | void System::SetExitLock(bool locked) { | 648 | void System::SetExitLock(bool locked) { |
| 638 | impl->exit_lock = locked; | 649 | impl->exit_lock = locked; |
| 639 | } | 650 | } |
diff --git a/src/core/core.h b/src/core/core.h index d13b6aa5e..90e7ac607 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -57,6 +57,10 @@ namespace Glue { | |||
| 57 | class ARPManager; | 57 | class ARPManager; |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | namespace LM { | ||
| 61 | class Manager; | ||
| 62 | } // namespace LM | ||
| 63 | |||
| 60 | namespace SM { | 64 | namespace SM { |
| 61 | class ServiceManager; | 65 | class ServiceManager; |
| 62 | } // namespace SM | 66 | } // namespace SM |
| @@ -327,6 +331,10 @@ public: | |||
| 327 | 331 | ||
| 328 | const Service::APM::Controller& GetAPMController() const; | 332 | const Service::APM::Controller& GetAPMController() const; |
| 329 | 333 | ||
| 334 | Service::LM::Manager& GetLogManager(); | ||
| 335 | |||
| 336 | const Service::LM::Manager& GetLogManager() const; | ||
| 337 | |||
| 330 | void SetExitLock(bool locked); | 338 | void SetExitLock(bool locked); |
| 331 | 339 | ||
| 332 | bool GetExitLock() const; | 340 | bool GetExitLock() const; |
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 2a61593e2..435f2d286 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp | |||
| @@ -6,8 +6,10 @@ | |||
| 6 | #include <string> | 6 | #include <string> |
| 7 | 7 | ||
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "common/scope_exit.h" | ||
| 9 | #include "core/hle/ipc_helpers.h" | 10 | #include "core/hle/ipc_helpers.h" |
| 10 | #include "core/hle/service/lm/lm.h" | 11 | #include "core/hle/service/lm/lm.h" |
| 12 | #include "core/hle/service/lm/manager.h" | ||
| 11 | #include "core/hle/service/service.h" | 13 | #include "core/hle/service/service.h" |
| 12 | #include "core/memory.h" | 14 | #include "core/memory.h" |
| 13 | 15 | ||
| @@ -15,65 +17,16 @@ namespace Service::LM { | |||
| 15 | 17 | ||
| 16 | class ILogger final : public ServiceFramework<ILogger> { | 18 | class ILogger final : public ServiceFramework<ILogger> { |
| 17 | public: | 19 | public: |
| 18 | ILogger() : ServiceFramework("ILogger") { | 20 | ILogger(Manager& manager) : ServiceFramework("ILogger"), manager(manager) { |
| 19 | static const FunctionInfo functions[] = { | 21 | static const FunctionInfo functions[] = { |
| 20 | {0x00000000, &ILogger::Initialize, "Initialize"}, | 22 | {0, &ILogger::Log, "Log"}, |
| 21 | {0x00000001, &ILogger::SetDestination, "SetDestination"}, | 23 | {1, &ILogger::SetDestination, "SetDestination"}, |
| 22 | }; | 24 | }; |
| 23 | RegisterHandlers(functions); | 25 | RegisterHandlers(functions); |
| 24 | } | 26 | } |
| 25 | 27 | ||
| 26 | private: | 28 | private: |
| 27 | struct MessageHeader { | 29 | void Log(Kernel::HLERequestContext& ctx) { |
| 28 | enum Flags : u32_le { | ||
| 29 | IsHead = 1, | ||
| 30 | IsTail = 2, | ||
| 31 | }; | ||
| 32 | enum Severity : u32_le { | ||
| 33 | Trace, | ||
| 34 | Info, | ||
| 35 | Warning, | ||
| 36 | Error, | ||
| 37 | Critical, | ||
| 38 | }; | ||
| 39 | |||
| 40 | u64_le pid; | ||
| 41 | u64_le threadContext; | ||
| 42 | union { | ||
| 43 | BitField<0, 16, Flags> flags; | ||
| 44 | BitField<16, 8, Severity> severity; | ||
| 45 | BitField<24, 8, u32> verbosity; | ||
| 46 | }; | ||
| 47 | u32_le payload_size; | ||
| 48 | |||
| 49 | bool IsHeadLog() const { | ||
| 50 | return flags & Flags::IsHead; | ||
| 51 | } | ||
| 52 | bool IsTailLog() const { | ||
| 53 | return flags & Flags::IsTail; | ||
| 54 | } | ||
| 55 | }; | ||
| 56 | static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size"); | ||
| 57 | |||
| 58 | /// Log field type | ||
| 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 | /** | ||
| 70 | * ILogger::Initialize service function | ||
| 71 | * Inputs: | ||
| 72 | * 0: 0x00000000 | ||
| 73 | * Outputs: | ||
| 74 | * 0: ResultCode | ||
| 75 | */ | ||
| 76 | void Initialize(Kernel::HLERequestContext& ctx) { | ||
| 77 | // This function only succeeds - Get that out of the way | 30 | // This function only succeeds - Get that out of the way |
| 78 | IPC::ResponseBuilder rb{ctx, 2}; | 31 | IPC::ResponseBuilder rb{ctx, 2}; |
| 79 | rb.Push(RESULT_SUCCESS); | 32 | rb.Push(RESULT_SUCCESS); |
| @@ -85,140 +38,70 @@ private: | |||
| 85 | Memory::ReadBlock(addr, &header, sizeof(MessageHeader)); | 38 | Memory::ReadBlock(addr, &header, sizeof(MessageHeader)); |
| 86 | addr += sizeof(MessageHeader); | 39 | addr += sizeof(MessageHeader); |
| 87 | 40 | ||
| 88 | if (header.IsHeadLog()) { | 41 | FieldMap fields; |
| 89 | log_stream.str(""); | ||
| 90 | log_stream.clear(); | ||
| 91 | } | ||
| 92 | |||
| 93 | // Parse out log metadata | ||
| 94 | u32 line{}; | ||
| 95 | std::string module; | ||
| 96 | std::string message; | ||
| 97 | std::string filename; | ||
| 98 | std::string function; | ||
| 99 | std::string thread; | ||
| 100 | while (addr < end_addr) { | 42 | while (addr < end_addr) { |
| 101 | const Field field{static_cast<Field>(Memory::Read8(addr++))}; | 43 | const auto field = static_cast<Field>(Memory::Read8(addr++)); |
| 102 | const std::size_t length{Memory::Read8(addr++)}; | 44 | const auto length = Memory::Read8(addr++); |
| 103 | 45 | ||
| 104 | if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { | 46 | if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { |
| 105 | ++addr; | 47 | ++addr; |
| 106 | } | 48 | } |
| 107 | 49 | ||
| 108 | switch (field) { | 50 | SCOPE_EXIT({ addr += length; }); |
| 109 | case Field::Skip: | ||
| 110 | break; | ||
| 111 | case Field::Message: | ||
| 112 | message = Memory::ReadCString(addr, length); | ||
| 113 | break; | ||
| 114 | case Field::Line: | ||
| 115 | line = Memory::Read32(addr); | ||
| 116 | break; | ||
| 117 | case Field::Filename: | ||
| 118 | filename = Memory::ReadCString(addr, length); | ||
| 119 | break; | ||
| 120 | case Field::Function: | ||
| 121 | function = Memory::ReadCString(addr, length); | ||
| 122 | break; | ||
| 123 | case Field::Module: | ||
| 124 | module = Memory::ReadCString(addr, length); | ||
| 125 | break; | ||
| 126 | case Field::Thread: | ||
| 127 | thread = Memory::ReadCString(addr, length); | ||
| 128 | break; | ||
| 129 | } | ||
| 130 | 51 | ||
| 131 | addr += length; | 52 | if (field == Field::Skip) { |
| 132 | } | 53 | continue; |
| 54 | } | ||
| 133 | 55 | ||
| 134 | // Empty log - nothing to do here | 56 | std::vector<u8> data(length); |
| 135 | if (log_stream.str().empty() && message.empty()) { | 57 | Memory::ReadBlock(addr, data.data(), length); |
| 136 | return; | 58 | fields.emplace(field, std::move(data)); |
| 137 | } | 59 | } |
| 138 | 60 | ||
| 139 | // Format a nicely printable string out of the log metadata | 61 | manager.Log({header, std::move(fields)}); |
| 140 | if (!filename.empty()) { | ||
| 141 | log_stream << filename << ':'; | ||
| 142 | } | ||
| 143 | if (!module.empty()) { | ||
| 144 | log_stream << module << ':'; | ||
| 145 | } | ||
| 146 | if (!function.empty()) { | ||
| 147 | log_stream << function << ':'; | ||
| 148 | } | ||
| 149 | if (line) { | ||
| 150 | log_stream << std::to_string(line) << ':'; | ||
| 151 | } | ||
| 152 | if (!thread.empty()) { | ||
| 153 | log_stream << thread << ':'; | ||
| 154 | } | ||
| 155 | if (log_stream.str().length() > 0 && log_stream.str().back() == ':') { | ||
| 156 | log_stream << ' '; | ||
| 157 | } | ||
| 158 | log_stream << message; | ||
| 159 | |||
| 160 | if (header.IsTailLog()) { | ||
| 161 | switch (header.severity) { | ||
| 162 | case MessageHeader::Severity::Trace: | ||
| 163 | LOG_DEBUG(Debug_Emulated, "{}", log_stream.str()); | ||
| 164 | break; | ||
| 165 | case MessageHeader::Severity::Info: | ||
| 166 | LOG_INFO(Debug_Emulated, "{}", log_stream.str()); | ||
| 167 | break; | ||
| 168 | case MessageHeader::Severity::Warning: | ||
| 169 | LOG_WARNING(Debug_Emulated, "{}", log_stream.str()); | ||
| 170 | break; | ||
| 171 | case MessageHeader::Severity::Error: | ||
| 172 | LOG_ERROR(Debug_Emulated, "{}", log_stream.str()); | ||
| 173 | break; | ||
| 174 | case MessageHeader::Severity::Critical: | ||
| 175 | LOG_CRITICAL(Debug_Emulated, "{}", log_stream.str()); | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | } | 62 | } |
| 180 | 63 | ||
| 181 | // This service function is intended to be used as a way to | ||
| 182 | // redirect logging output to different destinations, however, | ||
| 183 | // given we always want to see the logging output, it's sufficient | ||
| 184 | // to do nothing and return success here. | ||
| 185 | void SetDestination(Kernel::HLERequestContext& ctx) { | 64 | void SetDestination(Kernel::HLERequestContext& ctx) { |
| 186 | LOG_DEBUG(Service_LM, "called"); | 65 | IPC::RequestParser rp{ctx}; |
| 66 | const auto destination = rp.PopEnum<DestinationFlag>(); | ||
| 67 | |||
| 68 | LOG_DEBUG(Service_LM, "called, destination={:08X}", static_cast<u32>(destination)); | ||
| 69 | |||
| 70 | manager.SetDestination(destination); | ||
| 187 | 71 | ||
| 188 | IPC::ResponseBuilder rb{ctx, 2}; | 72 | IPC::ResponseBuilder rb{ctx, 2}; |
| 189 | rb.Push(RESULT_SUCCESS); | 73 | rb.Push(RESULT_SUCCESS); |
| 190 | } | 74 | } |
| 191 | 75 | ||
| 192 | std::ostringstream log_stream; | 76 | Manager& manager; |
| 193 | }; | 77 | }; |
| 194 | 78 | ||
| 195 | class LM final : public ServiceFramework<LM> { | 79 | class LM final : public ServiceFramework<LM> { |
| 196 | public: | 80 | public: |
| 197 | explicit LM() : ServiceFramework{"lm"} { | 81 | explicit LM(Manager& manager) : ServiceFramework{"lm"}, manager(manager) { |
| 82 | // clang-format off | ||
| 198 | static const FunctionInfo functions[] = { | 83 | static const FunctionInfo functions[] = { |
| 199 | {0x00000000, &LM::OpenLogger, "OpenLogger"}, | 84 | {0, &LM::OpenLogger, "OpenLogger"}, |
| 200 | }; | 85 | }; |
| 86 | // clang-format on | ||
| 87 | |||
| 201 | RegisterHandlers(functions); | 88 | RegisterHandlers(functions); |
| 202 | } | 89 | } |
| 203 | 90 | ||
| 204 | /** | 91 | private: |
| 205 | * LM::OpenLogger service function | ||
| 206 | * Inputs: | ||
| 207 | * 0: 0x00000000 | ||
| 208 | * Outputs: | ||
| 209 | * 0: ResultCode | ||
| 210 | */ | ||
| 211 | void OpenLogger(Kernel::HLERequestContext& ctx) { | 92 | void OpenLogger(Kernel::HLERequestContext& ctx) { |
| 212 | LOG_DEBUG(Service_LM, "called"); | 93 | LOG_DEBUG(Service_LM, "called"); |
| 213 | 94 | ||
| 214 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 95 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 215 | rb.Push(RESULT_SUCCESS); | 96 | rb.Push(RESULT_SUCCESS); |
| 216 | rb.PushIpcInterface<ILogger>(); | 97 | rb.PushIpcInterface<ILogger>(manager); |
| 217 | } | 98 | } |
| 99 | |||
| 100 | Manager& manager; | ||
| 218 | }; | 101 | }; |
| 219 | 102 | ||
| 220 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 103 | void InstallInterfaces(Core::System& system) { |
| 221 | std::make_shared<LM>()->InstallAsService(service_manager); | 104 | std::make_shared<LM>(system.GetLogManager())->InstallAsService(system.ServiceManager()); |
| 222 | } | 105 | } |
| 223 | 106 | ||
| 224 | } // namespace Service::LM | 107 | } // namespace Service::LM |
diff --git a/src/core/hle/service/lm/lm.h b/src/core/hle/service/lm/lm.h index 7806ae27b..d40410b5c 100644 --- a/src/core/hle/service/lm/lm.h +++ b/src/core/hle/service/lm/lm.h | |||
| @@ -4,13 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | namespace Service::SM { | 7 | namespace Core { |
| 8 | class ServiceManager; | 8 | class System; |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | namespace Service::LM { | 11 | namespace Service::LM { |
| 12 | 12 | ||
| 13 | /// Registers all LM services with the specified service manager. | 13 | /// Registers all LM services with the specified service manager. |
| 14 | void InstallInterfaces(SM::ServiceManager& service_manager); | 14 | void InstallInterfaces(Core::System& system); |
| 15 | 15 | ||
| 16 | } // namespace Service::LM | 16 | } // namespace Service::LM |
diff --git a/src/core/hle/service/lm/manager.cpp b/src/core/hle/service/lm/manager.cpp new file mode 100644 index 000000000..b67081b86 --- /dev/null +++ b/src/core/hle/service/lm/manager.cpp | |||
| @@ -0,0 +1,133 @@ | |||
| 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(); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | Manager::Manager(Core::Reporter& reporter) : reporter(reporter) {} | ||
| 94 | |||
| 95 | Manager::~Manager() = default; | ||
| 96 | |||
| 97 | void Manager::SetEnabled(bool enabled) { | ||
| 98 | this->enabled = enabled; | ||
| 99 | } | ||
| 100 | |||
| 101 | void Manager::SetDestination(DestinationFlag destination) { | ||
| 102 | this->destination = destination; | ||
| 103 | } | ||
| 104 | |||
| 105 | void Manager::Log(LogMessage message) { | ||
| 106 | if (message.header.IsHeadLog()) { | ||
| 107 | InitializeLog(); | ||
| 108 | } | ||
| 109 | |||
| 110 | current_log.emplace_back(std::move(message)); | ||
| 111 | |||
| 112 | if (current_log.back().header.IsTailLog()) { | ||
| 113 | FinalizeLog(); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | void Manager::Flush() { | ||
| 118 | FinalizeLog(); | ||
| 119 | } | ||
| 120 | |||
| 121 | void Manager::InitializeLog() { | ||
| 122 | current_log.clear(); | ||
| 123 | |||
| 124 | LOG_INFO(Service_LM, "Initialized new log session"); | ||
| 125 | } | ||
| 126 | |||
| 127 | void Manager::FinalizeLog() { | ||
| 128 | reporter.SaveLogReport(static_cast<u32>(destination), std::move(current_log)); | ||
| 129 | |||
| 130 | LOG_INFO(Service_LM, "Finalized current log session"); | ||
| 131 | } | ||
| 132 | |||
| 133 | } // namespace Service::LM | ||
diff --git a/src/core/hle/service/lm/manager.h b/src/core/hle/service/lm/manager.h new file mode 100644 index 000000000..544e636ba --- /dev/null +++ b/src/core/hle/service/lm/manager.h | |||
| @@ -0,0 +1,106 @@ | |||
| 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/hle/service/service.cpp b/src/core/hle/service/service.cpp index f2c6fe9dc..7c5302017 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -226,7 +226,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { | |||
| 226 | LBL::InstallInterfaces(*sm); | 226 | LBL::InstallInterfaces(*sm); |
| 227 | LDN::InstallInterfaces(*sm); | 227 | LDN::InstallInterfaces(*sm); |
| 228 | LDR::InstallInterfaces(*sm, system); | 228 | LDR::InstallInterfaces(*sm, system); |
| 229 | LM::InstallInterfaces(*sm); | 229 | LM::InstallInterfaces(system); |
| 230 | Migration::InstallInterfaces(*sm); | 230 | Migration::InstallInterfaces(*sm); |
| 231 | Mii::InstallInterfaces(*sm); | 231 | Mii::InstallInterfaces(*sm); |
| 232 | MM::InstallInterfaces(*sm); | 232 | MM::InstallInterfaces(*sm); |
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 9c657929e..6f4af77fd 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | #include <fmt/chrono.h> | 8 | #include <fmt/chrono.h> |
| 9 | #include <fmt/format.h> | 9 | #include <fmt/format.h> |
| 10 | #include <fmt/ostream.h> | ||
| 10 | #include <json.hpp> | 11 | #include <json.hpp> |
| 11 | 12 | ||
| 12 | #include "common/file_util.h" | 13 | #include "common/file_util.h" |
| @@ -17,6 +18,7 @@ | |||
| 17 | #include "core/hle/kernel/hle_ipc.h" | 18 | #include "core/hle/kernel/hle_ipc.h" |
| 18 | #include "core/hle/kernel/process.h" | 19 | #include "core/hle/kernel/process.h" |
| 19 | #include "core/hle/result.h" | 20 | #include "core/hle/result.h" |
| 21 | #include "core/hle/service/lm/manager.h" | ||
| 20 | #include "core/reporter.h" | 22 | #include "core/reporter.h" |
| 21 | #include "core/settings.h" | 23 | #include "core/settings.h" |
| 22 | 24 | ||
| @@ -354,6 +356,55 @@ void Reporter::SaveErrorReport(u64 title_id, ResultCode result, | |||
| 354 | SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); | 356 | SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); |
| 355 | } | 357 | } |
| 356 | 358 | ||
| 359 | void Reporter::SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const { | ||
| 360 | if (!IsReportingEnabled()) { | ||
| 361 | return; | ||
| 362 | } | ||
| 363 | |||
| 364 | const auto timestamp = GetTimestamp(); | ||
| 365 | json out; | ||
| 366 | |||
| 367 | out["yuzu_version"] = GetYuzuVersionData(); | ||
| 368 | out["report_common"] = | ||
| 369 | GetReportCommonData(system.CurrentProcess()->GetTitleID(), RESULT_SUCCESS, timestamp); | ||
| 370 | |||
| 371 | out["log_destination"] = | ||
| 372 | fmt::format("{}", static_cast<Service::LM::DestinationFlag>(destination)); | ||
| 373 | |||
| 374 | auto json_messages = json::array(); | ||
| 375 | std::transform(messages.begin(), messages.end(), std::back_inserter(json_messages), | ||
| 376 | [](const Service::LM::LogMessage& message) { | ||
| 377 | json out; | ||
| 378 | out["is_head"] = fmt::format("{}", message.header.IsHeadLog()); | ||
| 379 | out["is_tail"] = fmt::format("{}", message.header.IsTailLog()); | ||
| 380 | out["pid"] = fmt::format("{:016X}", message.header.pid); | ||
| 381 | out["thread_context"] = | ||
| 382 | fmt::format("{:016X}", message.header.thread_context); | ||
| 383 | out["payload_size"] = fmt::format("{:016X}", message.header.payload_size); | ||
| 384 | out["flags"] = fmt::format("{:04X}", message.header.flags.Value()); | ||
| 385 | out["severity"] = fmt::format("{}", message.header.severity.Value()); | ||
| 386 | out["verbosity"] = fmt::format("{:02X}", message.header.verbosity); | ||
| 387 | |||
| 388 | auto fields = json::array(); | ||
| 389 | std::transform(message.fields.begin(), message.fields.end(), | ||
| 390 | std::back_inserter(fields), [](const auto& kv) { | ||
| 391 | json out; | ||
| 392 | out["type"] = fmt::format("{}", kv.first); | ||
| 393 | out["data"] = | ||
| 394 | Service::LM::FormatField(kv.first, kv.second); | ||
| 395 | return out; | ||
| 396 | }); | ||
| 397 | |||
| 398 | out["fields"] = std::move(fields); | ||
| 399 | return out; | ||
| 400 | }); | ||
| 401 | |||
| 402 | out["log_messages"] = std::move(json_messages); | ||
| 403 | |||
| 404 | SaveToFile(std::move(out), | ||
| 405 | GetPath("log_report", system.CurrentProcess()->GetTitleID(), timestamp)); | ||
| 406 | } | ||
| 407 | |||
| 357 | void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, | 408 | void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, |
| 358 | std::string log_message) const { | 409 | std::string log_message) const { |
| 359 | if (!IsReportingEnabled()) | 410 | if (!IsReportingEnabled()) |
diff --git a/src/core/reporter.h b/src/core/reporter.h index f08aa11fb..380941b1b 100644 --- a/src/core/reporter.h +++ b/src/core/reporter.h | |||
| @@ -20,6 +20,10 @@ namespace Service::FileSystem { | |||
| 20 | enum class LogMode : u32; | 20 | enum class LogMode : u32; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | namespace Service::LM { | ||
| 24 | struct LogMessage; | ||
| 25 | } // namespace Service::LM | ||
| 26 | |||
| 23 | namespace Core { | 27 | namespace Core { |
| 24 | 28 | ||
| 25 | class System; | 29 | class System; |
| @@ -29,18 +33,22 @@ public: | |||
| 29 | explicit Reporter(System& system); | 33 | explicit Reporter(System& system); |
| 30 | ~Reporter(); | 34 | ~Reporter(); |
| 31 | 35 | ||
| 36 | // Used by fatal services | ||
| 32 | void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp, | 37 | void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp, |
| 33 | u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far, | 38 | u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far, |
| 34 | const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace, | 39 | const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace, |
| 35 | u32 backtrace_size, const std::string& arch, u32 unk10) const; | 40 | u32 backtrace_size, const std::string& arch, u32 unk10) const; |
| 36 | 41 | ||
| 42 | // Used by syscall svcBreak | ||
| 37 | void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, | 43 | void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, |
| 38 | std::optional<std::vector<u8>> resolved_buffer = {}) const; | 44 | std::optional<std::vector<u8>> resolved_buffer = {}) const; |
| 39 | 45 | ||
| 46 | // Used by HLE service handler | ||
| 40 | void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, | 47 | void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, |
| 41 | const std::string& name, | 48 | const std::string& name, |
| 42 | const std::string& service_name) const; | 49 | const std::string& service_name) const; |
| 43 | 50 | ||
| 51 | // Used by stub applet implementation | ||
| 44 | void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version, | 52 | void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version, |
| 45 | u32 theme_color, bool startup_sound, u64 system_tick, | 53 | u32 theme_color, bool startup_sound, u64 system_tick, |
| 46 | std::vector<std::vector<u8>> normal_channel, | 54 | std::vector<std::vector<u8>> normal_channel, |
| @@ -55,6 +63,7 @@ public: | |||
| 55 | void SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data, | 63 | void SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data, |
| 56 | std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const; | 64 | std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const; |
| 57 | 65 | ||
| 66 | // Used by error applet | ||
| 58 | void SaveErrorReport(u64 title_id, ResultCode result, | 67 | void SaveErrorReport(u64 title_id, ResultCode result, |
| 59 | std::optional<std::string> custom_text_main = {}, | 68 | std::optional<std::string> custom_text_main = {}, |
| 60 | std::optional<std::string> custom_text_detail = {}) const; | 69 | std::optional<std::string> custom_text_detail = {}) const; |
| @@ -62,6 +71,11 @@ public: | |||
| 62 | void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, | 71 | void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, |
| 63 | std::string log_message) const; | 72 | std::string log_message) const; |
| 64 | 73 | ||
| 74 | // Used by lm services | ||
| 75 | void SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const; | ||
| 76 | |||
| 77 | // Can be used anywhere to generate a backtrace and general info report at any point during | ||
| 78 | // execution. Not intended to be used for anything other than debugging or testing. | ||
| 65 | void SaveUserReport() const; | 79 | void SaveUserReport() const; |
| 66 | 80 | ||
| 67 | private: | 81 | private: |