From 2179ad7483bde3c210bf4f638ae8801b3ba11214 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 17 May 2019 21:41:33 -0400 Subject: [PATCH 01/12] core: Track load offsets of NSO modules Needed for backtrace decomposition --- src/core/core.cpp | 10 ++++++++++ src/core/core.h | 5 +++++ src/core/loader/nso.cpp | 3 +++ 3 files changed, 18 insertions(+) diff --git a/src/core/core.cpp b/src/core/core.cpp index 7106151bd..96ba6a569 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -270,6 +270,8 @@ struct System::Impl { /// Telemetry session for this emulation session std::unique_ptr telemetry_session; + std::map> modules; + ResultStatus status = ResultStatus::Success; std::string status_details = ""; @@ -509,6 +511,14 @@ void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) { impl->content_provider->ClearSlot(slot); } +void System::RegisterNSOModule(std::string name, VAddr start_address) { + impl->modules.insert_or_assign(start_address, name); +} + +const std::map>& System::GetRegisteredNSOModules() const { + return impl->modules; +} + System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { return impl->Init(*this, emu_window); } diff --git a/src/core/core.h b/src/core/core.h index a9a756a4c..10542ba21 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -8,6 +8,7 @@ #include #include +#include #include "common/common_types.h" #include "core/file_sys/vfs_types.h" #include "core/hle/kernel/object.h" @@ -285,6 +286,10 @@ public: void ClearContentProvider(FileSys::ContentProviderUnionSlot slot); + void RegisterNSOModule(std::string name, VAddr start_address); + + const std::map>& GetRegisteredNSOModules() const; + private: System(); diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 8592b1f44..7beeaaff3 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -164,6 +164,9 @@ std::optional AppLoader_NSO::LoadModule(Kernel::Process& process, // Register module with GDBStub GDBStub::RegisterModule(file.GetName(), load_base, load_base); + // Register module for ARMInterface with System + Core::System::GetInstance().RegisterNSOModule(file.GetName(), load_base); + return load_base + image_size; } From 5574be21ccd8cf86eaba5c50d30ad898bafc8ea8 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 17 May 2019 21:43:26 -0400 Subject: [PATCH 02/12] arm_interface: Expand backtrace generation Returns results as a vector of entries for further processing. Logs addresses, offsets, and mangled name. --- src/core/arm/arm_interface.cpp | 190 +++++++++++++++++++++++++++++++-- src/core/arm/arm_interface.h | 11 ++ 2 files changed, 194 insertions(+), 7 deletions(-) diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 2223cbeed..2945fcff8 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -2,26 +2,202 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include +#include "common/bit_field.h" #include "common/common_types.h" #include "common/logging/log.h" #include "core/arm/arm_interface.h" +#include "core/core.h" #include "core/memory.h" namespace Core { -void ARM_Interface::LogBacktrace() const { - VAddr fp = GetReg(29); - VAddr lr = GetReg(30); - const VAddr sp = GetReg(13); - const VAddr pc = GetPC(); - LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc); +namespace { + +constexpr u64 ELF_DYNAMIC_TAG_NULL = 0; +constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5; +constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6; +constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11; + +enum class ELFSymbolType : u8 { + None = 0, + Object = 1, + Function = 2, + Section = 3, + File = 4, + Common = 5, + TLS = 6, +}; + +enum class ELFSymbolBinding : u8 { + Local = 0, + Global = 1, + Weak = 2, +}; + +enum class ELFSymbolVisibility : u8 { + Default = 0, + Internal = 1, + Hidden = 2, + Protected = 3, +}; + +struct ELFSymbol { + u32 name_index; + union { + u8 info; + + BitField<0, 4, ELFSymbolType> type; + BitField<4, 4, ELFSymbolBinding> binding; + }; + ELFSymbolVisibility visibility; + u16 sh_index; + u64 value; + u64 size; +}; +static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size."); + +using Symbols = std::vector>; + +Symbols GetSymbols(VAddr text_offset) { + const auto mod_offset = text_offset + Memory::Read32(text_offset + 4); + + if (mod_offset < text_offset || (mod_offset & 0b11) != 0 || + Memory::Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { + return {}; + } + + const auto dynamic_offset = Memory::Read32(mod_offset + 0x4) + mod_offset; + + VAddr string_table_offset{}; + VAddr symbol_table_offset{}; + u64 symbol_entry_size{}; + + VAddr dynamic_index = dynamic_offset; while (true) { - LOG_ERROR(Core_ARM, "{:016X}", lr); + const auto tag = Memory::Read64(dynamic_index); + const auto value = Memory::Read64(dynamic_index + 0x8); + dynamic_index += 0x10; + + if (tag == ELF_DYNAMIC_TAG_NULL) + break; + + if (tag == ELF_DYNAMIC_TAG_STRTAB) + string_table_offset = value; + else if (tag == ELF_DYNAMIC_TAG_SYMTAB) + symbol_table_offset = value; + else if (tag == ELF_DYNAMIC_TAG_SYMENT) + symbol_entry_size = value; + } + + if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) { + return {}; + } + + const auto string_table_address = text_offset + string_table_offset; + const auto symbol_table_address = text_offset + symbol_table_offset; + + Symbols out; + + VAddr symbol_index = symbol_table_address; + while (symbol_index < string_table_address) { + ELFSymbol symbol{}; + Memory::ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol)); + + VAddr string_offset = string_table_address + symbol.name_index; + std::string name; + for (u8 c = Memory::Read8(string_offset); c != 0; c = Memory::Read8(++string_offset)) { + name += static_cast(c); + } + + symbol_index += symbol_entry_size; + out.push_back({symbol, name}); + } + + return out; +} + +std::optional GetSymbolName(const Symbols& symbols, VAddr func_address) { + const auto iter = + std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) { + const auto& [symbol, name] = pair; + const auto end_address = symbol.value + symbol.size; + return func_address >= symbol.value && func_address < end_address; + }); + + if (iter == symbols.end()) + return std::nullopt; + return iter->second; +} + +} // Anonymous namespace + +constexpr u64 SEGMENT_BASE = 0x7100000000ull; + +std::vector ARM_Interface::GetBacktrace() const { + std::vector out; + + auto fp = GetReg(29); + auto lr = GetReg(30); + + while (true) { + out.push_back({"", 0, lr, 0}); if (!fp) { break; } lr = Memory::Read64(fp + 8) - 4; fp = Memory::Read64(fp); } + + const auto& modules{System::GetInstance().GetRegisteredNSOModules()}; + std::map symbols; + for (const auto& module : modules) { + symbols.insert_or_assign(module.second, GetSymbols(module.first)); + } + + for (auto& entry : out) { + VAddr base = 0; + for (const auto& module : modules) { + if (entry.original_address >= module.first) { + entry.module = module.second; + base = module.first; + break; + } + } + + entry.offset = entry.original_address - base; + entry.address = SEGMENT_BASE + entry.offset; + + if (entry.module.empty()) + entry.module = "unknown"; + + const auto symbol_set = symbols.find(entry.module); + if (symbol_set != symbols.end()) { + const auto symbol = GetSymbolName(symbol_set->second, entry.offset); + if (symbol.has_value()) { + // TODO(DarkLordZach): Add demangling of symbol names. + entry.name = *symbol; + } + } + } + + return out; } + +void ARM_Interface::LogBacktrace() const { + const VAddr sp = GetReg(13); + const VAddr pc = GetPC(); + LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc); + LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address", + "Offset", "Symbol"); + LOG_ERROR(Core_ARM, "{}", std::string(100, '-')); + + const auto backtrace = GetBacktrace(); + for (const auto& entry : backtrace) { + LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address, + entry.original_address, entry.offset, entry.name); + } +} + } // 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 @@ #pragma once #include +#include #include "common/common_types.h" namespace Common { @@ -152,6 +153,16 @@ public: /// Prepare core for thread reschedule (if needed to correctly handle state) virtual void PrepareReschedule() = 0; + struct BacktraceEntry { + std::string module; + u64 address; + u64 original_address; + u64 offset; + std::string name; + }; + + std::vector GetBacktrace() const; + /// fp (= r29) points to the last frame record. /// Note that this is the frame record for the *previous* frame, not the current one. /// Note we need to subtract 4 from our last read to get the proper address From 658d2ee97d855634d9ecf7ce31b5a3668bc57b37 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 17 May 2019 21:44:28 -0400 Subject: [PATCH 03/12] settings: Add 'Reporting Services' config option Full enable/disable for all reports. --- src/core/settings.h | 1 + src/yuzu/configuration/config.cpp | 18 +++++++++--------- src/yuzu_cmd/config.cpp | 2 ++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/core/settings.h b/src/core/settings.h index 5ff3634aa..fe6b26ef0 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -416,6 +416,7 @@ struct Values { std::string program_args; bool dump_exefs; bool dump_nso; + bool reporting_services; // WebService bool enable_telemetry; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index db27da23e..ed14474aa 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -465,16 +465,16 @@ void Config::ReadDataStorageValues() { } void Config::ReadDebuggingValues() { - qt_config->beginGroup(QStringLiteral("Debugging")); + qt_config->beginGroup(QStringLiteral("Debugging")); - Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); - Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); - Settings::values.program_args = - ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString(); - Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); - Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); - - qt_config->endGroup(); + Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); + Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); + Settings::values.program_args = + ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString(); + Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); + Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); + Settings::values.reporting_services = ReadSetting("reporting_services", false).toBool(); + qt_config->endGroup(); } void Config::ReadDisabledAddOnValues() { diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index d0ae058fd..6c13e91e7 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -383,6 +383,8 @@ void Config::ReadValues() { Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); + Settings::values.reporting_services = + sdl2_config->GetBoolean("Debugging", "reporting_services", false); const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); std::stringstream ss(title_list); From 6f0ee45b5c3d62cbd74ae45bd56ade8d2fbbb6d2 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 17 May 2019 21:45:16 -0400 Subject: [PATCH 04/12] qt: Make UI option for 'Reporting Services' temporary Reports are unnecessary for normal users and this is to prevent 'power' users from enabling the feature by accident. --- src/yuzu/configuration/configure_debug.cpp | 2 ++ src/yuzu/configuration/configure_debug.ui | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 550cf9dca..77466d007 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -35,6 +35,7 @@ void ConfigureDebug::setConfiguration() { ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); ui->dump_exefs->setChecked(Settings::values.dump_exefs); ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso); + ui->reporting_services->setChecked(Settings::values.reporting_services); } void ConfigureDebug::applyConfiguration() { @@ -45,6 +46,7 @@ void ConfigureDebug::applyConfiguration() { Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); Settings::values.dump_exefs = ui->dump_exefs->isChecked(); Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked(); + Settings::values.reporting_services = ui->reporting_services->isChecked(); Debugger::ToggleConsole(); Log::Filter filter; filter.ParseFilterString(Settings::values.log_filter); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 758a92335..73bb85e13 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -155,6 +155,28 @@ + + + + Enable Verbose Reporting Services + + + + + + + + true + + + + This will be reset automatically when yuzu closes. + + + 20 + + + From 2dde8f5cfe81648d05d60285ab5b17a6f61c486e Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 17 May 2019 21:45:56 -0400 Subject: [PATCH 05/12] core: Add Reporter class to take/save reports --- src/core/CMakeLists.txt | 4 +- src/core/core.cpp | 6 + src/core/core.h | 3 + src/core/reporter.cpp | 351 ++++++++++++++++++++++++++++++++++++++++ src/core/reporter.h | 53 ++++++ 5 files changed, 416 insertions(+), 1 deletion(-) create mode 100644 src/core/reporter.cpp create mode 100644 src/core/reporter.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2ace866ee..3eacb11b4 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -452,6 +452,8 @@ add_library(core STATIC memory_setup.h perf_stats.cpp perf_stats.h + reporter.cpp + reporter.h settings.cpp settings.h telemetry_session.cpp @@ -464,7 +466,7 @@ add_library(core STATIC create_target_directory_groups(core) target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) -target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt mbedtls opus unicorn open_source_archives) +target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives) if (ENABLE_WEB_SERVICE) target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) target_link_libraries(core PRIVATE web_service) diff --git a/src/core/core.cpp b/src/core/core.cpp index 96ba6a569..db4c067df 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -34,6 +34,7 @@ #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" #include "core/perf_stats.h" +#include "core/reporter.h" #include "core/settings.h" #include "core/telemetry_session.h" #include "file_sys/cheat_engine.h" @@ -271,6 +272,7 @@ struct System::Impl { std::unique_ptr telemetry_session; std::map> modules; + Reporter reporter; ResultStatus status = ResultStatus::Success; std::string status_details = ""; @@ -519,6 +521,10 @@ const std::map>& System::GetRegisteredNSOModu return impl->modules; } +const Reporter& System::GetReporter() const { + return impl->reporter; +} + System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { return impl->Init(*this, emu_window); } diff --git a/src/core/core.h b/src/core/core.h index 10542ba21..440cdbbf0 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -69,6 +69,7 @@ class Cpu; class ExclusiveMonitor; class FrameLimiter; class PerfStats; +class Reporter; class TelemetrySession; struct PerfStatsResults; @@ -290,6 +291,8 @@ public: const std::map>& GetRegisteredNSOModules() const; + const Reporter& GetReporter() const; + private: System(); diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp new file mode 100644 index 000000000..95dcfffb5 --- /dev/null +++ b/src/core/reporter.cpp @@ -0,0 +1,351 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "common/file_util.h" +#include "common/hex_util.h" +#include "common/scm_rev.h" +#include "core/arm/arm_interface.h" +#include "core/core.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/process.h" +#include "core/reporter.h" +#include "core/settings.h" +#include "fmt/time.h" + +namespace { + +std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) { + return fmt::format("{}{}/{:016X}_{}.json", FileUtil::GetUserPath(FileUtil::UserPath::LogDir), + type, title_id, timestamp); +} + +std::string GetTimestamp() { + const auto time = std::time(nullptr); + return fmt::format("{:%FT%H-%M-%S}", *std::localtime(&time)); +} + +using namespace nlohmann; + +void SaveToFile(const json& json, const std::string& filename) { + FileUtil::CreateFullPath(filename); + std::ofstream file( + FileUtil::SanitizePath(filename, FileUtil::DirectorySeparator::PlatformDefault)); + file << std::setw(4) << json << std::endl; + file.flush(); + file.close(); +} + +json GetYuzuVersionData() { + return { + {"scm_rev", std::string(Common::g_scm_rev)}, + {"scm_branch", std::string(Common::g_scm_branch)}, + {"scm_desc", std::string(Common::g_scm_desc)}, + {"build_name", std::string(Common::g_build_name)}, + {"build_date", std::string(Common::g_build_date)}, + {"build_fullname", std::string(Common::g_build_fullname)}, + {"build_version", std::string(Common::g_build_version)}, + {"shader_cache_version", std::string(Common::g_shader_cache_version)}, + }; +} + +json GetReportCommonData(u64 title_id, ResultCode result, const std::string& timestamp, + std::optional user_id = {}) { + auto out = json{ + {"title_id", fmt::format("{:016X}", title_id)}, + {"result_raw", fmt::format("{:08X}", result.raw)}, + {"result_module", fmt::format("{:08X}", static_cast(result.module.Value()))}, + {"result_description", fmt::format("{:08X}", result.description.Value())}, + {"timestamp", timestamp}, + }; + if (user_id.has_value()) + out["user_id"] = fmt::format("{:016X}{:016X}", (*user_id)[1], (*user_id)[0]); + return std::move(out); +} + +json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64 sp, u64 pc, + u64 pstate, std::array registers, + std::optional> backtrace = {}) { + auto out = json{ + {"entry_point", fmt::format("{:016X}", entry_point)}, + {"sp", fmt::format("{:016X}", sp)}, + {"pc", fmt::format("{:016X}", pc)}, + {"pstate", fmt::format("{:016X}", pstate)}, + {"architecture", architecture}, + }; + + auto registers_out = json::object(); + for (std::size_t i = 0; i < registers.size(); ++i) { + registers_out[fmt::format("X{:02d}", i)] = fmt::format("{:016X}", registers[i]); + } + + out["registers"] = std::move(registers_out); + + if (backtrace.has_value()) { + auto backtrace_out = json::array(); + for (const auto& entry : *backtrace) { + backtrace_out.push_back(fmt::format("{:016X}", entry)); + } + out["backtrace"] = std::move(backtrace_out); + } + + return std::move(out); +} + +json GetProcessorStateDataAuto() { + const auto* process{Core::CurrentProcess()}; + const auto& vm_manager{process->VMManager()}; + auto& arm{Core::CurrentArmInterface()}; + + Core::ARM_Interface::ThreadContext context{}; + arm.SaveContext(context); + + return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32", + vm_manager.GetCodeRegionBaseAddress(), context.sp, context.pc, + context.pstate, context.cpu_registers); +} + +json GetBacktraceData() { + auto out = json::array(); + const auto& backtrace{Core::CurrentArmInterface().GetBacktrace()}; + for (const auto& entry : backtrace) { + out.push_back({ + {"module", entry.module}, + {"address", fmt::format("{:016X}", entry.address)}, + {"original_address", fmt::format("{:016X}", entry.original_address)}, + {"offset", fmt::format("{:016X}", entry.offset)}, + {"symbol_name", entry.name}, + }); + } + + return std::move(out); +} + +json GetFullDataAuto(const std::string& timestamp, u64 title_id) { + json out; + + out["yuzu_version"] = GetYuzuVersionData(); + out["report_common"] = GetReportCommonData(title_id, RESULT_SUCCESS, timestamp); + out["processor_state"] = GetProcessorStateDataAuto(); + out["backtrace"] = GetBacktraceData(); + + return std::move(out); +} + +template +json GetHLEBufferDescriptorData(const std::vector& buffer) { + auto buffer_out = json::array(); + for (const auto& desc : buffer) { + auto entry = json{ + {"address", fmt::format("{:016X}", desc.Address())}, + {"size", fmt::format("{:016X}", desc.Size())}, + }; + + if constexpr (read_value) { + std::vector data(desc.Size()); + Memory::ReadBlock(desc.Address(), data.data(), desc.Size()); + entry["data"] = Common::HexVectorToString(data); + } + + buffer_out.push_back(std::move(entry)); + } + + return std::move(buffer_out); +} + +json GetHLERequestContextData(Kernel::HLERequestContext& ctx) { + json out; + + auto cmd_buf = json::array(); + for (std::size_t i = 0; i < IPC::COMMAND_BUFFER_LENGTH; ++i) { + cmd_buf.push_back(fmt::format("{:08X}", ctx.CommandBuffer()[i])); + } + + out["command_buffer"] = std::move(cmd_buf); + + out["buffer_descriptor_a"] = GetHLEBufferDescriptorData(ctx.BufferDescriptorA()); + out["buffer_descriptor_b"] = GetHLEBufferDescriptorData(ctx.BufferDescriptorB()); + out["buffer_descriptor_c"] = GetHLEBufferDescriptorData(ctx.BufferDescriptorC()); + out["buffer_descriptor_x"] = GetHLEBufferDescriptorData(ctx.BufferDescriptorX()); + + return std::move(out); +} + +} // Anonymous namespace + +namespace Core { + +Reporter::Reporter() = default; + +Reporter::~Reporter() = default; + +void Reporter::SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, + u64 sp, u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far, + const std::array& registers, + const std::array& backtrace, u32 backtrace_size, + const std::string& arch, u32 unk10) const { + if (!IsReportingEnabled()) + return; + + const auto timestamp{GetTimestamp()}; + json out; + + out["yuzu_version"] = GetYuzuVersionData(); + out["report_common"] = GetReportCommonData(title_id, result, timestamp); + + auto proc_out = GetProcessorStateData(arch, entry_point, sp, pc, pstate, registers, backtrace); + proc_out["set_flags"] = fmt::format("{:016X}", set_flags); + proc_out["afsr0"] = fmt::format("{:016X}", afsr0); + proc_out["afsr1"] = fmt::format("{:016X}", afsr1); + proc_out["esr"] = fmt::format("{:016X}", esr); + proc_out["far"] = fmt::format("{:016X}", far); + proc_out["backtrace_size"] = fmt::format("{:08X}", backtrace_size); + proc_out["unknown_10"] = fmt::format("{:08X}", unk10); + + out["processor_state"] = std::move(proc_out); + + SaveToFile(std::move(out), GetPath("crash_report", title_id, timestamp)); +} + +void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, + std::optional> resolved_buffer) const { + if (!IsReportingEnabled()) + return; + + const auto timestamp{GetTimestamp()}; + const auto title_id{Core::CurrentProcess()->GetTitleID()}; + auto out = GetFullDataAuto(timestamp, title_id); + + auto break_out = json{ + {"type", fmt::format("{:08X}", type)}, + {"signal_debugger", fmt::format("{}", signal_debugger)}, + {"info1", fmt::format("{:016X}", info1)}, + {"info2", fmt::format("{:016X}", info2)}, + }; + + if (resolved_buffer.has_value()) { + break_out["debug_buffer"] = Common::HexVectorToString(*resolved_buffer); + } + + out["svc_break"] = std::move(break_out); + + SaveToFile(std::move(out), GetPath("svc_break_report", title_id, timestamp)); +} + +void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, + const std::string& name, + const std::string& service_name) const { + if (!IsReportingEnabled()) + return; + + const auto timestamp{GetTimestamp()}; + const auto title_id{Core::CurrentProcess()->GetTitleID()}; + auto out = GetFullDataAuto(timestamp, title_id); + + auto function_out = GetHLERequestContextData(ctx); + function_out["command_id"] = command_id; + function_out["function_name"] = name; + function_out["service_name"] = service_name; + + out["function"] = std::move(function_out); + + SaveToFile(std::move(out), GetPath("unimpl_func_report", title_id, timestamp)); +} + +void Reporter::SaveUnimplementedAppletReport( + u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color, + bool startup_sound, u64 system_tick, std::vector> normal_channel, + std::vector> interactive_channel) const { + if (!IsReportingEnabled()) + return; + + const auto timestamp{GetTimestamp()}; + const auto title_id{Core::CurrentProcess()->GetTitleID()}; + auto out = GetFullDataAuto(timestamp, title_id); + + out["applet_common_args"] = { + {"applet_id", fmt::format("{:02X}", applet_id)}, + {"common_args_version", fmt::format("{:08X}", common_args_version)}, + {"library_version", fmt::format("{:08X}", library_version)}, + {"theme_color", fmt::format("{:08X}", theme_color)}, + {"startup_sound", fmt::format("{}", startup_sound)}, + {"system_tick", fmt::format("{:016X}", system_tick)}, + }; + + auto normal_out = json::array(); + for (const auto& data : normal_channel) { + normal_out.push_back(Common::HexVectorToString(data)); + } + + auto interactive_out = json::array(); + for (const auto& data : interactive_channel) { + interactive_out.push_back(Common::HexVectorToString(data)); + } + + out["applet_normal_data"] = std::move(normal_out); + out["applet_interactive_data"] = std::move(interactive_out); + + SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp)); +} + +void Reporter::SavePlayReport(u64 title_id, u64 unk1, std::vector> data, + std::optional user_id) const { + if (!IsReportingEnabled()) + return; + + const auto timestamp{GetTimestamp()}; + json out; + + out["yuzu_version"] = GetYuzuVersionData(); + out["report_common"] = GetReportCommonData(title_id, RESULT_SUCCESS, timestamp, user_id); + + auto data_out = json::array(); + for (const auto& d : data) { + data_out.push_back(Common::HexVectorToString(d)); + } + + out["play_report_unk1"] = fmt::format("{:016X}", unk1); + out["play_report_data"] = std::move(data_out); + + SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp)); +} + +void Reporter::SaveErrorReport(u64 title_id, ResultCode result, + std::optional custom_text_main, + std::optional custom_text_detail) const { + if (!IsReportingEnabled()) + return; + + const auto timestamp{GetTimestamp()}; + json out; + + out["yuzu_version"] = GetYuzuVersionData(); + out["report_common"] = GetReportCommonData(title_id, result, timestamp); + out["processor_state"] = GetProcessorStateDataAuto(); + out["backtrace"] = GetBacktraceData(); + + out["error_custom_text"] = { + {"main", *custom_text_main}, + {"detail", *custom_text_detail}, + }; + + SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); +} + +void Reporter::SaveUserReport() const { + if (!IsReportingEnabled()) + return; + + const auto timestamp{GetTimestamp()}; + const auto title_id{Core::CurrentProcess()->GetTitleID()}; + + SaveToFile(GetFullDataAuto(timestamp, title_id), GetPath("user_report", title_id, timestamp)); +} + +bool Reporter::IsReportingEnabled() const { + return Settings::values.reporting_services; +} + +} // namespace Core diff --git a/src/core/reporter.h b/src/core/reporter.h new file mode 100644 index 000000000..2f798ee16 --- /dev/null +++ b/src/core/reporter.h @@ -0,0 +1,53 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include "common/common_types.h" +#include "core/hle/result.h" + +namespace Kernel { +class HLERequestContext; +} // namespace Kernel + +namespace Core { + +class Reporter { +public: + Reporter(); + ~Reporter(); + + void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp, + u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far, + const std::array& registers, const std::array& backtrace, + u32 backtrace_size, const std::string& arch, u32 unk10) const; + + void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, + std::optional> resolved_buffer = {}) const; + + void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, + const std::string& name, + const std::string& service_name) const; + + void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version, + u32 theme_color, bool startup_sound, u64 system_tick, + std::vector> normal_channel, + std::vector> interactive_channel) const; + + void SavePlayReport(u64 title_id, u64 unk1, std::vector> data, + std::optional user_id = {}) const; + + void SaveErrorReport(u64 title_id, ResultCode result, + std::optional custom_text_main = {}, + std::optional custom_text_detail = {}) const; + + void SaveUserReport() const; + +private: + bool IsReportingEnabled() const; +}; + +} // namespace Core From 186db894b9e7f322d5d339468f33feca4689d5e9 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 17 May 2019 21:46:17 -0400 Subject: [PATCH 06/12] svc: Save report on call to svcBreak --- src/core/hle/kernel/svc.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 5a5851f66..8b5216384 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -38,6 +38,7 @@ #include "core/hle/result.h" #include "core/hle/service/service.h" #include "core/memory.h" +#include "core/reporter.h" namespace Kernel { namespace { @@ -594,6 +595,7 @@ struct BreakReason { static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { BreakReason break_reason{reason}; bool has_dumped_buffer{}; + std::vector debug_buffer; const auto handle_debug_buffer = [&](VAddr addr, u64 sz) { if (sz == 0 || addr == 0 || has_dumped_buffer) { @@ -605,7 +607,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr)); } else { // We don't know what's in here so we'll hexdump it - std::vector debug_buffer(sz); + debug_buffer.resize(sz); Memory::ReadBlock(addr, debug_buffer.data(), sz); std::string hexdump; 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) { break; } + system.GetReporter().SaveSvcBreakReport( + static_cast(break_reason.break_type.Value()), break_reason.signal_debugger, info1, + info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); + if (!break_reason.signal_debugger) { LOG_CRITICAL( Debug_Emulated, From bdc47693f154ffe41f16a452f65d9bd609a72683 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 17 May 2019 21:47:07 -0400 Subject: [PATCH 07/12] applets: Save report on stubbed applet This also reworks the applet data storage to be peekable. --- src/core/hle/service/am/applets/applets.cpp | 33 ++++++++++++++----- src/core/hle/service/am/applets/applets.h | 16 ++++++--- .../service/am/applets/general_backend.cpp | 10 +++++- .../hle/service/am/applets/general_backend.h | 5 ++- 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index e812c66e9..2a945bc7b 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -35,12 +35,27 @@ AppletDataBroker::AppletDataBroker() { AppletDataBroker::~AppletDataBroker() = default; +AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() const { + std::vector> out_normal; + std::vector> out_interactive; + + for (const auto& storage : in_channel) { + out_normal.push_back(storage->GetData()); + } + + for (const auto& storage : in_interactive_channel) { + out_interactive.push_back(storage->GetData()); + } + + return {out_normal, out_interactive}; +} + std::unique_ptr AppletDataBroker::PopNormalDataToGame() { if (out_channel.empty()) return nullptr; auto out = std::move(out_channel.front()); - out_channel.pop(); + out_channel.pop_front(); return out; } @@ -49,7 +64,7 @@ std::unique_ptr AppletDataBroker::PopNormalDataToApplet() { return nullptr; auto out = std::move(in_channel.front()); - in_channel.pop(); + in_channel.pop_front(); return out; } @@ -58,7 +73,7 @@ std::unique_ptr AppletDataBroker::PopInteractiveDataToGame() { return nullptr; auto out = std::move(out_interactive_channel.front()); - out_interactive_channel.pop(); + out_interactive_channel.pop_front(); return out; } @@ -67,25 +82,25 @@ std::unique_ptr AppletDataBroker::PopInteractiveDataToApplet() { return nullptr; auto out = std::move(in_interactive_channel.front()); - in_interactive_channel.pop(); + in_interactive_channel.pop_front(); return out; } void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { - in_channel.push(std::make_unique(storage)); + in_channel.push_back(std::make_unique(storage)); } void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { - out_channel.push(std::make_unique(storage)); + out_channel.push_back(std::make_unique(storage)); pop_out_data_event.writable->Signal(); } void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { - in_interactive_channel.push(std::make_unique(storage)); + in_interactive_channel.push_back(std::make_unique(storage)); } void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { - out_interactive_channel.push(std::make_unique(storage)); + out_interactive_channel.push_back(std::make_unique(storage)); pop_interactive_out_data_event.writable->Signal(); } @@ -189,7 +204,7 @@ std::shared_ptr AppletManager::GetApplet(AppletId id) const { UNIMPLEMENTED_MSG( "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", static_cast(id)); - return std::make_shared(); + return std::make_shared(id); } } diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 7f932672c..8c2c15968 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -54,6 +54,14 @@ public: AppletDataBroker(); ~AppletDataBroker(); + struct RawChannelData { + std::vector> normal; + std::vector> interactive; + }; + + // Retrieves but does not pop the data sent to applet. + RawChannelData PeekDataToAppletForDebug() const; + std::unique_ptr PopNormalDataToGame(); std::unique_ptr PopNormalDataToApplet(); @@ -76,16 +84,16 @@ private: // Queues are named from applet's perspective // PopNormalDataToApplet and PushNormalDataFromGame - std::queue> in_channel; + std::deque> in_channel; // PopNormalDataToGame and PushNormalDataFromApplet - std::queue> out_channel; + std::deque> out_channel; // PopInteractiveDataToApplet and PushInteractiveDataFromGame - std::queue> in_interactive_channel; + std::deque> in_interactive_channel; // PopInteractiveDataToGame and PushInteractiveDataFromApplet - std::queue> out_interactive_channel; + std::deque> out_interactive_channel; Kernel::EventPair state_changed_event; diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp index c591b9ac2..55865176c 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 @@ #include "core/hle/result.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/general_backend.h" +#include "core/reporter.h" namespace Service::AM::Applets { @@ -83,13 +84,20 @@ void PhotoViewer::ViewFinished() { broker.SignalStateChanged(); } -StubApplet::StubApplet() = default; +StubApplet::StubApplet(AppletId id) : id(id) {} StubApplet::~StubApplet() = default; void StubApplet::Initialize() { LOG_WARNING(Service_AM, "called (STUBBED)"); Applet::Initialize(); + + const auto data = broker.PeekDataToAppletForDebug(); + Core::System::GetInstance().GetReporter().SaveUnimplementedAppletReport( + static_cast(id), common_args.arguments_version, common_args.library_version, + common_args.theme_color, common_args.play_startup_sound, common_args.system_tick, + data.normal, data.interactive); + LogCurrentStorage(broker, "Initialize"); } diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h index 2dd255d7c..bc919a8dd 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: class StubApplet final : public Applet { public: - StubApplet(); + StubApplet(AppletId id); ~StubApplet() override; void Initialize() override; @@ -43,6 +43,9 @@ public: ResultCode GetStatus() const override; void ExecuteInteractive() override; void Execute() override; + +private: + AppletId id; }; } // namespace Service::AM::Applets From 24392c8ec83b1b3f0b90bc80f37466c9a853f63d Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 17 May 2019 21:47:47 -0400 Subject: [PATCH 08/12] applets/error: Save report on error applet This matches official behavior with the erpt/eclct/eupld service chain. --- src/core/hle/service/am/applets/error.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) 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 @@ #include "common/string_util.h" #include "core/core.h" #include "core/frontend/applets/error.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/error.h" +#include "core/reporter.h" namespace Service::AM::Applets { @@ -143,9 +145,12 @@ void Error::Execute() { } const auto callback = [this] { DisplayCompleted(); }; + const auto title_id = Core::CurrentProcess()->GetTitleID(); + const auto& reporter{Core::System::GetInstance().GetReporter()}; switch (mode) { case ErrorAppletMode::ShowError: + reporter.SaveErrorReport(title_id, error_code); frontend.ShowError(error_code, callback); break; case ErrorAppletMode::ShowSystemError: @@ -156,14 +161,18 @@ void Error::Execute() { const auto& detail_text = system ? args->system_error.detail_text : args->application_error.detail_text; - frontend.ShowCustomErrorText( - error_code, - Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()), - Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()), - callback); + const auto main_text_string = + Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()); + const auto detail_text_string = + Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()); + + reporter.SaveErrorReport(title_id, error_code, main_text_string, detail_text_string); + frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback); break; } case ErrorAppletMode::ShowErrorRecord: + reporter.SaveErrorReport(title_id, error_code, + fmt::format("{:016X}", args->error_record.posix_time)); frontend.ShowErrorWithTimestamp( error_code, std::chrono::seconds{args->error_record.posix_time}, callback); break; From fc309bf893311b55bcff747a3ef3fe0afd0c7b96 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 17 May 2019 21:48:08 -0400 Subject: [PATCH 09/12] service: Save report on unimplemented function call --- src/core/hle/service/service.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 00806b0ed..fc29bb5da 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -68,6 +68,7 @@ #include "core/hle/service/usb/usb.h" #include "core/hle/service/vi/vi.h" #include "core/hle/service/wlan/wlan.h" +#include "core/reporter.h" namespace Service { @@ -148,6 +149,8 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext } buf.push_back('}'); + Core::System::GetInstance().GetReporter().SaveUnimplementedFunctionReport( + ctx, ctx.GetCommand(), function_name, service_name); UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf)); } From 34e9736910b029ceb87a24cae1a33a176267c889 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 17 May 2019 21:48:47 -0400 Subject: [PATCH 10/12] fatal: Save report on fatal:u call Matches offical behavior with creport and replaces old log/text based report system. --- src/core/hle/service/fatal/fatal.cpp | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) 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 @@ #include "core/hle/service/fatal/fatal.h" #include "core/hle/service/fatal/fatal_p.h" #include "core/hle/service/fatal/fatal_u.h" +#include "core/reporter.h" namespace Service::Fatal { @@ -100,27 +101,10 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { LOG_ERROR(Service_Fatal, "{}", crash_report); - const std::string crashreport_dir = - FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs"; - - if (!FileUtil::CreateFullPath(crashreport_dir)) { - LOG_ERROR( - Service_Fatal, - "Unable to create crash report directory. Possible log directory permissions issue."); - return; - } - - const std::time_t t = std::time(nullptr); - const std::string crashreport_filename = - fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t)); - - auto file = FileUtil::IOFile(crashreport_filename, "wb"); - if (file.IsOpen()) { - file.WriteString(crash_report); - LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename); - } else { - LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename); - } + Core::System::GetInstance().GetReporter().SaveCrashReport( + title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc, + info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace, + info.backtrace_size, info.ArchAsString(), info.unk10); } static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) { From cdf52b9374469a69d04b80753af12da57c3ad57e Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 17 May 2019 21:49:21 -0400 Subject: [PATCH 11/12] prepo: Save reports from PlayReport service Logs a lot of seemingly innocuous telemetry games generate. --- src/core/hle/service/prepo/prepo.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index e4fcee9f8..961b8882d 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -2,10 +2,18 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include "common/file_util.h" +#include "common/hex_util.h" #include "common/logging/log.h" +#include "common/scm_rev.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/process.h" +#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/prepo/prepo.h" #include "core/hle/service/service.h" +#include "core/reporter.h" +#include "core/settings.h" namespace Service::PlayReport { @@ -40,8 +48,21 @@ public: private: void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) { - // TODO(ogniK): Do we want to add play report? - LOG_WARNING(Service_PREPO, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto user_id = rp.PopRaw(); + const auto unk1 = rp.PopRaw(); + + const auto data1 = ctx.ReadBuffer(0); + const auto data2 = ctx.ReadBuffer(1); + + LOG_DEBUG( + Service_PREPO, + "called, user_id={:016X}{:016X}, unk1={:016X}, data1_size={:016X}, data2_size={:016X}", + user_id[1], user_id[0], unk1, data1.size(), data2.size()); + + const auto& reporter{Core::System::GetInstance().GetReporter()}; + reporter.SavePlayReport(Core::CurrentProcess()->GetTitleID(), unk1, {data1, data2}, + user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); From b77fde7c5c1f63aad9d4f01ea625805661870f3e Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 26 May 2019 11:40:41 -0400 Subject: [PATCH 12/12] loader: Move NSO module tracking to AppLoader Also cleanup of general stuff --- src/core/arm/arm_interface.cpp | 27 +++++-- src/core/core.cpp | 11 +-- src/core/core.h | 4 - src/core/hle/service/am/applets/applets.cpp | 5 +- .../hle/service/am/applets/general_backend.h | 2 +- src/core/hle/service/prepo/prepo.cpp | 6 +- .../loader/deconstructed_rom_directory.cpp | 11 +++ src/core/loader/deconstructed_rom_directory.h | 4 + src/core/loader/loader.h | 6 ++ src/core/loader/nax.cpp | 4 + src/core/loader/nax.h | 2 + src/core/loader/nca.cpp | 9 +++ src/core/loader/nca.h | 2 + src/core/loader/nso.cpp | 12 ++- src/core/loader/nso.h | 5 ++ src/core/loader/nsp.cpp | 4 + src/core/loader/nsp.h | 2 + src/core/loader/xci.cpp | 4 + src/core/loader/xci.h | 2 + src/core/reporter.cpp | 74 ++++++++++--------- src/core/reporter.h | 9 ++- src/yuzu/configuration/config.cpp | 22 +++--- 22 files changed, 147 insertions(+), 80 deletions(-) diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 2945fcff8..372612c9b 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -9,6 +9,7 @@ #include "common/logging/log.h" #include "core/arm/arm_interface.h" #include "core/core.h" +#include "core/loader/loader.h" #include "core/memory.h" namespace Core { @@ -80,15 +81,17 @@ Symbols GetSymbols(VAddr text_offset) { const auto value = Memory::Read64(dynamic_index + 0x8); dynamic_index += 0x10; - if (tag == ELF_DYNAMIC_TAG_NULL) + if (tag == ELF_DYNAMIC_TAG_NULL) { break; + } - if (tag == ELF_DYNAMIC_TAG_STRTAB) + if (tag == ELF_DYNAMIC_TAG_STRTAB) { string_table_offset = value; - else if (tag == ELF_DYNAMIC_TAG_SYMTAB) + } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) { symbol_table_offset = value; - else if (tag == ELF_DYNAMIC_TAG_SYMENT) + } else if (tag == ELF_DYNAMIC_TAG_SYMENT) { symbol_entry_size = value; + } } if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) { @@ -126,8 +129,10 @@ std::optional GetSymbolName(const Symbols& symbols, VAddr func_addr return func_address >= symbol.value && func_address < end_address; }); - if (iter == symbols.end()) + if (iter == symbols.end()) { return std::nullopt; + } + return iter->second; } @@ -150,7 +155,12 @@ std::vector ARM_Interface::GetBacktrace() const { fp = Memory::Read64(fp); } - const auto& modules{System::GetInstance().GetRegisteredNSOModules()}; + std::map modules; + auto& loader{System::GetInstance().GetAppLoader()}; + if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { + return {}; + } + std::map symbols; for (const auto& module : modules) { symbols.insert_or_assign(module.second, GetSymbols(module.first)); @@ -158,7 +168,8 @@ std::vector ARM_Interface::GetBacktrace() const { for (auto& entry : out) { VAddr base = 0; - for (const auto& module : modules) { + for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) { + const auto& module{*iter}; if (entry.original_address >= module.first) { entry.module = module.second; base = module.first; @@ -191,7 +202,7 @@ void ARM_Interface::LogBacktrace() const { LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc); LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address", "Offset", "Symbol"); - LOG_ERROR(Core_ARM, "{}", std::string(100, '-')); + LOG_ERROR(Core_ARM, ""); const auto backtrace = GetBacktrace(); for (const auto& entry : backtrace) { diff --git a/src/core/core.cpp b/src/core/core.cpp index db4c067df..ac9eeddbb 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -83,7 +83,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, return vfs->OpenFile(path, FileSys::Mode::Read); } struct System::Impl { - explicit Impl(System& system) : kernel{system}, cpu_core_manager{system} {} + explicit Impl(System& system) : kernel{system}, cpu_core_manager{system}, reporter{system} {} Cpu& CurrentCpuCore() { return cpu_core_manager.GetCurrentCore(); @@ -271,7 +271,6 @@ struct System::Impl { /// Telemetry session for this emulation session std::unique_ptr telemetry_session; - std::map> modules; Reporter reporter; ResultStatus status = ResultStatus::Success; @@ -513,14 +512,6 @@ void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) { impl->content_provider->ClearSlot(slot); } -void System::RegisterNSOModule(std::string name, VAddr start_address) { - impl->modules.insert_or_assign(start_address, name); -} - -const std::map>& System::GetRegisteredNSOModules() const { - return impl->modules; -} - const Reporter& System::GetReporter() const { return impl->reporter; } diff --git a/src/core/core.h b/src/core/core.h index 440cdbbf0..e033057aa 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -287,10 +287,6 @@ public: void ClearContentProvider(FileSys::ContentProviderUnionSlot slot); - void RegisterNSOModule(std::string name, VAddr start_address); - - const std::map>& GetRegisteredNSOModules() const; - const Reporter& GetReporter() const; private: diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 2a945bc7b..d8ad0079a 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -37,17 +37,18 @@ AppletDataBroker::~AppletDataBroker() = default; AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() const { std::vector> out_normal; - std::vector> out_interactive; for (const auto& storage : in_channel) { out_normal.push_back(storage->GetData()); } + std::vector> out_interactive; + for (const auto& storage : in_interactive_channel) { out_interactive.push_back(storage->GetData()); } - return {out_normal, out_interactive}; + return {std::move(out_normal), std::move(out_interactive)}; } std::unique_ptr AppletDataBroker::PopNormalDataToGame() { diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h index bc919a8dd..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: class StubApplet final : public Applet { public: - StubApplet(AppletId id); + explicit StubApplet(AppletId id); ~StubApplet() override; void Initialize() override; diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 961b8882d..7e134f5c1 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -50,7 +50,7 @@ private: void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto user_id = rp.PopRaw(); - const auto unk1 = rp.PopRaw(); + const auto process_id = rp.PopRaw(); const auto data1 = ctx.ReadBuffer(0); const auto data2 = ctx.ReadBuffer(1); @@ -58,10 +58,10 @@ private: LOG_DEBUG( Service_PREPO, "called, user_id={:016X}{:016X}, unk1={:016X}, data1_size={:016X}, data2_size={:016X}", - user_id[1], user_id[0], unk1, data1.size(), data2.size()); + user_id[1], user_id[0], process_id, data1.size(), data2.size()); const auto& reporter{Core::System::GetInstance().GetReporter()}; - reporter.SavePlayReport(Core::CurrentProcess()->GetTitleID(), unk1, {data1, data2}, + reporter.SavePlayReport(Core::CurrentProcess()->GetTitleID(), process_id, {data1, data2}, user_id); IPC::ResponseBuilder rb{ctx, 2}; 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 const FileSys::PatchManager pm(metadata.GetTitleID()); // Load NSO modules + modules.clear(); const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); VAddr next_load_addr = base_address; for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", @@ -159,6 +160,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect } next_load_addr = *tentative_next_load_addr; + modules.insert_or_assign(load_addr, module); LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); // Register module with GDBStub GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); @@ -212,4 +214,13 @@ bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() const { return false; } +ResultStatus AppLoader_DeconstructedRomDirectory::ReadNSOModules(Modules& modules) { + if (!is_loaded) { + return ResultStatus::ErrorNotInitialized; + } + + modules = this->modules; + return ResultStatus::Success; +} + } // 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: ResultStatus ReadTitle(std::string& title) override; bool IsRomFSUpdatable() const override; + ResultStatus ReadNSOModules(Modules& modules) override; + private: FileSys::ProgramMetadata metadata; FileSys::VirtualFile romfs; @@ -54,6 +56,8 @@ private: std::string name; u64 title_id{}; bool override_update; + + Modules modules; }; } // namespace Loader diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index f7846db52..d6372c559 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -278,6 +278,12 @@ public: return ResultStatus::ErrorNotImplemented; } + using Modules = std::map; + + virtual ResultStatus ReadNSOModules(Modules& modules) { + return ResultStatus::ErrorNotImplemented; + } + protected: FileSys::VirtualFile file; 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& buffer) { return nca_loader->ReadLogo(buffer); } +ResultStatus AppLoader_NAX::ReadNSOModules(Modules& modules) { + return nca_loader->ReadNSOModules(modules); +} + } // 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: ResultStatus ReadBanner(std::vector& buffer) override; ResultStatus ReadLogo(std::vector& buffer) override; + ResultStatus ReadNSOModules(Modules& modules) override; + private: std::unique_ptr nax; std::unique_ptr 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& buffer) { buffer = logo->GetFile("NintendoLogo.png")->ReadAllBytes(); return ResultStatus::Success; } + +ResultStatus AppLoader_NCA::ReadNSOModules(Modules& modules) { + if (directory_loader == nullptr) { + return ResultStatus::ErrorNotInitialized; + } + + return directory_loader->ReadNSOModules(modules); +} + } // 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: ResultStatus ReadBanner(std::vector& buffer) override; ResultStatus ReadLogo(std::vector& buffer) override; + ResultStatus ReadNSOModules(Modules& modules) override; + private: std::unique_ptr nca; std::unique_ptr directory_loader; diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 7beeaaff3..e0d6ab473 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -164,9 +164,6 @@ std::optional AppLoader_NSO::LoadModule(Kernel::Process& process, // Register module with GDBStub GDBStub::RegisterModule(file.GetName(), load_base, load_base); - // Register module for ARMInterface with System - Core::System::GetInstance().RegisterNSOModule(file.GetName(), load_base); - return load_base + image_size; } @@ -175,11 +172,15 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) { return {ResultStatus::ErrorAlreadyLoaded, {}}; } + modules.clear(); + // Load module const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); if (!LoadModule(process, *file, base_address, true)) { return {ResultStatus::ErrorLoadingNSO, {}}; } + + modules.insert_or_assign(base_address, file->GetName()); LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); is_loaded = true; @@ -187,4 +188,9 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) { LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}}; } +ResultStatus AppLoader_NSO::ReadNSOModules(Modules& modules) { + modules = this->modules; + return ResultStatus::Success; +} + } // 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: std::optional pm = {}); LoadResult Load(Kernel::Process& process) override; + + ResultStatus ReadNSOModules(Modules& modules) override; + +private: + Modules modules; }; } // 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& buffer) { return secondary_loader->ReadLogo(buffer); } +ResultStatus AppLoader_NSP::ReadNSOModules(Modules& modules) { + return secondary_loader->ReadNSOModules(modules); +} + } // 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: ResultStatus ReadBanner(std::vector& buffer) override; ResultStatus ReadLogo(std::vector& buffer) override; + ResultStatus ReadNSOModules(Modules& modules) override; + private: std::unique_ptr nsp; std::unique_ptr 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& buffer) { return nca_loader->ReadLogo(buffer); } +ResultStatus AppLoader_XCI::ReadNSOModules(Modules& modules) { + return nca_loader->ReadNSOModules(modules); +} + } // 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: ResultStatus ReadBanner(std::vector& buffer) override; ResultStatus ReadLogo(std::vector& buffer) override; + ResultStatus ReadNSOModules(Modules& modules) override; + private: std::unique_ptr xci; std::unique_ptr nca_loader; diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 95dcfffb5..8fe621aa0 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -11,6 +11,7 @@ #include "core/core.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/process.h" +#include "core/hle/result.h" #include "core/reporter.h" #include "core/settings.h" #include "fmt/time.h" @@ -30,12 +31,12 @@ std::string GetTimestamp() { using namespace nlohmann; void SaveToFile(const json& json, const std::string& filename) { - FileUtil::CreateFullPath(filename); + if (!FileUtil::CreateFullPath(filename)) + LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename); + std::ofstream file( FileUtil::SanitizePath(filename, FileUtil::DirectorySeparator::PlatformDefault)); file << std::setw(4) << json << std::endl; - file.flush(); - file.close(); } json GetYuzuVersionData() { @@ -62,7 +63,7 @@ json GetReportCommonData(u64 title_id, ResultCode result, const std::string& tim }; if (user_id.has_value()) out["user_id"] = fmt::format("{:016X}{:016X}", (*user_id)[1], (*user_id)[0]); - return std::move(out); + return out; } json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64 sp, u64 pc, @@ -91,13 +92,13 @@ json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64 out["backtrace"] = std::move(backtrace_out); } - return std::move(out); + return out; } -json GetProcessorStateDataAuto() { - const auto* process{Core::CurrentProcess()}; +json GetProcessorStateDataAuto(Core::System& system) { + const auto* process{system.CurrentProcess()}; const auto& vm_manager{process->VMManager()}; - auto& arm{Core::CurrentArmInterface()}; + auto& arm{system.CurrentArmInterface()}; Core::ARM_Interface::ThreadContext context{}; arm.SaveContext(context); @@ -107,9 +108,9 @@ json GetProcessorStateDataAuto() { context.pstate, context.cpu_registers); } -json GetBacktraceData() { +json GetBacktraceData(Core::System& system) { auto out = json::array(); - const auto& backtrace{Core::CurrentArmInterface().GetBacktrace()}; + const auto& backtrace{system.CurrentArmInterface().GetBacktrace()}; for (const auto& entry : backtrace) { out.push_back({ {"module", entry.module}, @@ -120,18 +121,18 @@ json GetBacktraceData() { }); } - return std::move(out); + return out; } -json GetFullDataAuto(const std::string& timestamp, u64 title_id) { +json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& system) { json out; out["yuzu_version"] = GetYuzuVersionData(); out["report_common"] = GetReportCommonData(title_id, RESULT_SUCCESS, timestamp); - out["processor_state"] = GetProcessorStateDataAuto(); - out["backtrace"] = GetBacktraceData(); + out["processor_state"] = GetProcessorStateDataAuto(system); + out["backtrace"] = GetBacktraceData(system); - return std::move(out); + return out; } template @@ -152,7 +153,7 @@ json GetHLEBufferDescriptorData(const std::vector& buffer) { buffer_out.push_back(std::move(entry)); } - return std::move(buffer_out); + return buffer_out; } json GetHLERequestContextData(Kernel::HLERequestContext& ctx) { @@ -177,7 +178,7 @@ json GetHLERequestContextData(Kernel::HLERequestContext& ctx) { namespace Core { -Reporter::Reporter() = default; +Reporter::Reporter(Core::System& system) : system(system) {} Reporter::~Reporter() = default; @@ -189,7 +190,7 @@ void Reporter::SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u if (!IsReportingEnabled()) return; - const auto timestamp{GetTimestamp()}; + const auto timestamp = GetTimestamp(); json out; out["yuzu_version"] = GetYuzuVersionData(); @@ -214,9 +215,9 @@ void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 if (!IsReportingEnabled()) return; - const auto timestamp{GetTimestamp()}; - const auto title_id{Core::CurrentProcess()->GetTitleID()}; - auto out = GetFullDataAuto(timestamp, title_id); + const auto timestamp = GetTimestamp(); + const auto title_id = system.CurrentProcess()->GetTitleID(); + auto out = GetFullDataAuto(timestamp, title_id, system); auto break_out = json{ {"type", fmt::format("{:08X}", type)}, @@ -240,9 +241,9 @@ void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u if (!IsReportingEnabled()) return; - const auto timestamp{GetTimestamp()}; - const auto title_id{Core::CurrentProcess()->GetTitleID()}; - auto out = GetFullDataAuto(timestamp, title_id); + const auto timestamp = GetTimestamp(); + const auto title_id = system.CurrentProcess()->GetTitleID(); + auto out = GetFullDataAuto(timestamp, title_id, system); auto function_out = GetHLERequestContextData(ctx); function_out["command_id"] = command_id; @@ -261,9 +262,9 @@ void Reporter::SaveUnimplementedAppletReport( if (!IsReportingEnabled()) return; - const auto timestamp{GetTimestamp()}; - const auto title_id{Core::CurrentProcess()->GetTitleID()}; - auto out = GetFullDataAuto(timestamp, title_id); + const auto timestamp = GetTimestamp(); + const auto title_id = system.CurrentProcess()->GetTitleID(); + auto out = GetFullDataAuto(timestamp, title_id, system); out["applet_common_args"] = { {"applet_id", fmt::format("{:02X}", applet_id)}, @@ -290,12 +291,12 @@ void Reporter::SaveUnimplementedAppletReport( SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp)); } -void Reporter::SavePlayReport(u64 title_id, u64 unk1, std::vector> data, +void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector> data, std::optional user_id) const { if (!IsReportingEnabled()) return; - const auto timestamp{GetTimestamp()}; + const auto timestamp = GetTimestamp(); json out; out["yuzu_version"] = GetYuzuVersionData(); @@ -306,7 +307,7 @@ void Reporter::SavePlayReport(u64 title_id, u64 unk1, std::vectorGetTitleID()}; + const auto timestamp = GetTimestamp(); + const auto title_id = system.CurrentProcess()->GetTitleID(); - SaveToFile(GetFullDataAuto(timestamp, title_id), GetPath("user_report", title_id, timestamp)); + SaveToFile(GetFullDataAuto(timestamp, title_id, system), + GetPath("user_report", title_id, timestamp)); } bool Reporter::IsReportingEnabled() const { diff --git a/src/core/reporter.h b/src/core/reporter.h index 2f798ee16..3de19c0f7 100644 --- a/src/core/reporter.h +++ b/src/core/reporter.h @@ -7,7 +7,8 @@ #include #include #include "common/common_types.h" -#include "core/hle/result.h" + +union ResultCode; namespace Kernel { class HLERequestContext; @@ -17,7 +18,7 @@ namespace Core { class Reporter { public: - Reporter(); + explicit Reporter(Core::System& system); ~Reporter(); void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp, @@ -37,7 +38,7 @@ public: std::vector> normal_channel, std::vector> interactive_channel) const; - void SavePlayReport(u64 title_id, u64 unk1, std::vector> data, + void SavePlayReport(u64 title_id, u64 process_id, std::vector> data, std::optional user_id = {}) const; void SaveErrorReport(u64 title_id, ResultCode result, @@ -48,6 +49,8 @@ public: private: bool IsReportingEnabled() const; + + Core::System& system; }; } // namespace Core diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index ed14474aa..ed0eafe4f 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -465,16 +465,18 @@ void Config::ReadDataStorageValues() { } void Config::ReadDebuggingValues() { - qt_config->beginGroup(QStringLiteral("Debugging")); + qt_config->beginGroup(QStringLiteral("Debugging")); - Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); - Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); - Settings::values.program_args = - ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString(); - Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); - Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); - Settings::values.reporting_services = ReadSetting("reporting_services", false).toBool(); - qt_config->endGroup(); + Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); + Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); + Settings::values.program_args = + ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString(); + Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); + Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); + Settings::values.reporting_services = + ReadSetting(QStringLiteral("reporting_services"), false).toBool(); + + qt_config->endGroup(); } void Config::ReadDisabledAddOnValues() { @@ -691,7 +693,7 @@ void Config::ReadValues() { ReadDataStorageValues(); ReadSystemValues(); ReadMiscellaneousValues(); - ReadDebugValues(); + ReadDebuggingValues(); ReadWebServiceValues(); ReadDisabledAddOnValues(); ReadUIValues();