diff options
| author | 2019-06-21 14:05:18 -0400 | |
|---|---|---|
| committer | 2019-06-21 14:05:18 -0400 | |
| commit | 96412848a9db0643198ea882824688f23dc19606 (patch) | |
| tree | 2824eafaf4bc026cc3fc0ee498d1c5c623f3aa65 /src | |
| parent | Merge pull request #2291 from DarkLordZach/homebrew-testing (diff) | |
| parent | loader: Move NSO module tracking to AppLoader (diff) | |
| download | yuzu-96412848a9db0643198ea882824688f23dc19606.tar.gz yuzu-96412848a9db0643198ea882824688f23dc19606.tar.xz yuzu-96412848a9db0643198ea882824688f23dc19606.zip | |
Merge pull request #2482 from DarkLordZach/prepo
core: Add detailed local reporting feature for development
Diffstat (limited to 'src')
34 files changed, 825 insertions, 54 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4204ace2b..2fb65c131 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -459,6 +459,8 @@ add_library(core STATIC | |||
| 459 | memory_setup.h | 459 | memory_setup.h |
| 460 | perf_stats.cpp | 460 | perf_stats.cpp |
| 461 | perf_stats.h | 461 | perf_stats.h |
| 462 | reporter.cpp | ||
| 463 | reporter.h | ||
| 462 | settings.cpp | 464 | settings.cpp |
| 463 | settings.h | 465 | settings.h |
| 464 | telemetry_session.cpp | 466 | telemetry_session.cpp |
| @@ -468,7 +470,7 @@ add_library(core STATIC | |||
| 468 | create_target_directory_groups(core) | 470 | create_target_directory_groups(core) |
| 469 | 471 | ||
| 470 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) | 472 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) |
| 471 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt mbedtls opus unicorn open_source_archives) | 473 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives) |
| 472 | if (ENABLE_WEB_SERVICE) | 474 | if (ENABLE_WEB_SERVICE) |
| 473 | target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) | 475 | target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) |
| 474 | target_link_libraries(core PRIVATE web_service) | 476 | target_link_libraries(core PRIVATE web_service) |
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 2223cbeed..372612c9b 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp | |||
| @@ -2,26 +2,213 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <map> | ||
| 6 | #include <optional> | ||
| 7 | #include "common/bit_field.h" | ||
| 5 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 6 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 7 | #include "core/arm/arm_interface.h" | 10 | #include "core/arm/arm_interface.h" |
| 11 | #include "core/core.h" | ||
| 12 | #include "core/loader/loader.h" | ||
| 8 | #include "core/memory.h" | 13 | #include "core/memory.h" |
| 9 | 14 | ||
| 10 | namespace Core { | 15 | namespace Core { |
| 11 | void ARM_Interface::LogBacktrace() const { | ||
| 12 | VAddr fp = GetReg(29); | ||
| 13 | VAddr lr = GetReg(30); | ||
| 14 | const VAddr sp = GetReg(13); | ||
| 15 | const VAddr pc = GetPC(); | ||
| 16 | 16 | ||
| 17 | LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc); | 17 | namespace { |
| 18 | |||
| 19 | constexpr u64 ELF_DYNAMIC_TAG_NULL = 0; | ||
| 20 | constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5; | ||
| 21 | constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6; | ||
| 22 | constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11; | ||
| 23 | |||
| 24 | enum class ELFSymbolType : u8 { | ||
| 25 | None = 0, | ||
| 26 | Object = 1, | ||
| 27 | Function = 2, | ||
| 28 | Section = 3, | ||
| 29 | File = 4, | ||
| 30 | Common = 5, | ||
| 31 | TLS = 6, | ||
| 32 | }; | ||
| 33 | |||
| 34 | enum class ELFSymbolBinding : u8 { | ||
| 35 | Local = 0, | ||
| 36 | Global = 1, | ||
| 37 | Weak = 2, | ||
| 38 | }; | ||
| 39 | |||
| 40 | enum class ELFSymbolVisibility : u8 { | ||
| 41 | Default = 0, | ||
| 42 | Internal = 1, | ||
| 43 | Hidden = 2, | ||
| 44 | Protected = 3, | ||
| 45 | }; | ||
| 46 | |||
| 47 | struct ELFSymbol { | ||
| 48 | u32 name_index; | ||
| 49 | union { | ||
| 50 | u8 info; | ||
| 51 | |||
| 52 | BitField<0, 4, ELFSymbolType> type; | ||
| 53 | BitField<4, 4, ELFSymbolBinding> binding; | ||
| 54 | }; | ||
| 55 | ELFSymbolVisibility visibility; | ||
| 56 | u16 sh_index; | ||
| 57 | u64 value; | ||
| 58 | u64 size; | ||
| 59 | }; | ||
| 60 | static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size."); | ||
| 61 | |||
| 62 | using Symbols = std::vector<std::pair<ELFSymbol, std::string>>; | ||
| 63 | |||
| 64 | Symbols GetSymbols(VAddr text_offset) { | ||
| 65 | const auto mod_offset = text_offset + Memory::Read32(text_offset + 4); | ||
| 66 | |||
| 67 | if (mod_offset < text_offset || (mod_offset & 0b11) != 0 || | ||
| 68 | Memory::Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { | ||
| 69 | return {}; | ||
| 70 | } | ||
| 71 | |||
| 72 | const auto dynamic_offset = Memory::Read32(mod_offset + 0x4) + mod_offset; | ||
| 73 | |||
| 74 | VAddr string_table_offset{}; | ||
| 75 | VAddr symbol_table_offset{}; | ||
| 76 | u64 symbol_entry_size{}; | ||
| 77 | |||
| 78 | VAddr dynamic_index = dynamic_offset; | ||
| 79 | while (true) { | ||
| 80 | const auto tag = Memory::Read64(dynamic_index); | ||
| 81 | const auto value = Memory::Read64(dynamic_index + 0x8); | ||
| 82 | dynamic_index += 0x10; | ||
| 83 | |||
| 84 | if (tag == ELF_DYNAMIC_TAG_NULL) { | ||
| 85 | break; | ||
| 86 | } | ||
| 87 | |||
| 88 | if (tag == ELF_DYNAMIC_TAG_STRTAB) { | ||
| 89 | string_table_offset = value; | ||
| 90 | } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) { | ||
| 91 | symbol_table_offset = value; | ||
| 92 | } else if (tag == ELF_DYNAMIC_TAG_SYMENT) { | ||
| 93 | symbol_entry_size = value; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) { | ||
| 98 | return {}; | ||
| 99 | } | ||
| 100 | |||
| 101 | const auto string_table_address = text_offset + string_table_offset; | ||
| 102 | const auto symbol_table_address = text_offset + symbol_table_offset; | ||
| 103 | |||
| 104 | Symbols out; | ||
| 105 | |||
| 106 | VAddr symbol_index = symbol_table_address; | ||
| 107 | while (symbol_index < string_table_address) { | ||
| 108 | ELFSymbol symbol{}; | ||
| 109 | Memory::ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol)); | ||
| 110 | |||
| 111 | VAddr string_offset = string_table_address + symbol.name_index; | ||
| 112 | std::string name; | ||
| 113 | for (u8 c = Memory::Read8(string_offset); c != 0; c = Memory::Read8(++string_offset)) { | ||
| 114 | name += static_cast<char>(c); | ||
| 115 | } | ||
| 116 | |||
| 117 | symbol_index += symbol_entry_size; | ||
| 118 | out.push_back({symbol, name}); | ||
| 119 | } | ||
| 120 | |||
| 121 | return out; | ||
| 122 | } | ||
| 123 | |||
| 124 | std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) { | ||
| 125 | const auto iter = | ||
| 126 | std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) { | ||
| 127 | const auto& [symbol, name] = pair; | ||
| 128 | const auto end_address = symbol.value + symbol.size; | ||
| 129 | return func_address >= symbol.value && func_address < end_address; | ||
| 130 | }); | ||
| 131 | |||
| 132 | if (iter == symbols.end()) { | ||
| 133 | return std::nullopt; | ||
| 134 | } | ||
| 135 | |||
| 136 | return iter->second; | ||
| 137 | } | ||
| 138 | |||
| 139 | } // Anonymous namespace | ||
| 140 | |||
| 141 | constexpr u64 SEGMENT_BASE = 0x7100000000ull; | ||
| 142 | |||
| 143 | std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { | ||
| 144 | std::vector<BacktraceEntry> out; | ||
| 145 | |||
| 146 | auto fp = GetReg(29); | ||
| 147 | auto lr = GetReg(30); | ||
| 148 | |||
| 18 | while (true) { | 149 | while (true) { |
| 19 | LOG_ERROR(Core_ARM, "{:016X}", lr); | 150 | out.push_back({"", 0, lr, 0}); |
| 20 | if (!fp) { | 151 | if (!fp) { |
| 21 | break; | 152 | break; |
| 22 | } | 153 | } |
| 23 | lr = Memory::Read64(fp + 8) - 4; | 154 | lr = Memory::Read64(fp + 8) - 4; |
| 24 | fp = Memory::Read64(fp); | 155 | fp = Memory::Read64(fp); |
| 25 | } | 156 | } |
| 157 | |||
| 158 | std::map<VAddr, std::string> modules; | ||
| 159 | auto& loader{System::GetInstance().GetAppLoader()}; | ||
| 160 | if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { | ||
| 161 | return {}; | ||
| 162 | } | ||
| 163 | |||
| 164 | std::map<std::string, Symbols> symbols; | ||
| 165 | for (const auto& module : modules) { | ||
| 166 | symbols.insert_or_assign(module.second, GetSymbols(module.first)); | ||
| 167 | } | ||
| 168 | |||
| 169 | for (auto& entry : out) { | ||
| 170 | VAddr base = 0; | ||
| 171 | for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) { | ||
| 172 | const auto& module{*iter}; | ||
| 173 | if (entry.original_address >= module.first) { | ||
| 174 | entry.module = module.second; | ||
| 175 | base = module.first; | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | entry.offset = entry.original_address - base; | ||
| 181 | entry.address = SEGMENT_BASE + entry.offset; | ||
| 182 | |||
| 183 | if (entry.module.empty()) | ||
| 184 | entry.module = "unknown"; | ||
| 185 | |||
| 186 | const auto symbol_set = symbols.find(entry.module); | ||
| 187 | if (symbol_set != symbols.end()) { | ||
| 188 | const auto symbol = GetSymbolName(symbol_set->second, entry.offset); | ||
| 189 | if (symbol.has_value()) { | ||
| 190 | // TODO(DarkLordZach): Add demangling of symbol names. | ||
| 191 | entry.name = *symbol; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | return out; | ||
| 197 | } | ||
| 198 | |||
| 199 | void ARM_Interface::LogBacktrace() const { | ||
| 200 | const VAddr sp = GetReg(13); | ||
| 201 | const VAddr pc = GetPC(); | ||
| 202 | LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc); | ||
| 203 | LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address", | ||
| 204 | "Offset", "Symbol"); | ||
| 205 | LOG_ERROR(Core_ARM, ""); | ||
| 206 | |||
| 207 | const auto backtrace = GetBacktrace(); | ||
| 208 | for (const auto& entry : backtrace) { | ||
| 209 | LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address, | ||
| 210 | entry.original_address, entry.offset, entry.name); | ||
| 211 | } | ||
| 26 | } | 212 | } |
| 213 | |||
| 27 | } // namespace Core | 214 | } // namespace Core |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 978b1518f..c6691a8e1 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <vector> | ||
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | 10 | ||
| 10 | namespace Common { | 11 | namespace Common { |
| @@ -152,6 +153,16 @@ public: | |||
| 152 | /// Prepare core for thread reschedule (if needed to correctly handle state) | 153 | /// Prepare core for thread reschedule (if needed to correctly handle state) |
| 153 | virtual void PrepareReschedule() = 0; | 154 | virtual void PrepareReschedule() = 0; |
| 154 | 155 | ||
| 156 | struct BacktraceEntry { | ||
| 157 | std::string module; | ||
| 158 | u64 address; | ||
| 159 | u64 original_address; | ||
| 160 | u64 offset; | ||
| 161 | std::string name; | ||
| 162 | }; | ||
| 163 | |||
| 164 | std::vector<BacktraceEntry> GetBacktrace() const; | ||
| 165 | |||
| 155 | /// fp (= r29) points to the last frame record. | 166 | /// fp (= r29) points to the last frame record. |
| 156 | /// Note that this is the frame record for the *previous* frame, not the current one. | 167 | /// Note that this is the frame record for the *previous* frame, not the current one. |
| 157 | /// Note we need to subtract 4 from our last read to get the proper address | 168 | /// Note we need to subtract 4 from our last read to get the proper address |
diff --git a/src/core/core.cpp b/src/core/core.cpp index ff0721079..b72a1fd6a 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -29,6 +29,7 @@ | |||
| 29 | #include "core/hle/service/sm/sm.h" | 29 | #include "core/hle/service/sm/sm.h" |
| 30 | #include "core/loader/loader.h" | 30 | #include "core/loader/loader.h" |
| 31 | #include "core/perf_stats.h" | 31 | #include "core/perf_stats.h" |
| 32 | #include "core/reporter.h" | ||
| 32 | #include "core/settings.h" | 33 | #include "core/settings.h" |
| 33 | #include "core/telemetry_session.h" | 34 | #include "core/telemetry_session.h" |
| 34 | #include "file_sys/cheat_engine.h" | 35 | #include "file_sys/cheat_engine.h" |
| @@ -74,7 +75,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 74 | return vfs->OpenFile(path, FileSys::Mode::Read); | 75 | return vfs->OpenFile(path, FileSys::Mode::Read); |
| 75 | } | 76 | } |
| 76 | struct System::Impl { | 77 | struct System::Impl { |
| 77 | explicit Impl(System& system) : kernel{system}, cpu_core_manager{system} {} | 78 | explicit Impl(System& system) : kernel{system}, cpu_core_manager{system}, reporter{system} {} |
| 78 | 79 | ||
| 79 | Cpu& CurrentCpuCore() { | 80 | Cpu& CurrentCpuCore() { |
| 80 | return cpu_core_manager.GetCurrentCore(); | 81 | return cpu_core_manager.GetCurrentCore(); |
| @@ -253,6 +254,8 @@ struct System::Impl { | |||
| 253 | /// Telemetry session for this emulation session | 254 | /// Telemetry session for this emulation session |
| 254 | std::unique_ptr<Core::TelemetrySession> telemetry_session; | 255 | std::unique_ptr<Core::TelemetrySession> telemetry_session; |
| 255 | 256 | ||
| 257 | Reporter reporter; | ||
| 258 | |||
| 256 | ResultStatus status = ResultStatus::Success; | 259 | ResultStatus status = ResultStatus::Success; |
| 257 | std::string status_details = ""; | 260 | std::string status_details = ""; |
| 258 | 261 | ||
| @@ -492,6 +495,10 @@ void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) { | |||
| 492 | impl->content_provider->ClearSlot(slot); | 495 | impl->content_provider->ClearSlot(slot); |
| 493 | } | 496 | } |
| 494 | 497 | ||
| 498 | const Reporter& System::GetReporter() const { | ||
| 499 | return impl->reporter; | ||
| 500 | } | ||
| 501 | |||
| 495 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { | 502 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { |
| 496 | return impl->Init(*this, emu_window); | 503 | return impl->Init(*this, emu_window); |
| 497 | } | 504 | } |
diff --git a/src/core/core.h b/src/core/core.h index 20959de54..226ef4630 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <string> | 9 | #include <string> |
| 10 | 10 | ||
| 11 | #include <map> | ||
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 12 | #include "core/file_sys/vfs_types.h" | 13 | #include "core/file_sys/vfs_types.h" |
| 13 | #include "core/hle/kernel/object.h" | 14 | #include "core/hle/kernel/object.h" |
| @@ -68,6 +69,7 @@ class Cpu; | |||
| 68 | class ExclusiveMonitor; | 69 | class ExclusiveMonitor; |
| 69 | class FrameLimiter; | 70 | class FrameLimiter; |
| 70 | class PerfStats; | 71 | class PerfStats; |
| 72 | class Reporter; | ||
| 71 | class TelemetrySession; | 73 | class TelemetrySession; |
| 72 | 74 | ||
| 73 | struct PerfStatsResults; | 75 | struct PerfStatsResults; |
| @@ -284,6 +286,8 @@ public: | |||
| 284 | 286 | ||
| 285 | void ClearContentProvider(FileSys::ContentProviderUnionSlot slot); | 287 | void ClearContentProvider(FileSys::ContentProviderUnionSlot slot); |
| 286 | 288 | ||
| 289 | const Reporter& GetReporter() const; | ||
| 290 | |||
| 287 | private: | 291 | private: |
| 288 | System(); | 292 | System(); |
| 289 | 293 | ||
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index f9c606bc5..de6363ff2 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -38,6 +38,7 @@ | |||
| 38 | #include "core/hle/result.h" | 38 | #include "core/hle/result.h" |
| 39 | #include "core/hle/service/service.h" | 39 | #include "core/hle/service/service.h" |
| 40 | #include "core/memory.h" | 40 | #include "core/memory.h" |
| 41 | #include "core/reporter.h" | ||
| 41 | 42 | ||
| 42 | namespace Kernel { | 43 | namespace Kernel { |
| 43 | namespace { | 44 | namespace { |
| @@ -594,6 +595,7 @@ struct BreakReason { | |||
| 594 | static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | 595 | static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { |
| 595 | BreakReason break_reason{reason}; | 596 | BreakReason break_reason{reason}; |
| 596 | bool has_dumped_buffer{}; | 597 | bool has_dumped_buffer{}; |
| 598 | std::vector<u8> debug_buffer; | ||
| 597 | 599 | ||
| 598 | const auto handle_debug_buffer = [&](VAddr addr, u64 sz) { | 600 | const auto handle_debug_buffer = [&](VAddr addr, u64 sz) { |
| 599 | if (sz == 0 || addr == 0 || has_dumped_buffer) { | 601 | if (sz == 0 || addr == 0 || has_dumped_buffer) { |
| @@ -605,7 +607,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | |||
| 605 | LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr)); | 607 | LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr)); |
| 606 | } else { | 608 | } else { |
| 607 | // We don't know what's in here so we'll hexdump it | 609 | // We don't know what's in here so we'll hexdump it |
| 608 | std::vector<u8> debug_buffer(sz); | 610 | debug_buffer.resize(sz); |
| 609 | Memory::ReadBlock(addr, debug_buffer.data(), sz); | 611 | Memory::ReadBlock(addr, debug_buffer.data(), sz); |
| 610 | std::string hexdump; | 612 | std::string hexdump; |
| 611 | for (std::size_t i = 0; i < debug_buffer.size(); i++) { | 613 | for (std::size_t i = 0; i < debug_buffer.size(); i++) { |
| @@ -664,6 +666,10 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | |||
| 664 | break; | 666 | break; |
| 665 | } | 667 | } |
| 666 | 668 | ||
| 669 | system.GetReporter().SaveSvcBreakReport( | ||
| 670 | static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger, info1, | ||
| 671 | info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); | ||
| 672 | |||
| 667 | if (!break_reason.signal_debugger) { | 673 | if (!break_reason.signal_debugger) { |
| 668 | LOG_CRITICAL( | 674 | LOG_CRITICAL( |
| 669 | Debug_Emulated, | 675 | Debug_Emulated, |
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 14fa92318..e3e4ead03 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp | |||
| @@ -35,12 +35,28 @@ AppletDataBroker::AppletDataBroker() { | |||
| 35 | 35 | ||
| 36 | AppletDataBroker::~AppletDataBroker() = default; | 36 | AppletDataBroker::~AppletDataBroker() = default; |
| 37 | 37 | ||
| 38 | AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() const { | ||
| 39 | std::vector<std::vector<u8>> out_normal; | ||
| 40 | |||
| 41 | for (const auto& storage : in_channel) { | ||
| 42 | out_normal.push_back(storage->GetData()); | ||
| 43 | } | ||
| 44 | |||
| 45 | std::vector<std::vector<u8>> out_interactive; | ||
| 46 | |||
| 47 | for (const auto& storage : in_interactive_channel) { | ||
| 48 | out_interactive.push_back(storage->GetData()); | ||
| 49 | } | ||
| 50 | |||
| 51 | return {std::move(out_normal), std::move(out_interactive)}; | ||
| 52 | } | ||
| 53 | |||
| 38 | std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { | 54 | std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { |
| 39 | if (out_channel.empty()) | 55 | if (out_channel.empty()) |
| 40 | return nullptr; | 56 | return nullptr; |
| 41 | 57 | ||
| 42 | auto out = std::move(out_channel.front()); | 58 | auto out = std::move(out_channel.front()); |
| 43 | out_channel.pop(); | 59 | out_channel.pop_front(); |
| 44 | return out; | 60 | return out; |
| 45 | } | 61 | } |
| 46 | 62 | ||
| @@ -49,7 +65,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() { | |||
| 49 | return nullptr; | 65 | return nullptr; |
| 50 | 66 | ||
| 51 | auto out = std::move(in_channel.front()); | 67 | auto out = std::move(in_channel.front()); |
| 52 | in_channel.pop(); | 68 | in_channel.pop_front(); |
| 53 | return out; | 69 | return out; |
| 54 | } | 70 | } |
| 55 | 71 | ||
| @@ -58,7 +74,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() { | |||
| 58 | return nullptr; | 74 | return nullptr; |
| 59 | 75 | ||
| 60 | auto out = std::move(out_interactive_channel.front()); | 76 | auto out = std::move(out_interactive_channel.front()); |
| 61 | out_interactive_channel.pop(); | 77 | out_interactive_channel.pop_front(); |
| 62 | return out; | 78 | return out; |
| 63 | } | 79 | } |
| 64 | 80 | ||
| @@ -67,25 +83,25 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() { | |||
| 67 | return nullptr; | 83 | return nullptr; |
| 68 | 84 | ||
| 69 | auto out = std::move(in_interactive_channel.front()); | 85 | auto out = std::move(in_interactive_channel.front()); |
| 70 | in_interactive_channel.pop(); | 86 | in_interactive_channel.pop_front(); |
| 71 | return out; | 87 | return out; |
| 72 | } | 88 | } |
| 73 | 89 | ||
| 74 | void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { | 90 | void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { |
| 75 | in_channel.push(std::make_unique<IStorage>(storage)); | 91 | in_channel.push_back(std::make_unique<IStorage>(storage)); |
| 76 | } | 92 | } |
| 77 | 93 | ||
| 78 | void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { | 94 | void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { |
| 79 | out_channel.push(std::make_unique<IStorage>(storage)); | 95 | out_channel.push_back(std::make_unique<IStorage>(storage)); |
| 80 | pop_out_data_event.writable->Signal(); | 96 | pop_out_data_event.writable->Signal(); |
| 81 | } | 97 | } |
| 82 | 98 | ||
| 83 | void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { | 99 | void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { |
| 84 | in_interactive_channel.push(std::make_unique<IStorage>(storage)); | 100 | in_interactive_channel.push_back(std::make_unique<IStorage>(storage)); |
| 85 | } | 101 | } |
| 86 | 102 | ||
| 87 | void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { | 103 | void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { |
| 88 | out_interactive_channel.push(std::make_unique<IStorage>(storage)); | 104 | out_interactive_channel.push_back(std::make_unique<IStorage>(storage)); |
| 89 | pop_interactive_out_data_event.writable->Signal(); | 105 | pop_interactive_out_data_event.writable->Signal(); |
| 90 | } | 106 | } |
| 91 | 107 | ||
| @@ -204,7 +220,7 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const { | |||
| 204 | UNIMPLEMENTED_MSG( | 220 | UNIMPLEMENTED_MSG( |
| 205 | "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", | 221 | "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", |
| 206 | static_cast<u8>(id)); | 222 | static_cast<u8>(id)); |
| 207 | return std::make_shared<StubApplet>(); | 223 | return std::make_shared<StubApplet>(id); |
| 208 | } | 224 | } |
| 209 | } | 225 | } |
| 210 | 226 | ||
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index b46e10a4a..05ae739ca 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h | |||
| @@ -54,6 +54,14 @@ public: | |||
| 54 | AppletDataBroker(); | 54 | AppletDataBroker(); |
| 55 | ~AppletDataBroker(); | 55 | ~AppletDataBroker(); |
| 56 | 56 | ||
| 57 | struct RawChannelData { | ||
| 58 | std::vector<std::vector<u8>> normal; | ||
| 59 | std::vector<std::vector<u8>> interactive; | ||
| 60 | }; | ||
| 61 | |||
| 62 | // Retrieves but does not pop the data sent to applet. | ||
| 63 | RawChannelData PeekDataToAppletForDebug() const; | ||
| 64 | |||
| 57 | std::unique_ptr<IStorage> PopNormalDataToGame(); | 65 | std::unique_ptr<IStorage> PopNormalDataToGame(); |
| 58 | std::unique_ptr<IStorage> PopNormalDataToApplet(); | 66 | std::unique_ptr<IStorage> PopNormalDataToApplet(); |
| 59 | 67 | ||
| @@ -76,16 +84,16 @@ private: | |||
| 76 | // Queues are named from applet's perspective | 84 | // Queues are named from applet's perspective |
| 77 | 85 | ||
| 78 | // PopNormalDataToApplet and PushNormalDataFromGame | 86 | // PopNormalDataToApplet and PushNormalDataFromGame |
| 79 | std::queue<std::unique_ptr<IStorage>> in_channel; | 87 | std::deque<std::unique_ptr<IStorage>> in_channel; |
| 80 | 88 | ||
| 81 | // PopNormalDataToGame and PushNormalDataFromApplet | 89 | // PopNormalDataToGame and PushNormalDataFromApplet |
| 82 | std::queue<std::unique_ptr<IStorage>> out_channel; | 90 | std::deque<std::unique_ptr<IStorage>> out_channel; |
| 83 | 91 | ||
| 84 | // PopInteractiveDataToApplet and PushInteractiveDataFromGame | 92 | // PopInteractiveDataToApplet and PushInteractiveDataFromGame |
| 85 | std::queue<std::unique_ptr<IStorage>> in_interactive_channel; | 93 | std::deque<std::unique_ptr<IStorage>> in_interactive_channel; |
| 86 | 94 | ||
| 87 | // PopInteractiveDataToGame and PushInteractiveDataFromApplet | 95 | // PopInteractiveDataToGame and PushInteractiveDataFromApplet |
| 88 | std::queue<std::unique_ptr<IStorage>> out_interactive_channel; | 96 | std::deque<std::unique_ptr<IStorage>> out_interactive_channel; |
| 89 | 97 | ||
| 90 | Kernel::EventPair state_changed_event; | 98 | Kernel::EventPair state_changed_event; |
| 91 | 99 | ||
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp index 04774bedc..af3a900f8 100644 --- a/src/core/hle/service/am/applets/error.cpp +++ b/src/core/hle/service/am/applets/error.cpp | |||
| @@ -9,8 +9,10 @@ | |||
| 9 | #include "common/string_util.h" | 9 | #include "common/string_util.h" |
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/frontend/applets/error.h" | 11 | #include "core/frontend/applets/error.h" |
| 12 | #include "core/hle/kernel/process.h" | ||
| 12 | #include "core/hle/service/am/am.h" | 13 | #include "core/hle/service/am/am.h" |
| 13 | #include "core/hle/service/am/applets/error.h" | 14 | #include "core/hle/service/am/applets/error.h" |
| 15 | #include "core/reporter.h" | ||
| 14 | 16 | ||
| 15 | namespace Service::AM::Applets { | 17 | namespace Service::AM::Applets { |
| 16 | 18 | ||
| @@ -143,9 +145,12 @@ void Error::Execute() { | |||
| 143 | } | 145 | } |
| 144 | 146 | ||
| 145 | const auto callback = [this] { DisplayCompleted(); }; | 147 | const auto callback = [this] { DisplayCompleted(); }; |
| 148 | const auto title_id = Core::CurrentProcess()->GetTitleID(); | ||
| 149 | const auto& reporter{Core::System::GetInstance().GetReporter()}; | ||
| 146 | 150 | ||
| 147 | switch (mode) { | 151 | switch (mode) { |
| 148 | case ErrorAppletMode::ShowError: | 152 | case ErrorAppletMode::ShowError: |
| 153 | reporter.SaveErrorReport(title_id, error_code); | ||
| 149 | frontend.ShowError(error_code, callback); | 154 | frontend.ShowError(error_code, callback); |
| 150 | break; | 155 | break; |
| 151 | case ErrorAppletMode::ShowSystemError: | 156 | case ErrorAppletMode::ShowSystemError: |
| @@ -156,14 +161,18 @@ void Error::Execute() { | |||
| 156 | const auto& detail_text = | 161 | const auto& detail_text = |
| 157 | system ? args->system_error.detail_text : args->application_error.detail_text; | 162 | system ? args->system_error.detail_text : args->application_error.detail_text; |
| 158 | 163 | ||
| 159 | frontend.ShowCustomErrorText( | 164 | const auto main_text_string = |
| 160 | error_code, | 165 | Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()); |
| 161 | Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()), | 166 | const auto detail_text_string = |
| 162 | Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()), | 167 | Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()); |
| 163 | callback); | 168 | |
| 169 | reporter.SaveErrorReport(title_id, error_code, main_text_string, detail_text_string); | ||
| 170 | frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback); | ||
| 164 | break; | 171 | break; |
| 165 | } | 172 | } |
| 166 | case ErrorAppletMode::ShowErrorRecord: | 173 | case ErrorAppletMode::ShowErrorRecord: |
| 174 | reporter.SaveErrorReport(title_id, error_code, | ||
| 175 | fmt::format("{:016X}", args->error_record.posix_time)); | ||
| 167 | frontend.ShowErrorWithTimestamp( | 176 | frontend.ShowErrorWithTimestamp( |
| 168 | error_code, std::chrono::seconds{args->error_record.posix_time}, callback); | 177 | error_code, std::chrono::seconds{args->error_record.posix_time}, callback); |
| 169 | break; | 178 | break; |
diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp index 76fc8906d..54c155dd8 100644 --- a/src/core/hle/service/am/applets/general_backend.cpp +++ b/src/core/hle/service/am/applets/general_backend.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include "core/hle/result.h" | 13 | #include "core/hle/result.h" |
| 14 | #include "core/hle/service/am/am.h" | 14 | #include "core/hle/service/am/am.h" |
| 15 | #include "core/hle/service/am/applets/general_backend.h" | 15 | #include "core/hle/service/am/applets/general_backend.h" |
| 16 | #include "core/reporter.h" | ||
| 16 | 17 | ||
| 17 | namespace Service::AM::Applets { | 18 | namespace Service::AM::Applets { |
| 18 | 19 | ||
| @@ -83,13 +84,20 @@ void PhotoViewer::ViewFinished() { | |||
| 83 | broker.SignalStateChanged(); | 84 | broker.SignalStateChanged(); |
| 84 | } | 85 | } |
| 85 | 86 | ||
| 86 | StubApplet::StubApplet() = default; | 87 | StubApplet::StubApplet(AppletId id) : id(id) {} |
| 87 | 88 | ||
| 88 | StubApplet::~StubApplet() = default; | 89 | StubApplet::~StubApplet() = default; |
| 89 | 90 | ||
| 90 | void StubApplet::Initialize() { | 91 | void StubApplet::Initialize() { |
| 91 | LOG_WARNING(Service_AM, "called (STUBBED)"); | 92 | LOG_WARNING(Service_AM, "called (STUBBED)"); |
| 92 | Applet::Initialize(); | 93 | Applet::Initialize(); |
| 94 | |||
| 95 | const auto data = broker.PeekDataToAppletForDebug(); | ||
| 96 | Core::System::GetInstance().GetReporter().SaveUnimplementedAppletReport( | ||
| 97 | static_cast<u32>(id), common_args.arguments_version, common_args.library_version, | ||
| 98 | common_args.theme_color, common_args.play_startup_sound, common_args.system_tick, | ||
| 99 | data.normal, data.interactive); | ||
| 100 | |||
| 93 | LogCurrentStorage(broker, "Initialize"); | 101 | LogCurrentStorage(broker, "Initialize"); |
| 94 | } | 102 | } |
| 95 | 103 | ||
diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h index 2dd255d7c..fb68a2543 100644 --- a/src/core/hle/service/am/applets/general_backend.h +++ b/src/core/hle/service/am/applets/general_backend.h | |||
| @@ -34,7 +34,7 @@ private: | |||
| 34 | 34 | ||
| 35 | class StubApplet final : public Applet { | 35 | class StubApplet final : public Applet { |
| 36 | public: | 36 | public: |
| 37 | StubApplet(); | 37 | explicit StubApplet(AppletId id); |
| 38 | ~StubApplet() override; | 38 | ~StubApplet() override; |
| 39 | 39 | ||
| 40 | void Initialize() override; | 40 | void Initialize() override; |
| @@ -43,6 +43,9 @@ public: | |||
| 43 | ResultCode GetStatus() const override; | 43 | ResultCode GetStatus() const override; |
| 44 | void ExecuteInteractive() override; | 44 | void ExecuteInteractive() override; |
| 45 | void Execute() override; | 45 | void Execute() override; |
| 46 | |||
| 47 | private: | ||
| 48 | AppletId id; | ||
| 46 | }; | 49 | }; |
| 47 | 50 | ||
| 48 | } // namespace Service::AM::Applets | 51 | } // namespace Service::AM::Applets |
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index 2c229bcad..fe49c2161 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include "core/hle/service/fatal/fatal.h" | 16 | #include "core/hle/service/fatal/fatal.h" |
| 17 | #include "core/hle/service/fatal/fatal_p.h" | 17 | #include "core/hle/service/fatal/fatal_p.h" |
| 18 | #include "core/hle/service/fatal/fatal_u.h" | 18 | #include "core/hle/service/fatal/fatal_u.h" |
| 19 | #include "core/reporter.h" | ||
| 19 | 20 | ||
| 20 | namespace Service::Fatal { | 21 | namespace Service::Fatal { |
| 21 | 22 | ||
| @@ -100,27 +101,10 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { | |||
| 100 | 101 | ||
| 101 | LOG_ERROR(Service_Fatal, "{}", crash_report); | 102 | LOG_ERROR(Service_Fatal, "{}", crash_report); |
| 102 | 103 | ||
| 103 | const std::string crashreport_dir = | 104 | Core::System::GetInstance().GetReporter().SaveCrashReport( |
| 104 | FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs"; | 105 | title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc, |
| 105 | 106 | info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace, | |
| 106 | if (!FileUtil::CreateFullPath(crashreport_dir)) { | 107 | info.backtrace_size, info.ArchAsString(), info.unk10); |
| 107 | LOG_ERROR( | ||
| 108 | Service_Fatal, | ||
| 109 | "Unable to create crash report directory. Possible log directory permissions issue."); | ||
| 110 | return; | ||
| 111 | } | ||
| 112 | |||
| 113 | const std::time_t t = std::time(nullptr); | ||
| 114 | const std::string crashreport_filename = | ||
| 115 | fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t)); | ||
| 116 | |||
| 117 | auto file = FileUtil::IOFile(crashreport_filename, "wb"); | ||
| 118 | if (file.IsOpen()) { | ||
| 119 | file.WriteString(crash_report); | ||
| 120 | LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename); | ||
| 121 | } else { | ||
| 122 | LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename); | ||
| 123 | } | ||
| 124 | } | 108 | } |
| 125 | 109 | ||
| 126 | static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) { | 110 | static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) { |
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index e4fcee9f8..7e134f5c1 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp | |||
| @@ -2,10 +2,18 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <json.hpp> | ||
| 6 | #include "common/file_util.h" | ||
| 7 | #include "common/hex_util.h" | ||
| 5 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "common/scm_rev.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | 10 | #include "core/hle/ipc_helpers.h" |
| 11 | #include "core/hle/kernel/process.h" | ||
| 12 | #include "core/hle/service/acc/profile_manager.h" | ||
| 7 | #include "core/hle/service/prepo/prepo.h" | 13 | #include "core/hle/service/prepo/prepo.h" |
| 8 | #include "core/hle/service/service.h" | 14 | #include "core/hle/service/service.h" |
| 15 | #include "core/reporter.h" | ||
| 16 | #include "core/settings.h" | ||
| 9 | 17 | ||
| 10 | namespace Service::PlayReport { | 18 | namespace Service::PlayReport { |
| 11 | 19 | ||
| @@ -40,8 +48,21 @@ public: | |||
| 40 | 48 | ||
| 41 | private: | 49 | private: |
| 42 | void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) { | 50 | void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) { |
| 43 | // TODO(ogniK): Do we want to add play report? | 51 | IPC::RequestParser rp{ctx}; |
| 44 | LOG_WARNING(Service_PREPO, "(STUBBED) called"); | 52 | const auto user_id = rp.PopRaw<u128>(); |
| 53 | const auto process_id = rp.PopRaw<u64>(); | ||
| 54 | |||
| 55 | const auto data1 = ctx.ReadBuffer(0); | ||
| 56 | const auto data2 = ctx.ReadBuffer(1); | ||
| 57 | |||
| 58 | LOG_DEBUG( | ||
| 59 | Service_PREPO, | ||
| 60 | "called, user_id={:016X}{:016X}, unk1={:016X}, data1_size={:016X}, data2_size={:016X}", | ||
| 61 | user_id[1], user_id[0], process_id, data1.size(), data2.size()); | ||
| 62 | |||
| 63 | const auto& reporter{Core::System::GetInstance().GetReporter()}; | ||
| 64 | reporter.SavePlayReport(Core::CurrentProcess()->GetTitleID(), process_id, {data1, data2}, | ||
| 65 | user_id); | ||
| 45 | 66 | ||
| 46 | IPC::ResponseBuilder rb{ctx, 2}; | 67 | IPC::ResponseBuilder rb{ctx, 2}; |
| 47 | rb.Push(RESULT_SUCCESS); | 68 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 6c69f899e..b2954eb34 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -68,6 +68,7 @@ | |||
| 68 | #include "core/hle/service/usb/usb.h" | 68 | #include "core/hle/service/usb/usb.h" |
| 69 | #include "core/hle/service/vi/vi.h" | 69 | #include "core/hle/service/vi/vi.h" |
| 70 | #include "core/hle/service/wlan/wlan.h" | 70 | #include "core/hle/service/wlan/wlan.h" |
| 71 | #include "core/reporter.h" | ||
| 71 | 72 | ||
| 72 | namespace Service { | 73 | namespace Service { |
| 73 | 74 | ||
| @@ -148,6 +149,8 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext | |||
| 148 | } | 149 | } |
| 149 | buf.push_back('}'); | 150 | buf.push_back('}'); |
| 150 | 151 | ||
| 152 | Core::System::GetInstance().GetReporter().SaveUnimplementedFunctionReport( | ||
| 153 | ctx, ctx.GetCommand(), function_name, service_name); | ||
| 151 | UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf)); | 154 | UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf)); |
| 152 | } | 155 | } |
| 153 | 156 | ||
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 10b13fb1d..f9e88be2b 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -141,6 +141,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 141 | const FileSys::PatchManager pm(metadata.GetTitleID()); | 141 | const FileSys::PatchManager pm(metadata.GetTitleID()); |
| 142 | 142 | ||
| 143 | // Load NSO modules | 143 | // Load NSO modules |
| 144 | modules.clear(); | ||
| 144 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); | 145 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); |
| 145 | VAddr next_load_addr = base_address; | 146 | VAddr next_load_addr = base_address; |
| 146 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", | 147 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", |
| @@ -159,6 +160,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 159 | } | 160 | } |
| 160 | 161 | ||
| 161 | next_load_addr = *tentative_next_load_addr; | 162 | next_load_addr = *tentative_next_load_addr; |
| 163 | modules.insert_or_assign(load_addr, module); | ||
| 162 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); | 164 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); |
| 163 | // Register module with GDBStub | 165 | // Register module with GDBStub |
| 164 | GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); | 166 | GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); |
| @@ -212,4 +214,13 @@ bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() const { | |||
| 212 | return false; | 214 | return false; |
| 213 | } | 215 | } |
| 214 | 216 | ||
| 217 | ResultStatus AppLoader_DeconstructedRomDirectory::ReadNSOModules(Modules& modules) { | ||
| 218 | if (!is_loaded) { | ||
| 219 | return ResultStatus::ErrorNotInitialized; | ||
| 220 | } | ||
| 221 | |||
| 222 | modules = this->modules; | ||
| 223 | return ResultStatus::Success; | ||
| 224 | } | ||
| 225 | |||
| 215 | } // namespace Loader | 226 | } // namespace Loader |
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 1a65c16a4..1c0a354a4 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h | |||
| @@ -45,6 +45,8 @@ public: | |||
| 45 | ResultStatus ReadTitle(std::string& title) override; | 45 | ResultStatus ReadTitle(std::string& title) override; |
| 46 | bool IsRomFSUpdatable() const override; | 46 | bool IsRomFSUpdatable() const override; |
| 47 | 47 | ||
| 48 | ResultStatus ReadNSOModules(Modules& modules) override; | ||
| 49 | |||
| 48 | private: | 50 | private: |
| 49 | FileSys::ProgramMetadata metadata; | 51 | FileSys::ProgramMetadata metadata; |
| 50 | FileSys::VirtualFile romfs; | 52 | FileSys::VirtualFile romfs; |
| @@ -54,6 +56,8 @@ private: | |||
| 54 | std::string name; | 56 | std::string name; |
| 55 | u64 title_id{}; | 57 | u64 title_id{}; |
| 56 | bool override_update; | 58 | bool override_update; |
| 59 | |||
| 60 | Modules modules; | ||
| 57 | }; | 61 | }; |
| 58 | 62 | ||
| 59 | } // namespace Loader | 63 | } // namespace Loader |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 869406b75..8d3329202 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -267,6 +267,12 @@ public: | |||
| 267 | return ResultStatus::ErrorNotImplemented; | 267 | return ResultStatus::ErrorNotImplemented; |
| 268 | } | 268 | } |
| 269 | 269 | ||
| 270 | using Modules = std::map<VAddr, std::string>; | ||
| 271 | |||
| 272 | virtual ResultStatus ReadNSOModules(Modules& modules) { | ||
| 273 | return ResultStatus::ErrorNotImplemented; | ||
| 274 | } | ||
| 275 | |||
| 270 | protected: | 276 | protected: |
| 271 | FileSys::VirtualFile file; | 277 | FileSys::VirtualFile file; |
| 272 | bool is_loaded = false; | 278 | bool is_loaded = false; |
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp index 34efef09a..a152981a0 100644 --- a/src/core/loader/nax.cpp +++ b/src/core/loader/nax.cpp | |||
| @@ -94,4 +94,8 @@ ResultStatus AppLoader_NAX::ReadLogo(std::vector<u8>& buffer) { | |||
| 94 | return nca_loader->ReadLogo(buffer); | 94 | return nca_loader->ReadLogo(buffer); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | ResultStatus AppLoader_NAX::ReadNSOModules(Modules& modules) { | ||
| 98 | return nca_loader->ReadNSOModules(modules); | ||
| 99 | } | ||
| 100 | |||
| 97 | } // namespace Loader | 101 | } // namespace Loader |
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h index 00f1659c1..eaec9bf58 100644 --- a/src/core/loader/nax.h +++ b/src/core/loader/nax.h | |||
| @@ -42,6 +42,8 @@ public: | |||
| 42 | ResultStatus ReadBanner(std::vector<u8>& buffer) override; | 42 | ResultStatus ReadBanner(std::vector<u8>& buffer) override; |
| 43 | ResultStatus ReadLogo(std::vector<u8>& buffer) override; | 43 | ResultStatus ReadLogo(std::vector<u8>& buffer) override; |
| 44 | 44 | ||
| 45 | ResultStatus ReadNSOModules(Modules& modules) override; | ||
| 46 | |||
| 45 | private: | 47 | private: |
| 46 | std::unique_ptr<FileSys::NAX> nax; | 48 | std::unique_ptr<FileSys::NAX> nax; |
| 47 | std::unique_ptr<AppLoader_NCA> nca_loader; | 49 | std::unique_ptr<AppLoader_NCA> nca_loader; |
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index b3f8f1083..0f65fb637 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp | |||
| @@ -105,4 +105,13 @@ ResultStatus AppLoader_NCA::ReadLogo(std::vector<u8>& buffer) { | |||
| 105 | buffer = logo->GetFile("NintendoLogo.png")->ReadAllBytes(); | 105 | buffer = logo->GetFile("NintendoLogo.png")->ReadAllBytes(); |
| 106 | return ResultStatus::Success; | 106 | return ResultStatus::Success; |
| 107 | } | 107 | } |
| 108 | |||
| 109 | ResultStatus AppLoader_NCA::ReadNSOModules(Modules& modules) { | ||
| 110 | if (directory_loader == nullptr) { | ||
| 111 | return ResultStatus::ErrorNotInitialized; | ||
| 112 | } | ||
| 113 | |||
| 114 | return directory_loader->ReadNSOModules(modules); | ||
| 115 | } | ||
| 116 | |||
| 108 | } // namespace Loader | 117 | } // namespace Loader |
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index 94f0ed677..e47dc0e47 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h | |||
| @@ -42,6 +42,8 @@ public: | |||
| 42 | ResultStatus ReadBanner(std::vector<u8>& buffer) override; | 42 | ResultStatus ReadBanner(std::vector<u8>& buffer) override; |
| 43 | ResultStatus ReadLogo(std::vector<u8>& buffer) override; | 43 | ResultStatus ReadLogo(std::vector<u8>& buffer) override; |
| 44 | 44 | ||
| 45 | ResultStatus ReadNSOModules(Modules& modules) override; | ||
| 46 | |||
| 45 | private: | 47 | private: |
| 46 | std::unique_ptr<FileSys::NCA> nca; | 48 | std::unique_ptr<FileSys::NCA> nca; |
| 47 | std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader; | 49 | std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader; |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 80090b792..29311404a 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -172,11 +172,15 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) { | |||
| 172 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | 172 | return {ResultStatus::ErrorAlreadyLoaded, {}}; |
| 173 | } | 173 | } |
| 174 | 174 | ||
| 175 | modules.clear(); | ||
| 176 | |||
| 175 | // Load module | 177 | // Load module |
| 176 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); | 178 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); |
| 177 | if (!LoadModule(process, *file, base_address, true)) { | 179 | if (!LoadModule(process, *file, base_address, true)) { |
| 178 | return {ResultStatus::ErrorLoadingNSO, {}}; | 180 | return {ResultStatus::ErrorLoadingNSO, {}}; |
| 179 | } | 181 | } |
| 182 | |||
| 183 | modules.insert_or_assign(base_address, file->GetName()); | ||
| 180 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); | 184 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); |
| 181 | 185 | ||
| 182 | is_loaded = true; | 186 | is_loaded = true; |
| @@ -184,4 +188,9 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) { | |||
| 184 | LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}}; | 188 | LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}}; |
| 185 | } | 189 | } |
| 186 | 190 | ||
| 191 | ResultStatus AppLoader_NSO::ReadNSOModules(Modules& modules) { | ||
| 192 | modules = this->modules; | ||
| 193 | return ResultStatus::Success; | ||
| 194 | } | ||
| 195 | |||
| 187 | } // namespace Loader | 196 | } // namespace Loader |
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index fdce9191c..58cbe162d 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h | |||
| @@ -85,6 +85,11 @@ public: | |||
| 85 | std::optional<FileSys::PatchManager> pm = {}); | 85 | std::optional<FileSys::PatchManager> pm = {}); |
| 86 | 86 | ||
| 87 | LoadResult Load(Kernel::Process& process) override; | 87 | LoadResult Load(Kernel::Process& process) override; |
| 88 | |||
| 89 | ResultStatus ReadNSOModules(Modules& modules) override; | ||
| 90 | |||
| 91 | private: | ||
| 92 | Modules modules; | ||
| 88 | }; | 93 | }; |
| 89 | 94 | ||
| 90 | } // namespace Loader | 95 | } // namespace Loader |
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index ad56bbb38..3a22ec2c6 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp | |||
| @@ -183,4 +183,8 @@ ResultStatus AppLoader_NSP::ReadLogo(std::vector<u8>& buffer) { | |||
| 183 | return secondary_loader->ReadLogo(buffer); | 183 | return secondary_loader->ReadLogo(buffer); |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | ResultStatus AppLoader_NSP::ReadNSOModules(Modules& modules) { | ||
| 187 | return secondary_loader->ReadNSOModules(modules); | ||
| 188 | } | ||
| 189 | |||
| 186 | } // namespace Loader | 190 | } // namespace Loader |
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index 85e870bdf..868b028d3 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h | |||
| @@ -49,6 +49,8 @@ public: | |||
| 49 | ResultStatus ReadBanner(std::vector<u8>& buffer) override; | 49 | ResultStatus ReadBanner(std::vector<u8>& buffer) override; |
| 50 | ResultStatus ReadLogo(std::vector<u8>& buffer) override; | 50 | ResultStatus ReadLogo(std::vector<u8>& buffer) override; |
| 51 | 51 | ||
| 52 | ResultStatus ReadNSOModules(Modules& modules) override; | ||
| 53 | |||
| 52 | private: | 54 | private: |
| 53 | std::unique_ptr<FileSys::NSP> nsp; | 55 | std::unique_ptr<FileSys::NSP> nsp; |
| 54 | std::unique_ptr<AppLoader> secondary_loader; | 56 | std::unique_ptr<AppLoader> secondary_loader; |
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 1e285a053..a5c4d3688 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp | |||
| @@ -149,4 +149,8 @@ ResultStatus AppLoader_XCI::ReadLogo(std::vector<u8>& buffer) { | |||
| 149 | return nca_loader->ReadLogo(buffer); | 149 | return nca_loader->ReadLogo(buffer); |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | ResultStatus AppLoader_XCI::ReadNSOModules(Modules& modules) { | ||
| 153 | return nca_loader->ReadNSOModules(modules); | ||
| 154 | } | ||
| 155 | |||
| 152 | } // namespace Loader | 156 | } // namespace Loader |
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index ae7145b14..618ae2f47 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h | |||
| @@ -49,6 +49,8 @@ public: | |||
| 49 | ResultStatus ReadBanner(std::vector<u8>& buffer) override; | 49 | ResultStatus ReadBanner(std::vector<u8>& buffer) override; |
| 50 | ResultStatus ReadLogo(std::vector<u8>& buffer) override; | 50 | ResultStatus ReadLogo(std::vector<u8>& buffer) override; |
| 51 | 51 | ||
| 52 | ResultStatus ReadNSOModules(Modules& modules) override; | ||
| 53 | |||
| 52 | private: | 54 | private: |
| 53 | std::unique_ptr<FileSys::XCI> xci; | 55 | std::unique_ptr<FileSys::XCI> xci; |
| 54 | std::unique_ptr<AppLoader_NCA> nca_loader; | 56 | std::unique_ptr<AppLoader_NCA> nca_loader; |
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp new file mode 100644 index 000000000..8fe621aa0 --- /dev/null +++ b/src/core/reporter.cpp | |||
| @@ -0,0 +1,353 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <fstream> | ||
| 6 | #include <json.hpp> | ||
| 7 | #include "common/file_util.h" | ||
| 8 | #include "common/hex_util.h" | ||
| 9 | #include "common/scm_rev.h" | ||
| 10 | #include "core/arm/arm_interface.h" | ||
| 11 | #include "core/core.h" | ||
| 12 | #include "core/hle/kernel/hle_ipc.h" | ||
| 13 | #include "core/hle/kernel/process.h" | ||
| 14 | #include "core/hle/result.h" | ||
| 15 | #include "core/reporter.h" | ||
| 16 | #include "core/settings.h" | ||
| 17 | #include "fmt/time.h" | ||
| 18 | |||
| 19 | namespace { | ||
| 20 | |||
| 21 | std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) { | ||
| 22 | return fmt::format("{}{}/{:016X}_{}.json", FileUtil::GetUserPath(FileUtil::UserPath::LogDir), | ||
| 23 | type, title_id, timestamp); | ||
| 24 | } | ||
| 25 | |||
| 26 | std::string GetTimestamp() { | ||
| 27 | const auto time = std::time(nullptr); | ||
| 28 | return fmt::format("{:%FT%H-%M-%S}", *std::localtime(&time)); | ||
| 29 | } | ||
| 30 | |||
| 31 | using namespace nlohmann; | ||
| 32 | |||
| 33 | void SaveToFile(const json& json, const std::string& filename) { | ||
| 34 | if (!FileUtil::CreateFullPath(filename)) | ||
| 35 | LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename); | ||
| 36 | |||
| 37 | std::ofstream file( | ||
| 38 | FileUtil::SanitizePath(filename, FileUtil::DirectorySeparator::PlatformDefault)); | ||
| 39 | file << std::setw(4) << json << std::endl; | ||
| 40 | } | ||
| 41 | |||
| 42 | json GetYuzuVersionData() { | ||
| 43 | return { | ||
| 44 | {"scm_rev", std::string(Common::g_scm_rev)}, | ||
| 45 | {"scm_branch", std::string(Common::g_scm_branch)}, | ||
| 46 | {"scm_desc", std::string(Common::g_scm_desc)}, | ||
| 47 | {"build_name", std::string(Common::g_build_name)}, | ||
| 48 | {"build_date", std::string(Common::g_build_date)}, | ||
| 49 | {"build_fullname", std::string(Common::g_build_fullname)}, | ||
| 50 | {"build_version", std::string(Common::g_build_version)}, | ||
| 51 | {"shader_cache_version", std::string(Common::g_shader_cache_version)}, | ||
| 52 | }; | ||
| 53 | } | ||
| 54 | |||
| 55 | json GetReportCommonData(u64 title_id, ResultCode result, const std::string& timestamp, | ||
| 56 | std::optional<u128> user_id = {}) { | ||
| 57 | auto out = json{ | ||
| 58 | {"title_id", fmt::format("{:016X}", title_id)}, | ||
| 59 | {"result_raw", fmt::format("{:08X}", result.raw)}, | ||
| 60 | {"result_module", fmt::format("{:08X}", static_cast<u32>(result.module.Value()))}, | ||
| 61 | {"result_description", fmt::format("{:08X}", result.description.Value())}, | ||
| 62 | {"timestamp", timestamp}, | ||
| 63 | }; | ||
| 64 | if (user_id.has_value()) | ||
| 65 | out["user_id"] = fmt::format("{:016X}{:016X}", (*user_id)[1], (*user_id)[0]); | ||
| 66 | return out; | ||
| 67 | } | ||
| 68 | |||
| 69 | json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64 sp, u64 pc, | ||
| 70 | u64 pstate, std::array<u64, 31> registers, | ||
| 71 | std::optional<std::array<u64, 32>> backtrace = {}) { | ||
| 72 | auto out = json{ | ||
| 73 | {"entry_point", fmt::format("{:016X}", entry_point)}, | ||
| 74 | {"sp", fmt::format("{:016X}", sp)}, | ||
| 75 | {"pc", fmt::format("{:016X}", pc)}, | ||
| 76 | {"pstate", fmt::format("{:016X}", pstate)}, | ||
| 77 | {"architecture", architecture}, | ||
| 78 | }; | ||
| 79 | |||
| 80 | auto registers_out = json::object(); | ||
| 81 | for (std::size_t i = 0; i < registers.size(); ++i) { | ||
| 82 | registers_out[fmt::format("X{:02d}", i)] = fmt::format("{:016X}", registers[i]); | ||
| 83 | } | ||
| 84 | |||
| 85 | out["registers"] = std::move(registers_out); | ||
| 86 | |||
| 87 | if (backtrace.has_value()) { | ||
| 88 | auto backtrace_out = json::array(); | ||
| 89 | for (const auto& entry : *backtrace) { | ||
| 90 | backtrace_out.push_back(fmt::format("{:016X}", entry)); | ||
| 91 | } | ||
| 92 | out["backtrace"] = std::move(backtrace_out); | ||
| 93 | } | ||
| 94 | |||
| 95 | return out; | ||
| 96 | } | ||
| 97 | |||
| 98 | json GetProcessorStateDataAuto(Core::System& system) { | ||
| 99 | const auto* process{system.CurrentProcess()}; | ||
| 100 | const auto& vm_manager{process->VMManager()}; | ||
| 101 | auto& arm{system.CurrentArmInterface()}; | ||
| 102 | |||
| 103 | Core::ARM_Interface::ThreadContext context{}; | ||
| 104 | arm.SaveContext(context); | ||
| 105 | |||
| 106 | return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32", | ||
| 107 | vm_manager.GetCodeRegionBaseAddress(), context.sp, context.pc, | ||
| 108 | context.pstate, context.cpu_registers); | ||
| 109 | } | ||
| 110 | |||
| 111 | json GetBacktraceData(Core::System& system) { | ||
| 112 | auto out = json::array(); | ||
| 113 | const auto& backtrace{system.CurrentArmInterface().GetBacktrace()}; | ||
| 114 | for (const auto& entry : backtrace) { | ||
| 115 | out.push_back({ | ||
| 116 | {"module", entry.module}, | ||
| 117 | {"address", fmt::format("{:016X}", entry.address)}, | ||
| 118 | {"original_address", fmt::format("{:016X}", entry.original_address)}, | ||
| 119 | {"offset", fmt::format("{:016X}", entry.offset)}, | ||
| 120 | {"symbol_name", entry.name}, | ||
| 121 | }); | ||
| 122 | } | ||
| 123 | |||
| 124 | return out; | ||
| 125 | } | ||
| 126 | |||
| 127 | json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& system) { | ||
| 128 | json out; | ||
| 129 | |||
| 130 | out["yuzu_version"] = GetYuzuVersionData(); | ||
| 131 | out["report_common"] = GetReportCommonData(title_id, RESULT_SUCCESS, timestamp); | ||
| 132 | out["processor_state"] = GetProcessorStateDataAuto(system); | ||
| 133 | out["backtrace"] = GetBacktraceData(system); | ||
| 134 | |||
| 135 | return out; | ||
| 136 | } | ||
| 137 | |||
| 138 | template <bool read_value, typename DescriptorType> | ||
| 139 | json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) { | ||
| 140 | auto buffer_out = json::array(); | ||
| 141 | for (const auto& desc : buffer) { | ||
| 142 | auto entry = json{ | ||
| 143 | {"address", fmt::format("{:016X}", desc.Address())}, | ||
| 144 | {"size", fmt::format("{:016X}", desc.Size())}, | ||
| 145 | }; | ||
| 146 | |||
| 147 | if constexpr (read_value) { | ||
| 148 | std::vector<u8> data(desc.Size()); | ||
| 149 | Memory::ReadBlock(desc.Address(), data.data(), desc.Size()); | ||
| 150 | entry["data"] = Common::HexVectorToString(data); | ||
| 151 | } | ||
| 152 | |||
| 153 | buffer_out.push_back(std::move(entry)); | ||
| 154 | } | ||
| 155 | |||
| 156 | return buffer_out; | ||
| 157 | } | ||
| 158 | |||
| 159 | json GetHLERequestContextData(Kernel::HLERequestContext& ctx) { | ||
| 160 | json out; | ||
| 161 | |||
| 162 | auto cmd_buf = json::array(); | ||
| 163 | for (std::size_t i = 0; i < IPC::COMMAND_BUFFER_LENGTH; ++i) { | ||
| 164 | cmd_buf.push_back(fmt::format("{:08X}", ctx.CommandBuffer()[i])); | ||
| 165 | } | ||
| 166 | |||
| 167 | out["command_buffer"] = std::move(cmd_buf); | ||
| 168 | |||
| 169 | out["buffer_descriptor_a"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorA()); | ||
| 170 | out["buffer_descriptor_b"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorB()); | ||
| 171 | out["buffer_descriptor_c"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorC()); | ||
| 172 | out["buffer_descriptor_x"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorX()); | ||
| 173 | |||
| 174 | return std::move(out); | ||
| 175 | } | ||
| 176 | |||
| 177 | } // Anonymous namespace | ||
| 178 | |||
| 179 | namespace Core { | ||
| 180 | |||
| 181 | Reporter::Reporter(Core::System& system) : system(system) {} | ||
| 182 | |||
| 183 | Reporter::~Reporter() = default; | ||
| 184 | |||
| 185 | void Reporter::SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, | ||
| 186 | u64 sp, u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far, | ||
| 187 | const std::array<u64, 31>& registers, | ||
| 188 | const std::array<u64, 32>& backtrace, u32 backtrace_size, | ||
| 189 | const std::string& arch, u32 unk10) const { | ||
| 190 | if (!IsReportingEnabled()) | ||
| 191 | return; | ||
| 192 | |||
| 193 | const auto timestamp = GetTimestamp(); | ||
| 194 | json out; | ||
| 195 | |||
| 196 | out["yuzu_version"] = GetYuzuVersionData(); | ||
| 197 | out["report_common"] = GetReportCommonData(title_id, result, timestamp); | ||
| 198 | |||
| 199 | auto proc_out = GetProcessorStateData(arch, entry_point, sp, pc, pstate, registers, backtrace); | ||
| 200 | proc_out["set_flags"] = fmt::format("{:016X}", set_flags); | ||
| 201 | proc_out["afsr0"] = fmt::format("{:016X}", afsr0); | ||
| 202 | proc_out["afsr1"] = fmt::format("{:016X}", afsr1); | ||
| 203 | proc_out["esr"] = fmt::format("{:016X}", esr); | ||
| 204 | proc_out["far"] = fmt::format("{:016X}", far); | ||
| 205 | proc_out["backtrace_size"] = fmt::format("{:08X}", backtrace_size); | ||
| 206 | proc_out["unknown_10"] = fmt::format("{:08X}", unk10); | ||
| 207 | |||
| 208 | out["processor_state"] = std::move(proc_out); | ||
| 209 | |||
| 210 | SaveToFile(std::move(out), GetPath("crash_report", title_id, timestamp)); | ||
| 211 | } | ||
| 212 | |||
| 213 | void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, | ||
| 214 | std::optional<std::vector<u8>> resolved_buffer) const { | ||
| 215 | if (!IsReportingEnabled()) | ||
| 216 | return; | ||
| 217 | |||
| 218 | const auto timestamp = GetTimestamp(); | ||
| 219 | const auto title_id = system.CurrentProcess()->GetTitleID(); | ||
| 220 | auto out = GetFullDataAuto(timestamp, title_id, system); | ||
| 221 | |||
| 222 | auto break_out = json{ | ||
| 223 | {"type", fmt::format("{:08X}", type)}, | ||
| 224 | {"signal_debugger", fmt::format("{}", signal_debugger)}, | ||
| 225 | {"info1", fmt::format("{:016X}", info1)}, | ||
| 226 | {"info2", fmt::format("{:016X}", info2)}, | ||
| 227 | }; | ||
| 228 | |||
| 229 | if (resolved_buffer.has_value()) { | ||
| 230 | break_out["debug_buffer"] = Common::HexVectorToString(*resolved_buffer); | ||
| 231 | } | ||
| 232 | |||
| 233 | out["svc_break"] = std::move(break_out); | ||
| 234 | |||
| 235 | SaveToFile(std::move(out), GetPath("svc_break_report", title_id, timestamp)); | ||
| 236 | } | ||
| 237 | |||
| 238 | void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, | ||
| 239 | const std::string& name, | ||
| 240 | const std::string& service_name) const { | ||
| 241 | if (!IsReportingEnabled()) | ||
| 242 | return; | ||
| 243 | |||
| 244 | const auto timestamp = GetTimestamp(); | ||
| 245 | const auto title_id = system.CurrentProcess()->GetTitleID(); | ||
| 246 | auto out = GetFullDataAuto(timestamp, title_id, system); | ||
| 247 | |||
| 248 | auto function_out = GetHLERequestContextData(ctx); | ||
| 249 | function_out["command_id"] = command_id; | ||
| 250 | function_out["function_name"] = name; | ||
| 251 | function_out["service_name"] = service_name; | ||
| 252 | |||
| 253 | out["function"] = std::move(function_out); | ||
| 254 | |||
| 255 | SaveToFile(std::move(out), GetPath("unimpl_func_report", title_id, timestamp)); | ||
| 256 | } | ||
| 257 | |||
| 258 | void Reporter::SaveUnimplementedAppletReport( | ||
| 259 | u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color, | ||
| 260 | bool startup_sound, u64 system_tick, std::vector<std::vector<u8>> normal_channel, | ||
| 261 | std::vector<std::vector<u8>> interactive_channel) const { | ||
| 262 | if (!IsReportingEnabled()) | ||
| 263 | return; | ||
| 264 | |||
| 265 | const auto timestamp = GetTimestamp(); | ||
| 266 | const auto title_id = system.CurrentProcess()->GetTitleID(); | ||
| 267 | auto out = GetFullDataAuto(timestamp, title_id, system); | ||
| 268 | |||
| 269 | out["applet_common_args"] = { | ||
| 270 | {"applet_id", fmt::format("{:02X}", applet_id)}, | ||
| 271 | {"common_args_version", fmt::format("{:08X}", common_args_version)}, | ||
| 272 | {"library_version", fmt::format("{:08X}", library_version)}, | ||
| 273 | {"theme_color", fmt::format("{:08X}", theme_color)}, | ||
| 274 | {"startup_sound", fmt::format("{}", startup_sound)}, | ||
| 275 | {"system_tick", fmt::format("{:016X}", system_tick)}, | ||
| 276 | }; | ||
| 277 | |||
| 278 | auto normal_out = json::array(); | ||
| 279 | for (const auto& data : normal_channel) { | ||
| 280 | normal_out.push_back(Common::HexVectorToString(data)); | ||
| 281 | } | ||
| 282 | |||
| 283 | auto interactive_out = json::array(); | ||
| 284 | for (const auto& data : interactive_channel) { | ||
| 285 | interactive_out.push_back(Common::HexVectorToString(data)); | ||
| 286 | } | ||
| 287 | |||
| 288 | out["applet_normal_data"] = std::move(normal_out); | ||
| 289 | out["applet_interactive_data"] = std::move(interactive_out); | ||
| 290 | |||
| 291 | SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp)); | ||
| 292 | } | ||
| 293 | |||
| 294 | void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data, | ||
| 295 | std::optional<u128> user_id) const { | ||
| 296 | if (!IsReportingEnabled()) | ||
| 297 | return; | ||
| 298 | |||
| 299 | const auto timestamp = GetTimestamp(); | ||
| 300 | json out; | ||
| 301 | |||
| 302 | out["yuzu_version"] = GetYuzuVersionData(); | ||
| 303 | out["report_common"] = GetReportCommonData(title_id, RESULT_SUCCESS, timestamp, user_id); | ||
| 304 | |||
| 305 | auto data_out = json::array(); | ||
| 306 | for (const auto& d : data) { | ||
| 307 | data_out.push_back(Common::HexVectorToString(d)); | ||
| 308 | } | ||
| 309 | |||
| 310 | out["play_report_process_id"] = fmt::format("{:016X}", process_id); | ||
| 311 | out["play_report_data"] = std::move(data_out); | ||
| 312 | |||
| 313 | SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp)); | ||
| 314 | } | ||
| 315 | |||
| 316 | void Reporter::SaveErrorReport(u64 title_id, ResultCode result, | ||
| 317 | std::optional<std::string> custom_text_main, | ||
| 318 | std::optional<std::string> custom_text_detail) const { | ||
| 319 | if (!IsReportingEnabled()) | ||
| 320 | return; | ||
| 321 | |||
| 322 | const auto timestamp = GetTimestamp(); | ||
| 323 | json out; | ||
| 324 | |||
| 325 | out["yuzu_version"] = GetYuzuVersionData(); | ||
| 326 | out["report_common"] = GetReportCommonData(title_id, result, timestamp); | ||
| 327 | out["processor_state"] = GetProcessorStateDataAuto(system); | ||
| 328 | out["backtrace"] = GetBacktraceData(system); | ||
| 329 | |||
| 330 | out["error_custom_text"] = { | ||
| 331 | {"main", *custom_text_main}, | ||
| 332 | {"detail", *custom_text_detail}, | ||
| 333 | }; | ||
| 334 | |||
| 335 | SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); | ||
| 336 | } | ||
| 337 | |||
| 338 | void Reporter::SaveUserReport() const { | ||
| 339 | if (!IsReportingEnabled()) | ||
| 340 | return; | ||
| 341 | |||
| 342 | const auto timestamp = GetTimestamp(); | ||
| 343 | const auto title_id = system.CurrentProcess()->GetTitleID(); | ||
| 344 | |||
| 345 | SaveToFile(GetFullDataAuto(timestamp, title_id, system), | ||
| 346 | GetPath("user_report", title_id, timestamp)); | ||
| 347 | } | ||
| 348 | |||
| 349 | bool Reporter::IsReportingEnabled() const { | ||
| 350 | return Settings::values.reporting_services; | ||
| 351 | } | ||
| 352 | |||
| 353 | } // namespace Core | ||
diff --git a/src/core/reporter.h b/src/core/reporter.h new file mode 100644 index 000000000..3de19c0f7 --- /dev/null +++ b/src/core/reporter.h | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <optional> | ||
| 8 | #include <vector> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | union ResultCode; | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | class HLERequestContext; | ||
| 15 | } // namespace Kernel | ||
| 16 | |||
| 17 | namespace Core { | ||
| 18 | |||
| 19 | class Reporter { | ||
| 20 | public: | ||
| 21 | explicit Reporter(Core::System& system); | ||
| 22 | ~Reporter(); | ||
| 23 | |||
| 24 | void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp, | ||
| 25 | u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far, | ||
| 26 | const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace, | ||
| 27 | u32 backtrace_size, const std::string& arch, u32 unk10) const; | ||
| 28 | |||
| 29 | void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, | ||
| 30 | std::optional<std::vector<u8>> resolved_buffer = {}) const; | ||
| 31 | |||
| 32 | void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, | ||
| 33 | const std::string& name, | ||
| 34 | const std::string& service_name) const; | ||
| 35 | |||
| 36 | void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version, | ||
| 37 | u32 theme_color, bool startup_sound, u64 system_tick, | ||
| 38 | std::vector<std::vector<u8>> normal_channel, | ||
| 39 | std::vector<std::vector<u8>> interactive_channel) const; | ||
| 40 | |||
| 41 | void SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data, | ||
| 42 | std::optional<u128> user_id = {}) const; | ||
| 43 | |||
| 44 | void SaveErrorReport(u64 title_id, ResultCode result, | ||
| 45 | std::optional<std::string> custom_text_main = {}, | ||
| 46 | std::optional<std::string> custom_text_detail = {}) const; | ||
| 47 | |||
| 48 | void SaveUserReport() const; | ||
| 49 | |||
| 50 | private: | ||
| 51 | bool IsReportingEnabled() const; | ||
| 52 | |||
| 53 | Core::System& system; | ||
| 54 | }; | ||
| 55 | |||
| 56 | } // namespace Core | ||
diff --git a/src/core/settings.h b/src/core/settings.h index b84390745..e2ffcaaf7 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -415,6 +415,7 @@ struct Values { | |||
| 415 | std::string program_args; | 415 | std::string program_args; |
| 416 | bool dump_exefs; | 416 | bool dump_exefs; |
| 417 | bool dump_nso; | 417 | bool dump_nso; |
| 418 | bool reporting_services; | ||
| 418 | 419 | ||
| 419 | // WebService | 420 | // WebService |
| 420 | bool enable_telemetry; | 421 | bool enable_telemetry; |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 10e5c5c38..5a456e603 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -473,6 +473,8 @@ void Config::ReadDebuggingValues() { | |||
| 473 | ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString(); | 473 | ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString(); |
| 474 | Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); | 474 | Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); |
| 475 | Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); | 475 | Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); |
| 476 | Settings::values.reporting_services = | ||
| 477 | ReadSetting(QStringLiteral("reporting_services"), false).toBool(); | ||
| 476 | 478 | ||
| 477 | qt_config->endGroup(); | 479 | qt_config->endGroup(); |
| 478 | } | 480 | } |
| @@ -691,7 +693,7 @@ void Config::ReadValues() { | |||
| 691 | ReadDataStorageValues(); | 693 | ReadDataStorageValues(); |
| 692 | ReadSystemValues(); | 694 | ReadSystemValues(); |
| 693 | ReadMiscellaneousValues(); | 695 | ReadMiscellaneousValues(); |
| 694 | ReadDebugValues(); | 696 | ReadDebuggingValues(); |
| 695 | ReadWebServiceValues(); | 697 | ReadWebServiceValues(); |
| 696 | ReadDisabledAddOnValues(); | 698 | ReadDisabledAddOnValues(); |
| 697 | ReadUIValues(); | 699 | ReadUIValues(); |
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index efc2bedfd..63426fe4f 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp | |||
| @@ -36,6 +36,7 @@ void ConfigureDebug::SetConfiguration() { | |||
| 36 | ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); | 36 | ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); |
| 37 | ui->dump_exefs->setChecked(Settings::values.dump_exefs); | 37 | ui->dump_exefs->setChecked(Settings::values.dump_exefs); |
| 38 | ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso); | 38 | ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso); |
| 39 | ui->reporting_services->setChecked(Settings::values.reporting_services); | ||
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | void ConfigureDebug::ApplyConfiguration() { | 42 | void ConfigureDebug::ApplyConfiguration() { |
| @@ -46,6 +47,7 @@ void ConfigureDebug::ApplyConfiguration() { | |||
| 46 | Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); | 47 | Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); |
| 47 | Settings::values.dump_exefs = ui->dump_exefs->isChecked(); | 48 | Settings::values.dump_exefs = ui->dump_exefs->isChecked(); |
| 48 | Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked(); | 49 | Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked(); |
| 50 | Settings::values.reporting_services = ui->reporting_services->isChecked(); | ||
| 49 | Debugger::ToggleConsole(); | 51 | Debugger::ToggleConsole(); |
| 50 | Log::Filter filter; | 52 | Log::Filter filter; |
| 51 | filter.ParseFilterString(Settings::values.log_filter); | 53 | filter.ParseFilterString(Settings::values.log_filter); |
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 5ca9ce0e6..4a7e3dc3d 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui | |||
| @@ -155,6 +155,28 @@ | |||
| 155 | </property> | 155 | </property> |
| 156 | </widget> | 156 | </widget> |
| 157 | </item> | 157 | </item> |
| 158 | <item> | ||
| 159 | <widget class="QCheckBox" name="reporting_services"> | ||
| 160 | <property name="text"> | ||
| 161 | <string>Enable Verbose Reporting Services</string> | ||
| 162 | </property> | ||
| 163 | </widget> | ||
| 164 | </item> | ||
| 165 | <item> | ||
| 166 | <widget class="QLabel" name="label_3"> | ||
| 167 | <property name="font"> | ||
| 168 | <font> | ||
| 169 | <italic>true</italic> | ||
| 170 | </font> | ||
| 171 | </property> | ||
| 172 | <property name="text"> | ||
| 173 | <string>This will be reset automatically when yuzu closes.</string> | ||
| 174 | </property> | ||
| 175 | <property name="indent"> | ||
| 176 | <number>20</number> | ||
| 177 | </property> | ||
| 178 | </widget> | ||
| 179 | </item> | ||
| 158 | </layout> | 180 | </layout> |
| 159 | </widget> | 181 | </widget> |
| 160 | </item> | 182 | </item> |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index f3817bb87..9ac92e937 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -381,6 +381,8 @@ void Config::ReadValues() { | |||
| 381 | Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); | 381 | Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); |
| 382 | Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); | 382 | Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); |
| 383 | Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); | 383 | Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); |
| 384 | Settings::values.reporting_services = | ||
| 385 | sdl2_config->GetBoolean("Debugging", "reporting_services", false); | ||
| 384 | 386 | ||
| 385 | const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); | 387 | const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); |
| 386 | std::stringstream ss(title_list); | 388 | std::stringstream ss(title_list); |