summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2019-06-21 14:05:18 -0400
committerGravatar GitHub2019-06-21 14:05:18 -0400
commit96412848a9db0643198ea882824688f23dc19606 (patch)
tree2824eafaf4bc026cc3fc0ee498d1c5c623f3aa65 /src
parentMerge pull request #2291 from DarkLordZach/homebrew-testing (diff)
parentloader: Move NSO module tracking to AppLoader (diff)
downloadyuzu-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')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/arm_interface.cpp201
-rw-r--r--src/core/arm/arm_interface.h11
-rw-r--r--src/core/core.cpp9
-rw-r--r--src/core/core.h4
-rw-r--r--src/core/hle/kernel/svc.cpp8
-rw-r--r--src/core/hle/service/am/applets/applets.cpp34
-rw-r--r--src/core/hle/service/am/applets/applets.h16
-rw-r--r--src/core/hle/service/am/applets/error.cpp19
-rw-r--r--src/core/hle/service/am/applets/general_backend.cpp10
-rw-r--r--src/core/hle/service/am/applets/general_backend.h5
-rw-r--r--src/core/hle/service/fatal/fatal.cpp26
-rw-r--r--src/core/hle/service/prepo/prepo.cpp25
-rw-r--r--src/core/hle/service/service.cpp3
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp11
-rw-r--r--src/core/loader/deconstructed_rom_directory.h4
-rw-r--r--src/core/loader/loader.h6
-rw-r--r--src/core/loader/nax.cpp4
-rw-r--r--src/core/loader/nax.h2
-rw-r--r--src/core/loader/nca.cpp9
-rw-r--r--src/core/loader/nca.h2
-rw-r--r--src/core/loader/nso.cpp9
-rw-r--r--src/core/loader/nso.h5
-rw-r--r--src/core/loader/nsp.cpp4
-rw-r--r--src/core/loader/nsp.h2
-rw-r--r--src/core/loader/xci.cpp4
-rw-r--r--src/core/loader/xci.h2
-rw-r--r--src/core/reporter.cpp353
-rw-r--r--src/core/reporter.h56
-rw-r--r--src/core/settings.h1
-rw-r--r--src/yuzu/configuration/config.cpp4
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui22
-rw-r--r--src/yuzu_cmd/config.cpp2
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
468create_target_directory_groups(core) 470create_target_directory_groups(core)
469 471
470target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 472target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
471target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt mbedtls opus unicorn open_source_archives) 473target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives)
472if (ENABLE_WEB_SERVICE) 474if (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
10namespace Core { 15namespace Core {
11void 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); 17namespace {
18
19constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
20constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
21constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
22constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
23
24enum 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
34enum class ELFSymbolBinding : u8 {
35 Local = 0,
36 Global = 1,
37 Weak = 2,
38};
39
40enum class ELFSymbolVisibility : u8 {
41 Default = 0,
42 Internal = 1,
43 Hidden = 2,
44 Protected = 3,
45};
46
47struct 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};
60static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
61
62using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
63
64Symbols 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
124std::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
141constexpr u64 SEGMENT_BASE = 0x7100000000ull;
142
143std::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
199void 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
10namespace Common { 11namespace 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}
76struct System::Impl { 77struct 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
498const Reporter& System::GetReporter() const {
499 return impl->reporter;
500}
501
495System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { 502System::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;
68class ExclusiveMonitor; 69class ExclusiveMonitor;
69class FrameLimiter; 70class FrameLimiter;
70class PerfStats; 71class PerfStats;
72class Reporter;
71class TelemetrySession; 73class TelemetrySession;
72 74
73struct PerfStatsResults; 75struct 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
287private: 291private:
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
42namespace Kernel { 43namespace Kernel {
43namespace { 44namespace {
@@ -594,6 +595,7 @@ struct BreakReason {
594static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { 595static 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
36AppletDataBroker::~AppletDataBroker() = default; 36AppletDataBroker::~AppletDataBroker() = default;
37 37
38AppletDataBroker::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
38std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { 54std::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
74void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { 90void 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
78void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { 94void 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
83void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { 99void 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
87void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { 103void 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
15namespace Service::AM::Applets { 17namespace 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
17namespace Service::AM::Applets { 18namespace Service::AM::Applets {
18 19
@@ -83,13 +84,20 @@ void PhotoViewer::ViewFinished() {
83 broker.SignalStateChanged(); 84 broker.SignalStateChanged();
84} 85}
85 86
86StubApplet::StubApplet() = default; 87StubApplet::StubApplet(AppletId id) : id(id) {}
87 88
88StubApplet::~StubApplet() = default; 89StubApplet::~StubApplet() = default;
89 90
90void StubApplet::Initialize() { 91void 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
35class StubApplet final : public Applet { 35class StubApplet final : public Applet {
36public: 36public:
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
47private:
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
20namespace Service::Fatal { 21namespace 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
126static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) { 110static 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
10namespace Service::PlayReport { 18namespace Service::PlayReport {
11 19
@@ -40,8 +48,21 @@ public:
40 48
41private: 49private:
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
72namespace Service { 73namespace 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
217ResultStatus 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
48private: 50private:
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
270protected: 276protected:
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
97ResultStatus 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
45private: 47private:
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
109ResultStatus 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
45private: 47private:
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
191ResultStatus 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
91private:
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
186ResultStatus 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
52private: 54private:
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
152ResultStatus 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
52private: 54private:
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
19namespace {
20
21std::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
26std::string GetTimestamp() {
27 const auto time = std::time(nullptr);
28 return fmt::format("{:%FT%H-%M-%S}", *std::localtime(&time));
29}
30
31using namespace nlohmann;
32
33void 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
42json 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
55json 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
69json 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
98json 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
111json 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
127json 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
138template <bool read_value, typename DescriptorType>
139json 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
159json 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
179namespace Core {
180
181Reporter::Reporter(Core::System& system) : system(system) {}
182
183Reporter::~Reporter() = default;
184
185void 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
213void 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
238void 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
258void 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
294void 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
316void 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
338void 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
349bool 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
11union ResultCode;
12
13namespace Kernel {
14class HLERequestContext;
15} // namespace Kernel
16
17namespace Core {
18
19class Reporter {
20public:
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
50private:
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
41void ConfigureDebug::ApplyConfiguration() { 42void 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);