diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 75cb11161..1916303bc 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -25,6 +25,7 @@ #include "Common/FileStream.h" #include "GamePatch.h" #include "HW/Espresso/Debugger/GDBStub.h" +#include "Common/ExceptionHandler/ExceptionHandler.h" #include "Cafe/IOSU/legacy/iosu_ioctl.h" #include "Cafe/IOSU/legacy/iosu_act.h" @@ -84,6 +85,18 @@ bool isLaunchTypeELF = false; MPTR _entryPoint = MPTR_NULL; +std::thread crashErrorThread; +std::atomic crashErrorThreadInterpreter = nullptr; +std::atomic crashErrorThreadRunning = false; + +void CrashErrorThreadLoop() +{ + while (!crashErrorThreadRunning) + std::this_thread::yield(); + + ExceptionHandler_DisplayErrorInfo(crashErrorThreadInterpreter); +} + uint32 generateHashFromRawRPXData(uint8* rpxData, sint32 size) { uint32 h = 0x3416DCBF; @@ -154,7 +167,7 @@ void LoadMainExecutable() } currentUpdatedApplicationHash = generateHashFromRawRPXData(rpxData, rpxSize); // determine if this file is an ELF - const uint8 elfHeaderMagic[9] = { 0x7F,0x45,0x4C,0x46,0x01,0x02,0x01,0x00,0x00 }; + const uint8 elfHeaderMagic[9] = {0x7F, 0x45, 0x4C, 0x46, 0x01, 0x02, 0x01, 0x00, 0x00}; if (rpxSize >= 10 && memcmp(rpxData, elfHeaderMagic, sizeof(elfHeaderMagic)) == 0) { // ELF @@ -206,7 +219,7 @@ void InfoLog_TitleLoaded() cemuLog_log(LogType::Force, "TitleId: {:08x}-{:08x}", (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF)); cemuLog_log(LogType::Force, "TitleVersion: v{}", CafeSystem::GetForegroundTitleVersion()); CafeConsoleRegion region = CafeSystem::GetForegroundTitleRegion(); - if(region == CafeConsoleRegion::JPN) + if (region == CafeConsoleRegion::JPN) cemuLog_log(LogType::Force, "TitleRegion: JP"); else if (region == CafeConsoleRegion::EUR) cemuLog_log(LogType::Force, "TitleRegion: EU"); @@ -222,7 +235,7 @@ void InfoLog_TitleLoaded() cemuLog_log(LogType::Force, "Shader cache file: shaderCache/transferable/{:016x}.bin", titleId); // game profile info std::string gameProfilePath; - if(g_current_game_profile->IsDefaultProfile()) + if (g_current_game_profile->IsDefaultProfile()) gameProfilePath = fmt::format("gameProfiles/default/{:016x}.ini", titleId); else gameProfilePath = fmt::format("gameProfiles/{:016x}.ini", titleId); @@ -248,7 +261,7 @@ void InfoLog_PrintActiveSettings() if (ActiveSettings::GetGraphicsAPI() == GraphicAPI::kVulkan) { cemuLog_log(LogType::Force, "Async compile: {}", GetConfig().async_compile.GetValue() ? "true" : "false"); - if(!GetConfig().vk_accurate_barriers.GetValue()) + if (!GetConfig().vk_accurate_barriers.GetValue()) cemuLog_log(LogType::Force, "Accurate barriers are disabled!"); } cemuLog_log(LogType::Force, "Console language: {}", stdx::to_underlying(config.console_language.GetValue())); @@ -272,13 +285,12 @@ struct const char* fileName; const char* resourcePath; const char* mlcPath; -}shareddataDef[] = -{ - 0xFFCAFE01, 2, "CafeCn.ttf", "resources/sharedFonts/CafeCn.ttf", "sys/title/0005001b/10042400/content/CafeCn.ttf", - 0xFFCAFE02, 2, "CafeKr.ttf", "resources/sharedFonts/CafeKr.ttf", "sys/title/0005001b/10042400/content/CafeKr.ttf", - 0xFFCAFE03, 2, "CafeStd.ttf", "resources/sharedFonts/CafeStd.ttf", "sys/title/0005001b/10042400/content/CafeStd.ttf", - 0xFFCAFE04, 2, "CafeTw.ttf", "resources/sharedFonts/CafeTw.ttf", "sys/title/0005001b/10042400/content/CafeTw.ttf" -}; +} shareddataDef[] = + { + 0xFFCAFE01, 2, "CafeCn.ttf", "resources/sharedFonts/CafeCn.ttf", "sys/title/0005001b/10042400/content/CafeCn.ttf", + 0xFFCAFE02, 2, "CafeKr.ttf", "resources/sharedFonts/CafeKr.ttf", "sys/title/0005001b/10042400/content/CafeKr.ttf", + 0xFFCAFE03, 2, "CafeStd.ttf", "resources/sharedFonts/CafeStd.ttf", "sys/title/0005001b/10042400/content/CafeStd.ttf", + 0xFFCAFE04, 2, "CafeTw.ttf", "resources/sharedFonts/CafeTw.ttf", "sys/title/0005001b/10042400/content/CafeTw.ttf"}; static_assert(sizeof(SharedDataEntry) == 0x1C); @@ -397,11 +409,11 @@ void cemu_initForGame() InfoLog_PrintActiveSettings(); Latte_Start(); // check for debugger entrypoint bp - if (g_gdbstub) - { - g_gdbstub->HandleEntryStop(_entryPoint); - g_gdbstub->Initialize(); - } + if (g_gdbstub) + { + g_gdbstub->HandleEntryStop(_entryPoint); + g_gdbstub->Initialize(); + } debugger_handleEntryBreakpoint(_entryPoint); // load graphic packs cemuLog_log(LogType::Force, "------- Activate graphic packs -------"); @@ -412,7 +424,8 @@ void cemu_initForGame() // everything initialized cemuLog_log(LogType::Force, "------- Run title -------"); // wait till GPU thread is initialized - while (g_isGPUInitFinished == false) std::this_thread::sleep_for(std::chrono::milliseconds(50)); + while (g_isGPUInitFinished == false) + std::this_thread::sleep_for(std::chrono::milliseconds(50)); // init initial thread OSThread_t* initialThread = coreinit::OSGetDefaultThread(1); coreinit::OSSetThreadPriority(initialThread, 16); @@ -425,11 +438,11 @@ namespace CafeSystem { void InitVirtualMlcStorage(); void MlcStorageMountTitle(TitleInfo& titleInfo); - void MlcStorageUnmountAllTitles(); + void MlcStorageUnmountAllTitles(); - static bool s_initialized = false; + static bool s_initialized = false; static SystemImplementation* s_implementation{nullptr}; - bool sLaunchModeIsStandalone = false; + bool sLaunchModeIsStandalone = false; std::optional> s_overrideArgs; bool sSystemRunning = false; @@ -437,10 +450,9 @@ namespace CafeSystem GameInfo2 sGameInfo_ForegroundTitle; - static void _CheckForWine() { - #if BOOST_OS_WINDOWS +#if BOOST_OS_WINDOWS const HMODULE hmodule = GetModuleHandleA("ntdll.dll"); if (!hmodule) return; @@ -450,7 +462,7 @@ namespace CafeSystem { cemuLog_log(LogType::Force, "Wine version: {}", pwine_get_version()); } - #endif +#endif } void logCPUAndMemoryInfo() @@ -458,26 +470,28 @@ namespace CafeSystem std::string cpuName = g_CPUFeatures.GetCPUName(); if (!cpuName.empty()) cemuLog_log(LogType::Force, "CPU: {}", cpuName); - #if BOOST_OS_WINDOWS +#if BOOST_OS_WINDOWS MEMORYSTATUSEX statex; statex.dwLength = sizeof(statex); GlobalMemoryStatusEx(&statex); uint32 memoryInMB = (uint32)(statex.ullTotalPhys / 1024LL / 1024LL); cemuLog_log(LogType::Force, "RAM: {}MB", memoryInMB); - #elif BOOST_OS_LINUX - struct sysinfo info {}; +#elif BOOST_OS_LINUX + struct sysinfo info + { + }; sysinfo(&info); cemuLog_log(LogType::Force, "RAM: {}MB", ((static_cast(info.totalram) * info.mem_unit) / 1024LL / 1024LL)); - #elif BOOST_OS_MACOS +#elif BOOST_OS_MACOS int64_t totalRam; size_t size = sizeof(totalRam); int result = sysctlbyname("hw.memsize", &totalRam, &size, NULL, 0); if (result == 0) cemuLog_log(LogType::Force, "RAM: {}MB", (totalRam / 1024LL / 1024LL)); - #endif +#endif } - #if BOOST_OS_WINDOWS +#if BOOST_OS_WINDOWS std::string GetWindowsNamedVersion(uint32& buildNumber) { char productName[256]; @@ -497,41 +511,41 @@ namespace CafeSystem buildNumber = osvi.dwBuildNumber; return std::string(productName); } - #endif +#endif void logPlatformInfo() { std::string buffer; const char* platform = NULL; - #if BOOST_OS_WINDOWS +#if BOOST_OS_WINDOWS uint32 buildNumber; std::string windowsVersionName = GetWindowsNamedVersion(buildNumber); buffer = fmt::format("{} (Build {})", windowsVersionName, buildNumber); platform = buffer.c_str(); - #elif BOOST_OS_LINUX - if (getenv ("APPIMAGE")) +#elif BOOST_OS_LINUX + if (getenv("APPIMAGE")) platform = "Linux (AppImage)"; - else if (getenv ("SNAP")) + else if (getenv("SNAP")) platform = "Linux (Snap)"; - else if (platform = getenv ("container")) + else if (platform = getenv("container")) { - if (strcmp (platform, "flatpak") == 0) + if (strcmp(platform, "flatpak") == 0) platform = "Linux (Flatpak)"; } else platform = "Linux"; - #elif BOOST_OS_MACOS +#elif BOOST_OS_MACOS platform = "MacOS"; - #endif +#endif cemuLog_log(LogType::Force, "Platform: {}", platform); } static std::vector s_iosuModules = - { - // entries in this list are ordered by initialization order. Shutdown in reverse order - iosu::kernel::GetModule(), - iosu::fpd::GetModule(), - iosu::pdm::GetModule(), + { + // entries in this list are ordered by initialization order. Shutdown in reverse order + iosu::kernel::GetModule(), + iosu::fpd::GetModule(), + iosu::pdm::GetModule(), }; // initialize all subsystems which are persistent and don't depend on a game running @@ -559,7 +573,7 @@ namespace CafeSystem // must happen before COS module init, but also before iosu::kernel::Initialize() SysAllocatorContainer::GetInstance().Initialize(); // init IOSU modules - for(auto& module : s_iosuModules) + for (auto& module : s_iosuModules) module->SystemLaunch(); // init IOSU (deprecated manual init) iosuCrypto_init(); @@ -588,6 +602,9 @@ namespace CafeSystem mic::Initialize(); // init hardware register interfaces HW_SI::Initialize(); + + crashErrorThread = std::thread(CrashErrorThreadLoop); + crashErrorThread.detach(); } void SetImplementation(SystemImplementation* impl) @@ -595,44 +612,44 @@ namespace CafeSystem s_implementation = impl; } - void Shutdown() - { - cemu_assert_debug(s_initialized); - // if a title is running, shut it down - if (sSystemRunning) - ShutdownTitle(); - // shutdown persistent subsystems (deprecated manual shutdown) + void Shutdown() + { + cemu_assert_debug(s_initialized); + // if a title is running, shut it down + if (sSystemRunning) + ShutdownTitle(); + // shutdown persistent subsystems (deprecated manual shutdown) iosu::odm::Shutdown(); iosu::act::Stop(); - iosu::mcp::Shutdown(); - iosu::fsa::Shutdown(); + iosu::mcp::Shutdown(); + iosu::fsa::Shutdown(); // shutdown IOSU modules - for(auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it) + for (auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it) (*it)->SystemExit(); - s_initialized = false; - } + s_initialized = false; + } std::string GetInternalVirtualCodeFolder() { return "/internal/current_title/code/"; } - void MountBaseDirectories() - { - const auto mlc = ActiveSettings::GetMlcPath(); - FSCDeviceHostFS_Mount("/cemuBossStorage/", _pathToUtf8(mlc / "usr/boss/"), FSC_PRIORITY_BASE); - FSCDeviceHostFS_Mount("/vol/storage_mlc01/", _pathToUtf8(mlc / ""), FSC_PRIORITY_BASE); - } + void MountBaseDirectories() + { + const auto mlc = ActiveSettings::GetMlcPath(); + FSCDeviceHostFS_Mount("/cemuBossStorage/", _pathToUtf8(mlc / "usr/boss/"), FSC_PRIORITY_BASE); + FSCDeviceHostFS_Mount("/vol/storage_mlc01/", _pathToUtf8(mlc / ""), FSC_PRIORITY_BASE); + } - void UnmountBaseDirectories() - { - fsc_unmount("/vol/storage_mlc01/", FSC_PRIORITY_BASE); - fsc_unmount("/cemuBossStorage/", FSC_PRIORITY_BASE); - } + void UnmountBaseDirectories() + { + fsc_unmount("/vol/storage_mlc01/", FSC_PRIORITY_BASE); + fsc_unmount("/cemuBossStorage/", FSC_PRIORITY_BASE); + } STATUS_CODE LoadAndMountForegroundTitle(TitleId titleId) { - cemuLog_log(LogType::Force, "Mounting title {:016x}", (uint64)titleId); + cemuLog_log(LogType::Force, "Mounting title {:016x}", (uint64)titleId); sGameInfo_ForegroundTitle = CafeTitleList::GetGameInfo(titleId); if (!sGameInfo_ForegroundTitle.IsValid()) { @@ -643,7 +660,7 @@ namespace CafeSystem TitleInfo& titleBase = sGameInfo_ForegroundTitle.GetBase(); if (!titleBase.IsValid()) return STATUS_CODE::UNABLE_TO_MOUNT; - if(!titleBase.ParseXmlInfo()) + if (!titleBase.ParseXmlInfo()) return STATUS_CODE::UNABLE_TO_MOUNT; cemuLog_log(LogType::Force, "Base: {}", titleBase.GetPrintPath()); // mount base @@ -691,30 +708,30 @@ namespace CafeSystem return STATUS_CODE::SUCCESS; } - void UnmountForegroundTitle() - { - if(sLaunchModeIsStandalone) - return; - cemu_assert_debug(sGameInfo_ForegroundTitle.IsValid()); // unmounting title which was never mounted? - if (!sGameInfo_ForegroundTitle.IsValid()) - return; - sGameInfo_ForegroundTitle.GetBase().Unmount("/vol/content"); - sGameInfo_ForegroundTitle.GetBase().Unmount(GetInternalVirtualCodeFolder()); - if (sGameInfo_ForegroundTitle.HasUpdate()) - { - if(auto& update = sGameInfo_ForegroundTitle.GetUpdate(); update.IsValid()) - { - update.Unmount("/vol/content"); - update.Unmount(GetInternalVirtualCodeFolder()); - } - } - auto aocList = sGameInfo_ForegroundTitle.GetAOC(); - if (!aocList.empty()) - { - TitleInfo& titleAOC = aocList[0]; - titleAOC.Unmount(fmt::format("/vol/aoc{:016x}", titleAOC.GetAppTitleId())); - } - } + void UnmountForegroundTitle() + { + if (sLaunchModeIsStandalone) + return; + cemu_assert_debug(sGameInfo_ForegroundTitle.IsValid()); // unmounting title which was never mounted? + if (!sGameInfo_ForegroundTitle.IsValid()) + return; + sGameInfo_ForegroundTitle.GetBase().Unmount("/vol/content"); + sGameInfo_ForegroundTitle.GetBase().Unmount(GetInternalVirtualCodeFolder()); + if (sGameInfo_ForegroundTitle.HasUpdate()) + { + if (auto& update = sGameInfo_ForegroundTitle.GetUpdate(); update.IsValid()) + { + update.Unmount("/vol/content"); + update.Unmount(GetInternalVirtualCodeFolder()); + } + } + auto aocList = sGameInfo_ForegroundTitle.GetAOC(); + if (!aocList.empty()) + { + TitleInfo& titleAOC = aocList[0]; + titleAOC.Unmount(fmt::format("/vol/aoc{:016x}", titleAOC.GetAppTitleId())); + } + } STATUS_CODE SetupExecutable() { @@ -751,35 +768,35 @@ namespace CafeSystem return STATUS_CODE::SUCCESS; } - void SetupMemorySpace() - { - memory_mapForCurrentTitle(); - LoadSharedData(); - } + void SetupMemorySpace() + { + memory_mapForCurrentTitle(); + LoadSharedData(); + } - void DestroyMemorySpace() - { - memory_unmapForCurrentTitle(); - } + void DestroyMemorySpace() + { + memory_unmapForCurrentTitle(); + } STATUS_CODE PrepareForegroundTitle(TitleId titleId) { CafeTitleList::WaitForMandatoryScan(); sLaunchModeIsStandalone = false; - _pathToExecutable.clear(); + _pathToExecutable.clear(); TitleIdParser tip(titleId); if (tip.GetType() == TitleIdParser::TITLE_TYPE::AOC || tip.GetType() == TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE) cemuLog_log(LogType::Force, "Launched titleId is not the base of a title"); - // mount mlc storage - MountBaseDirectories(); - // mount title folders + // mount mlc storage + MountBaseDirectories(); + // mount title folders STATUS_CODE r = LoadAndMountForegroundTitle(titleId); if (r != STATUS_CODE::SUCCESS) return r; gameProfile_load(); // setup memory space and PPC recompiler - SetupMemorySpace(); - PPCRecompiler_init(); + SetupMemorySpace(); + PPCRecompiler_init(); r = SetupExecutable(); // load RPX if (r != STATUS_CODE::SUCCESS) return r; @@ -822,17 +839,17 @@ namespace CafeSystem sForegroundTitleId = 0xFFFFFFFF00000000ULL | (uint64)h; cemuLog_log(LogType::Force, "Generated placeholder TitleId: {:016x}", sForegroundTitleId); // setup memory space and ppc recompiler - SetupMemorySpace(); - PPCRecompiler_init(); - // load executable - SetupExecutable(); + SetupMemorySpace(); + PPCRecompiler_init(); + // load executable + SetupExecutable(); InitVirtualMlcStorage(); return STATUS_CODE::SUCCESS; } void _LaunchTitleThread() { - for(auto& module : s_iosuModules) + for (auto& module : s_iosuModules) module->TitleStart(); cemu_initForGame(); // enter scheduler @@ -890,9 +907,9 @@ namespace CafeSystem return "Unknown Game"; std::string applicationName; applicationName = sGameInfo_ForegroundTitle.GetBase().GetMetaInfo()->GetShortName(GetConfig().console_language); - if (applicationName.empty()) //Try to get the English Title + if (applicationName.empty()) // Try to get the English Title applicationName = sGameInfo_ForegroundTitle.GetBase().GetMetaInfo()->GetShortName(CafeConsoleLanguage::EN); - if (applicationName.empty()) //Unknown Game + if (applicationName.empty()) // Unknown Game applicationName = "Unknown Game"; return applicationName; } @@ -918,7 +935,7 @@ namespace CafeSystem bool GetOverrideArgStr(std::vector& args) { args.clear(); - if(!s_overrideArgs) + if (!s_overrideArgs) return false; args = *s_overrideArgs; return true; @@ -950,32 +967,32 @@ namespace CafeSystem void UnmountCurrentTitle() { - UnmountForegroundTitle(); - fsc_unmount("/internal/code/", FSC_PRIORITY_BASE); + UnmountForegroundTitle(); + fsc_unmount("/internal/code/", FSC_PRIORITY_BASE); } void ShutdownTitle() { - if(!sSystemRunning) + if (!sSystemRunning) return; - coreinit::OSSchedulerEnd(); - Latte_Stop(); - // reset Cafe OS userspace modules - snd_core::reset(); - coreinit::OSAlarm_Shutdown(); - GX2::_GX2DriverReset(); - nn::save::ResetToDefaultState(); - coreinit::__OSDeleteAllActivePPCThreads(); - RPLLoader_ResetState(); - for(auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it) + coreinit::OSSchedulerEnd(); + Latte_Stop(); + // reset Cafe OS userspace modules + snd_core::reset(); + coreinit::OSAlarm_Shutdown(); + GX2::_GX2DriverReset(); + nn::save::ResetToDefaultState(); + coreinit::__OSDeleteAllActivePPCThreads(); + RPLLoader_ResetState(); + for (auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it) (*it)->TitleStop(); - // reset Cemu subsystems - PPCRecompiler_Shutdown(); - GraphicPack2::Reset(); - UnmountCurrentTitle(); - MlcStorageUnmountAllTitles(); - UnmountBaseDirectories(); - DestroyMemorySpace(); + // reset Cemu subsystems + PPCRecompiler_Shutdown(); + GraphicPack2::Reset(); + UnmountCurrentTitle(); + MlcStorageUnmountAllTitles(); + UnmountBaseDirectories(); + DestroyMemorySpace(); sSystemRunning = false; } @@ -985,7 +1002,7 @@ namespace CafeSystem { // starting with Cemu 1.27.0 /vol/storage_mlc01/ is virtualized, meaning that it doesn't point to one singular host os folder anymore // instead it now uses a more complex solution to source titles with various formats (folder, wud, wua) from the game paths and host mlc path - + // todo - mount /vol/storage_mlc01/ with base priority to the host mlc? // since mounting titles is an expensive operation we have to avoid mounting all titles at once @@ -996,7 +1013,7 @@ namespace CafeSystem MlcStorageMountTitle(sGameInfo_ForegroundTitle.GetBase()); if (sGameInfo_ForegroundTitle.GetUpdate().IsValid()) MlcStorageMountTitle(sGameInfo_ForegroundTitle.GetUpdate()); - for(auto& it : sGameInfo_ForegroundTitle.GetAOC()) + for (auto& it : sGameInfo_ForegroundTitle.GetAOC()) MlcStorageMountTitle(it); // setup system for lazy-mounting of other known titles @@ -1052,15 +1069,15 @@ namespace CafeSystem MlcStorageMountTitle(it); } - void MlcStorageUnmountAllTitles() - { - for(auto& it : m_mlcMountedTitles) - { - std::string mlcStoragePath = GetMlcStoragePath(it.first); - it.second->Unmount(mlcStoragePath); - } - m_mlcMountedTitles.clear(); - } + void MlcStorageUnmountAllTitles() + { + for (auto& it : m_mlcMountedTitles) + { + std::string mlcStoragePath = GetMlcStoragePath(it.first); + it.second->Unmount(mlcStoragePath); + } + m_mlcMountedTitles.clear(); + } uint32 GetRPXHashBase() { @@ -1077,4 +1094,12 @@ namespace CafeSystem s_implementation->CafeRecreateCanvas(); } -} + void StartCrashErrorThread(PPCInterpreter_t* interpreter) + { + if (!crashErrorThreadRunning) + { + crashErrorThreadInterpreter = interpreter; + crashErrorThreadRunning = true; + } + } +} // namespace CafeSystem diff --git a/src/Cafe/CafeSystem.h b/src/Cafe/CafeSystem.h index 336c2f406..d442213f4 100644 --- a/src/Cafe/CafeSystem.h +++ b/src/Cafe/CafeSystem.h @@ -6,9 +6,8 @@ namespace CafeSystem { - class SystemImplementation - { - public: + class SystemImplementation { + public: virtual void CafeRecreateCanvas() = 0; }; @@ -17,12 +16,12 @@ namespace CafeSystem SUCCESS, INVALID_RPX, UNABLE_TO_MOUNT, // failed to mount through TitleInfo (most likely caused by an invalid or outdated path) - //BAD_META_DATA, - the title list only stores titles with valid meta, so this error code is impossible + // BAD_META_DATA, - the title list only stores titles with valid meta, so this error code is impossible }; void Initialize(); void SetImplementation(SystemImplementation* impl); - void Shutdown(); + void Shutdown(); STATUS_CODE PrepareForegroundTitle(TitleId titleId); STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path); @@ -53,7 +52,10 @@ namespace CafeSystem uint32 GetRPXHashUpdated(); void RequestRecreateCanvas(); -}; + + void StartCrashErrorThread(PPCInterpreter_t* interpreter); + +}; // namespace CafeSystem extern RPLModule* applicationRPX; diff --git a/src/Common/ExceptionHandler/ExceptionHandler.cpp b/src/Common/ExceptionHandler/ExceptionHandler.cpp index 5fefc8caa..bb75f9bfd 100644 --- a/src/Common/ExceptionHandler/ExceptionHandler.cpp +++ b/src/Common/ExceptionHandler/ExceptionHandler.cpp @@ -5,6 +5,13 @@ #include "Cafe/HW/Espresso/PPCState.h" #include "Cafe/HW/Espresso/Debugger/GDBStub.h" #include "ExceptionHandler.h" +#include "gui/MainWindow.h" + +#include + +#if BOOST_OS_WINDOWS +#include +#endif void DebugLogStackTrace(OSThread_t* thread, MPTR sp); @@ -12,12 +19,12 @@ bool crashLogCreated = false; bool CrashLog_Create() { - if (crashLogCreated) - return false; // only create one crashlog - crashLogCreated = true; - cemuLog_createLogFile(true); - CrashLog_SetOutputChannels(true, true); - return true; + if (crashLogCreated) + return false; // only create one crashlog + crashLogCreated = true; + cemuLog_createLogFile(true); + CrashLog_SetOutputChannels(true, true); + return true; } static bool s_writeToStdErr{true}; @@ -25,138 +32,248 @@ static bool s_writeToLogTxt{true}; void CrashLog_SetOutputChannels(bool writeToStdErr, bool writeToLogTxt) { - s_writeToStdErr = writeToStdErr; - s_writeToLogTxt = writeToLogTxt; + s_writeToStdErr = writeToStdErr; + s_writeToLogTxt = writeToLogTxt; } // outputs to both stderr and log.txt void CrashLog_WriteLine(std::string_view text, bool newLine) { - if(s_writeToLogTxt) - cemuLog_writeLineToLog(text, false, newLine); - if(s_writeToStdErr) - { - fwrite(text.data(), sizeof(char), text.size(), stderr); - if(newLine) - fwrite("\n", sizeof(char), 1, stderr); - } + if (s_writeToLogTxt) + cemuLog_writeLineToLog(text, false, newLine); + if (s_writeToStdErr) + { + fwrite(text.data(), sizeof(char), text.size(), stderr); + if (newLine) + fwrite("\n", sizeof(char), 1, stderr); + } } void CrashLog_WriteHeader(const char* header) { - CrashLog_WriteLine("-----------------------------------------"); - CrashLog_WriteLine(" ", false); - CrashLog_WriteLine(header); - CrashLog_WriteLine("-----------------------------------------"); + CrashLog_WriteLine("-----------------------------------------"); + CrashLog_WriteLine(" ", false); + CrashLog_WriteLine(header); + CrashLog_WriteLine("-----------------------------------------"); } void ExceptionHandler_LogGeneralInfo() { - char dumpLine[1024]; - // info about game - CrashLog_WriteLine(""); - CrashLog_WriteHeader("Game info"); - if (CafeSystem::IsTitleRunning()) - { - CrashLog_WriteLine("Game: ", false); - CrashLog_WriteLine(CafeSystem::GetForegroundTitleName()); - // title id - CrashLog_WriteLine(fmt::format("TitleId: {:016x}", CafeSystem::GetForegroundTitleId())); - // rpx hash - sprintf(dumpLine, "RPXHash: %08x (Update: %08x)", CafeSystem::GetRPXHashBase(), CafeSystem::GetRPXHashUpdated()); - CrashLog_WriteLine(dumpLine); - } - else - { - CrashLog_WriteLine("Not running"); - } - // info about active PPC instance: - CrashLog_WriteLine(""); - CrashLog_WriteHeader("Active PPC instance"); + char dumpLine[1024]; + // info about game + CrashLog_WriteLine(""); + CrashLog_WriteHeader("Game info"); + if (CafeSystem::IsTitleRunning()) + { + CrashLog_WriteLine("Game: ", false); + CrashLog_WriteLine(CafeSystem::GetForegroundTitleName()); + // title id + CrashLog_WriteLine(fmt::format("TitleId: {:016x}", CafeSystem::GetForegroundTitleId())); + // rpx hash + sprintf(dumpLine, "RPXHash: %08x (Update: %08x)", CafeSystem::GetRPXHashBase(), CafeSystem::GetRPXHashUpdated()); + CrashLog_WriteLine(dumpLine); + } + else + { + CrashLog_WriteLine("Not running"); + } + // info about active PPC instance: + CrashLog_WriteLine(""); + CrashLog_WriteHeader("Active PPC instance"); PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); - if (hCPU) - { - OSThread_t* currentThread = coreinit::OSGetCurrentThread(); - uint32 threadPtr = memory_getVirtualOffsetFromPointer(coreinit::OSGetCurrentThread()); - sprintf(dumpLine, "IP 0x%08x LR 0x%08x Thread 0x%08x", hCPU->instructionPointer, hCPU->spr.LR, threadPtr); - CrashLog_WriteLine(dumpLine); - - // GPR info - CrashLog_WriteLine(""); - auto gprs = hCPU->gpr; - sprintf(dumpLine, "r0 =%08x r1 =%08x r2 =%08x r3 =%08x r4 =%08x r5 =%08x r6 =%08x r7 =%08x", gprs[0], gprs[1], gprs[2], gprs[3], gprs[4], gprs[5], gprs[6], gprs[7]); - CrashLog_WriteLine(dumpLine); - sprintf(dumpLine, "r8 =%08x r9 =%08x r10=%08x r11=%08x r12=%08x r13=%08x r14=%08x r15=%08x", gprs[8], gprs[9], gprs[10], gprs[11], gprs[12], gprs[13], gprs[14], gprs[15]); - CrashLog_WriteLine(dumpLine); - sprintf(dumpLine, "r16=%08x r17=%08x r18=%08x r19=%08x r20=%08x r21=%08x r22=%08x r23=%08x", gprs[16], gprs[17], gprs[18], gprs[19], gprs[20], gprs[21], gprs[22], gprs[23]); - CrashLog_WriteLine(dumpLine); - sprintf(dumpLine, "r24=%08x r25=%08x r26=%08x r27=%08x r28=%08x r29=%08x r30=%08x r31=%08x", gprs[24], gprs[25], gprs[26], gprs[27], gprs[28], gprs[29], gprs[30], gprs[31]); - CrashLog_WriteLine(dumpLine); - - // stack trace - MPTR currentStackVAddr = hCPU->gpr[1]; - CrashLog_WriteLine(""); - CrashLog_WriteHeader("PPC stack trace"); - DebugLogStackTrace(currentThread, currentStackVAddr); - - // stack dump - CrashLog_WriteLine(""); - CrashLog_WriteHeader("PPC stack dump"); - for (uint32 i = 0; i < 16; i++) - { - MPTR lineAddr = currentStackVAddr + i * 8 * 4; - if (memory_isAddressRangeAccessible(lineAddr, 8 * 4)) - { - sprintf(dumpLine, "[0x%08x] %08x %08x %08x %08x - %08x %08x %08x %08x", lineAddr, memory_readU32(lineAddr + 0), memory_readU32(lineAddr + 4), memory_readU32(lineAddr + 8), memory_readU32(lineAddr + 12), memory_readU32(lineAddr + 16), memory_readU32(lineAddr + 20), memory_readU32(lineAddr + 24), memory_readU32(lineAddr + 28)); - CrashLog_WriteLine(dumpLine); - } - else - { - sprintf(dumpLine, "[0x%08x] ?", lineAddr); - CrashLog_WriteLine(dumpLine); - } - } - } - else - { - CrashLog_WriteLine("Not active"); - } - - // PPC thread log - CrashLog_WriteLine(""); - CrashLog_WriteHeader("PPC threads"); - if (activeThreadCount == 0) - { - CrashLog_WriteLine("None active"); - } - for (sint32 i = 0; i < activeThreadCount; i++) - { - MPTR threadItrMPTR = activeThread[i]; - OSThread_t* threadItrBE = (OSThread_t*)memory_getPointerFromVirtualOffset(threadItrMPTR); - - // get thread state - OSThread_t::THREAD_STATE threadState = threadItrBE->state; - const char* threadStateStr = "UNDEFINED"; - if (threadItrBE->suspendCounter != 0) - threadStateStr = "SUSPENDED"; - else if (threadState == OSThread_t::THREAD_STATE::STATE_NONE) - threadStateStr = "NONE"; - else if (threadState == OSThread_t::THREAD_STATE::STATE_READY) - threadStateStr = "READY"; - else if (threadState == OSThread_t::THREAD_STATE::STATE_RUNNING) - threadStateStr = "RUNNING"; - else if (threadState == OSThread_t::THREAD_STATE::STATE_WAITING) - threadStateStr = "WAITING"; - else if (threadState == OSThread_t::THREAD_STATE::STATE_MORIBUND) - threadStateStr = "MORIBUND"; - // generate log line - uint8 affinity = threadItrBE->attr; - sint32 effectivePriority = threadItrBE->effectivePriority; - const char* threadName = "NULL"; - if (!threadItrBE->threadName.IsNull()) - threadName = threadItrBE->threadName.GetPtr(); - sprintf(dumpLine, "%08x Ent %08x IP %08x LR %08x %-9s Aff %d%d%d Pri %2d Name %s", threadItrMPTR, _swapEndianU32(threadItrBE->entrypoint), threadItrBE->context.srr0, _swapEndianU32(threadItrBE->context.lr), threadStateStr, (affinity >> 0) & 1, (affinity >> 1) & 1, (affinity >> 2) & 1, effectivePriority, threadName); - // write line to log - CrashLog_WriteLine(dumpLine); - } + if (hCPU) + { + OSThread_t* currentThread = coreinit::OSGetCurrentThread(); + uint32 threadPtr = memory_getVirtualOffsetFromPointer(coreinit::OSGetCurrentThread()); + sprintf(dumpLine, "IP 0x%08x LR 0x%08x Thread 0x%08x", hCPU->instructionPointer, hCPU->spr.LR, threadPtr); + CrashLog_WriteLine(dumpLine); + + // GPR info + CrashLog_WriteLine(""); + auto gprs = hCPU->gpr; + sprintf(dumpLine, "r0 =%08x r1 =%08x r2 =%08x r3 =%08x r4 =%08x r5 =%08x r6 =%08x r7 =%08x", gprs[0], gprs[1], gprs[2], gprs[3], gprs[4], gprs[5], gprs[6], gprs[7]); + CrashLog_WriteLine(dumpLine); + sprintf(dumpLine, "r8 =%08x r9 =%08x r10=%08x r11=%08x r12=%08x r13=%08x r14=%08x r15=%08x", gprs[8], gprs[9], gprs[10], gprs[11], gprs[12], gprs[13], gprs[14], gprs[15]); + CrashLog_WriteLine(dumpLine); + sprintf(dumpLine, "r16=%08x r17=%08x r18=%08x r19=%08x r20=%08x r21=%08x r22=%08x r23=%08x", gprs[16], gprs[17], gprs[18], gprs[19], gprs[20], gprs[21], gprs[22], gprs[23]); + CrashLog_WriteLine(dumpLine); + sprintf(dumpLine, "r24=%08x r25=%08x r26=%08x r27=%08x r28=%08x r29=%08x r30=%08x r31=%08x", gprs[24], gprs[25], gprs[26], gprs[27], gprs[28], gprs[29], gprs[30], gprs[31]); + CrashLog_WriteLine(dumpLine); + + // stack trace + MPTR currentStackVAddr = hCPU->gpr[1]; + CrashLog_WriteLine(""); + CrashLog_WriteHeader("PPC stack trace"); + DebugLogStackTrace(currentThread, currentStackVAddr); + + // stack dump + CrashLog_WriteLine(""); + CrashLog_WriteHeader("PPC stack dump"); + for (uint32 i = 0; i < 16; i++) + { + MPTR lineAddr = currentStackVAddr + i * 8 * 4; + if (memory_isAddressRangeAccessible(lineAddr, 8 * 4)) + { + sprintf(dumpLine, "[0x%08x] %08x %08x %08x %08x - %08x %08x %08x %08x", lineAddr, memory_readU32(lineAddr + 0), memory_readU32(lineAddr + 4), memory_readU32(lineAddr + 8), memory_readU32(lineAddr + 12), memory_readU32(lineAddr + 16), memory_readU32(lineAddr + 20), memory_readU32(lineAddr + 24), memory_readU32(lineAddr + 28)); + CrashLog_WriteLine(dumpLine); + } + else + { + sprintf(dumpLine, "[0x%08x] ?", lineAddr); + CrashLog_WriteLine(dumpLine); + } + } + } + else + { + CrashLog_WriteLine("Not active"); + } + + // PPC thread log + CrashLog_WriteLine(""); + CrashLog_WriteHeader("PPC threads"); + if (activeThreadCount == 0) + { + CrashLog_WriteLine("None active"); + } + for (sint32 i = 0; i < activeThreadCount; i++) + { + MPTR threadItrMPTR = activeThread[i]; + OSThread_t* threadItrBE = (OSThread_t*)memory_getPointerFromVirtualOffset(threadItrMPTR); + + // get thread state + OSThread_t::THREAD_STATE threadState = threadItrBE->state; + const char* threadStateStr = "UNDEFINED"; + if (threadItrBE->suspendCounter != 0) + threadStateStr = "SUSPENDED"; + else if (threadState == OSThread_t::THREAD_STATE::STATE_NONE) + threadStateStr = "NONE"; + else if (threadState == OSThread_t::THREAD_STATE::STATE_READY) + threadStateStr = "READY"; + else if (threadState == OSThread_t::THREAD_STATE::STATE_RUNNING) + threadStateStr = "RUNNING"; + else if (threadState == OSThread_t::THREAD_STATE::STATE_WAITING) + threadStateStr = "WAITING"; + else if (threadState == OSThread_t::THREAD_STATE::STATE_MORIBUND) + threadStateStr = "MORIBUND"; + // generate log line + uint8 affinity = threadItrBE->attr; + sint32 effectivePriority = threadItrBE->effectivePriority; + const char* threadName = "NULL"; + if (!threadItrBE->threadName.IsNull()) + threadName = threadItrBE->threadName.GetPtr(); + sprintf(dumpLine, "%08x Ent %08x IP %08x LR %08x %-9s Aff %d%d%d Pri %2d Name %s", threadItrMPTR, _swapEndianU32(threadItrBE->entrypoint), threadItrBE->context.srr0, _swapEndianU32(threadItrBE->context.lr), threadStateStr, (affinity >> 0) & 1, (affinity >> 1) & 1, (affinity >> 2) & 1, effectivePriority, threadName); + // write line to log + CrashLog_WriteLine(dumpLine); + } +} + +void ExceptionHandler_DisplayErrorInfo(PPCInterpreter_t* hCpu) +{ + if (g_mainFrame->IsFullScreen()) + g_mainFrame->SetFullScreen(false); + + // If we're not a CPU thread, then we can shutdown the game execution + if (!hCpu) + CafeSystem::Shutdown(); + + wxString errorTitle; + wxString errorMessage; + + // Game crash + if (hCpu) + { + errorTitle = _("The game crashed"); + errorMessage = _("Oops! It looks like the game has encountered an unexpected error and crashed.\n" + "\n" + "Please note that this crash may be specific to the game and not necessarily due to CEMU.\n" + "\n" + "If this is a recurring issue, please consider reporting the bug on the Discord or GitGub.\n"); + } + // CEMU crash + else + { + errorTitle = _("CEMU has crashed"); + errorMessage = _("Oops! It looks like CEMU has encountered an unexpected error and crashed.\n" + "\n" + "If this is a recurring issue, please consider reporting the bug on the Discord or GitGub.\n"); + } + + // Display error message + wxMessageDialog dialog(nullptr, errorMessage, errorTitle, wxYES_NO | wxCENTRE | wxICON_ERROR); + dialog.SetYesNoLabels(_("Quit"), _("Open GitHub and Quit")); + + if (dialog.ShowModal() == wxID_NO) + wxLaunchDefaultBrowser("https://github.com/cemu-project/Cemu/issues/new?template=emulation_bug_report.yaml&title=Enter+a+title+for+the+bug+report+here"); + + bool clipboardSet = false; + + // Copy log file to clipboard +#if BOOST_OS_WINDOWS + if (OpenClipboard(nullptr)) + { + const auto logPath = fs::absolute(cemuLog_GetLogFilePath()); + const auto logPathStr = logPath.wstring(); + const auto totalSize = ((logPathStr.length() + 1) * sizeof(wchar_t)) + sizeof(DROPFILES); + + EmptyClipboard(); + HGLOBAL hMem = GlobalAlloc(GHND | GMEM_SHARE, totalSize); + if (hMem) + { + DROPFILES* pDropFiles = (DROPFILES*)GlobalLock(hMem); + if (pDropFiles) + { + memset(pDropFiles, 0, totalSize); + + pDropFiles->pFiles = sizeof(DROPFILES); + pDropFiles->fWide = TRUE; + + wchar_t* dataDest = (wchar_t*)((char*)pDropFiles + sizeof(DROPFILES)); + wcscpy_s(dataDest, logPathStr.length(), logPathStr.c_str()); + + GlobalUnlock(hMem); + SetClipboardData(CF_HDROP, hMem); + + clipboardSet = true; + } + } + CloseClipboard(); + } +#endif + + if (clipboardSet) + wxMessageBox(_("The log file has been copied to the clipboard. You can paste it on GitHub or Discord.\n"), _("Log file copied"), wxOK | wxICON_INFORMATION); + + _Exit(0); +} + +/* +* +* This errors for some reason, but the message box still pops up +* +* Failed to put data on the clipboard: -2147221008 (CoInitialize has not been called...) +* +* No matter my attempts to fix it +* +* calling CoInitialize here, still fails +* calling CoInitialize in the main thread, still fails +* calling CoInitialize in the UI thread, still fails +* + +if (wxTheClipboard->Open()) +{ +wxTheClipboard->Clear(); + +wxFileDataObject* fileData = new wxFileDataObject(); +fileData->AddFile(fs::absolute(cemuLog_GetLogFilePath()).string()); + +debug_printf("%s\n", fs::absolute(cemuLog_GetLogFilePath()).c_str()); + +wxTheClipboard->SetData(fileData); +wxTheClipboard->Flush(); +wxTheClipboard->Close(); + +wxMessageBox(_("The log file has been copied to the clipboard. You can paste it on GitHub or Discord.\n"), _("Log file copied"), wxOK | wxICON_INFORMATION); + } +*/ diff --git a/src/Common/ExceptionHandler/ExceptionHandler.h b/src/Common/ExceptionHandler/ExceptionHandler.h index 845e5f7fc..4ee79f0f2 100644 --- a/src/Common/ExceptionHandler/ExceptionHandler.h +++ b/src/Common/ExceptionHandler/ExceptionHandler.h @@ -8,3 +8,5 @@ void CrashLog_WriteLine(std::string_view text, bool newLine = true); void CrashLog_WriteHeader(const char* header); void ExceptionHandler_LogGeneralInfo(); + +void ExceptionHandler_DisplayErrorInfo(PPCInterpreter_t* hCpu); diff --git a/src/Common/ExceptionHandler/ExceptionHandler_win32.cpp b/src/Common/ExceptionHandler/ExceptionHandler_win32.cpp index 6b205d26c..d9823668b 100644 --- a/src/Common/ExceptionHandler/ExceptionHandler_win32.cpp +++ b/src/Common/ExceptionHandler/ExceptionHandler_win32.cpp @@ -238,7 +238,7 @@ void createCrashlog(EXCEPTION_POINTERS* e, PCONTEXT context) fs::copy_file(ActiveSettings::GetUserDataPath("log.txt"), p, ec); } - exit(0); + CafeSystem::StartCrashErrorThread(PPCInterpreter_getCurrentInstance()); return; } @@ -276,7 +276,7 @@ LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) LONG WINAPI cemu_unhandledExceptionFilter(EXCEPTION_POINTERS* pExceptionInfo) { createCrashlog(pExceptionInfo, pExceptionInfo->ContextRecord); - return EXCEPTION_NONCONTINUABLE_EXCEPTION; + return EXCEPTION_CONTINUE_EXECUTION; } void ExceptionHandler_Init()