diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index b83a48925675..8abec6927b16 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -385,16 +385,9 @@ public static native void Run(String[] path, boolean riivolution, String savesta public static native boolean IsRunningAndUnpaused(); /** - * Enables or disables CPU block profiling - * - * @param enable - */ - public static native void SetProfiling(boolean enable); - - /** - * Writes out the block profile results + * Writes out the JitBlock Cache log dump */ - public static native void WriteProfileResults(); + public static native void WriteJitBlockLogDump(); /** * Native EGL functions not exposed by Java bindings @@ -454,6 +447,14 @@ private static void CheckGameMetadataValid() private static native String GetCurrentTitleDescriptionUnchecked(); + @Keep + public static void displayToastMsg(final String text, final boolean long_length) + { + final int length = long_length ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT; + new Handler(Looper.getMainLooper()) + .post(() -> Toast.makeText(DolphinApplication.getAppContext(), text, length).show()); + } + @Keep public static boolean displayAlertMsg(final String caption, final String text, final boolean yesNo, final boolean isWarning, final boolean nonBlocking) @@ -466,9 +467,7 @@ public static boolean displayAlertMsg(final String caption, final String text, // and are allowed to block. As a fallback, we can use toasts. if (emulationActivity == null || nonBlocking) { - new Handler(Looper.getMainLooper()).post( - () -> Toast.makeText(DolphinApplication.getAppContext(), text, Toast.LENGTH_LONG) - .show()); + displayToastMsg(text, true); } else { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt index 6fddbe6b4325..331ad7fa09ce 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt @@ -214,6 +214,12 @@ enum class BooleanSetting( "JitRegisterCacheOff", false ), + MAIN_DEBUG_JIT_ENABLE_PROFILING( + Settings.FILE_DOLPHIN, + Settings.SECTION_DEBUG, + "JitEnableProfiling", + false + ), MAIN_EMULATE_SKYLANDER_PORTAL( Settings.FILE_DOLPHIN, Settings.SECTION_EMULATED_USB_DEVICES, diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt index a3ec018b469f..6de39ed1e909 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt @@ -1978,6 +1978,26 @@ class SettingsFragmentPresenter( ) ) + sl.add(HeaderSetting(context, R.string.debug_jit_profiling_header, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_DEBUG_JIT_ENABLE_PROFILING, + R.string.debug_jit_enable_block_profiling, + 0 + ) + ) + sl.add( + RunRunnable( + context, + R.string.debug_jit_write_block_log_dump, + 0, + 0, + 0, + true + ) { NativeLibrary.WriteJitBlockLogDump() } + ) + sl.add(HeaderSetting(context, R.string.debug_jit_header, 0)) sl.add( SwitchSetting( diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 5dde637d7d73..360e5006a03a 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -406,6 +406,9 @@ Disable Fastmem Disable Fastmem Arena Disable Large Entry Points Map + Jit Profiling + Enable Jit Block Profiling + Write Jit Block Log Dump Jit Jit Disabled Jit Load Store Disabled diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp index a19423d67c3d..5fe151278f28 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.cpp +++ b/Source/Android/jni/AndroidCommon/IDCache.cpp @@ -12,6 +12,7 @@ static JavaVM* s_java_vm; static jclass s_string_class; static jclass s_native_library_class; +static jmethodID s_display_toast_msg; static jmethodID s_display_alert_msg; static jmethodID s_update_touch_pointer; static jmethodID s_on_title_changed; @@ -146,6 +147,11 @@ jclass GetNativeLibraryClass() return s_native_library_class; } +jmethodID GetDisplayToastMsg() +{ + return s_display_toast_msg; +} + jmethodID GetDisplayAlertMsg() { return s_display_alert_msg; @@ -528,6 +534,8 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) const jclass native_library_class = env->FindClass("org/dolphinemu/dolphinemu/NativeLibrary"); s_native_library_class = reinterpret_cast(env->NewGlobalRef(native_library_class)); + s_display_toast_msg = + env->GetStaticMethodID(s_native_library_class, "displayToastMsg", "(Ljava/lang/String;Z)V"); s_display_alert_msg = env->GetStaticMethodID(s_native_library_class, "displayAlertMsg", "(Ljava/lang/String;Ljava/lang/String;ZZZ)Z"); s_update_touch_pointer = diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index 604b68e38e29..c324b6cb1987 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -12,6 +12,7 @@ JNIEnv* GetEnvForThread(); jclass GetStringClass(); jclass GetNativeLibraryClass(); +jmethodID GetDisplayToastMsg(); jmethodID GetDisplayAlertMsg(); jmethodID GetUpdateTouchPointer(); jmethodID GetOnTitleChanged(); diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index ebd0b2c79238..1034406d7a85 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,7 @@ #include "Common/Event.h" #include "Common/FileUtil.h" #include "Common/Flag.h" +#include "Common/IOFile.h" #include "Common/IniFile.h" #include "Common/Logging/LogManager.h" #include "Common/MsgHandler.h" @@ -42,7 +44,6 @@ #include "Core/Host.h" #include "Core/PowerPC/JitInterface.h" #include "Core/PowerPC/PowerPC.h" -#include "Core/PowerPC/Profiler.h" #include "Core/State.h" #include "Core/System.h" @@ -404,26 +405,34 @@ JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetMaxLogLev return static_cast(Common::Log::MAX_LOGLEVEL); } -JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling(JNIEnv*, jclass, - jboolean enable) +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteJitBlockLogDump( + JNIEnv* env, jclass native_library_class) { HostThreadLock guard; auto& system = Core::System::GetInstance(); auto& jit_interface = system.GetJitInterface(); - const Core::CPUThreadGuard cpu_guard(system); - jit_interface.ClearCache(cpu_guard); - jit_interface.SetProfilingState(enable ? JitInterface::ProfilingState::Enabled : - JitInterface::ProfilingState::Disabled); -} - -JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv*, - jclass) -{ - HostThreadLock guard; - std::string filename = File::GetUserPath(D_DUMP_IDX) + "Debug/profiler.txt"; - File::CreateFullPath(filename); - auto& jit_interface = Core::System::GetInstance().GetJitInterface(); - jit_interface.WriteProfileResults(filename); + if (jit_interface.GetCore() == nullptr) + { + env->CallStaticVoidMethod(native_library_class, IDCache::GetDisplayToastMsg(), + ToJString(env, Common::GetStringT("JIT is not active")), + static_cast(false)); + return; + } + const std::string filename = fmt::format("{}{}.txt", File::GetUserPath(D_DUMPDEBUG_JITBLOCKS_IDX), + SConfig::GetInstance().GetGameID()); + File::IOFile f(filename, "w"); + if (!f) + { + env->CallStaticVoidMethod( + native_library_class, IDCache::GetDisplayToastMsg(), + ToJString(env, Common::FmtFormatT("Failed to open \"{0}\" for writing.", filename)), + static_cast(false)); + return; + } + jit_interface.JitBlockLogDump(Core::CPUThreadGuard{system}, f.GetHandle()); + env->CallStaticVoidMethod(native_library_class, IDCache::GetDisplayToastMsg(), + ToJString(env, Common::FmtFormatT("Wrote to \"{0}\".", filename)), + static_cast(false)); } // Surface Handling diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 1431239af253..7b81cd2c6d0f 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -108,8 +108,6 @@ add_library(common Network.h PcapFile.cpp PcapFile.h - PerformanceCounter.cpp - PerformanceCounter.h Profiler.cpp Profiler.h QoSSession.cpp diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index 1dce5c47f569..bd0f94699115 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -77,6 +77,7 @@ #define DUMP_SSL_DIR "SSL" #define DUMP_DEBUG_DIR "Debug" #define DUMP_DEBUG_BRANCHWATCH_DIR "BranchWatch" +#define DUMP_DEBUG_JITBLOCKS_DIR "JitBlocks" #define LOGS_DIR "Logs" #define MAIL_LOGS_DIR "Mail" #define SHADERS_DIR "Shaders" diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index 20c57c2b5a82..a93db638df8c 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -859,6 +859,8 @@ static void RebuildUserDirectories(unsigned int dir_index) s_user_paths[D_DUMPDEBUG_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_DEBUG_DIR DIR_SEP; s_user_paths[D_DUMPDEBUG_BRANCHWATCH_IDX] = s_user_paths[D_DUMPDEBUG_IDX] + DUMP_DEBUG_BRANCHWATCH_DIR DIR_SEP; + s_user_paths[D_DUMPDEBUG_JITBLOCKS_IDX] = + s_user_paths[D_DUMPDEBUG_IDX] + DUMP_DEBUG_JITBLOCKS_DIR DIR_SEP; s_user_paths[D_LOGS_IDX] = s_user_paths[D_USER_IDX] + LOGS_DIR DIR_SEP; s_user_paths[D_MAILLOGS_IDX] = s_user_paths[D_LOGS_IDX] + MAIL_LOGS_DIR DIR_SEP; s_user_paths[D_THEMES_IDX] = s_user_paths[D_USER_IDX] + THEMES_DIR DIR_SEP; @@ -938,6 +940,8 @@ static void RebuildUserDirectories(unsigned int dir_index) s_user_paths[D_DUMPDEBUG_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_DEBUG_DIR DIR_SEP; s_user_paths[D_DUMPDEBUG_BRANCHWATCH_IDX] = s_user_paths[D_DUMPDEBUG_IDX] + DUMP_DEBUG_BRANCHWATCH_DIR DIR_SEP; + s_user_paths[D_DUMPDEBUG_JITBLOCKS_IDX] = + s_user_paths[D_DUMPDEBUG_IDX] + DUMP_DEBUG_JITBLOCKS_DIR DIR_SEP; s_user_paths[F_MEM1DUMP_IDX] = s_user_paths[D_DUMP_IDX] + MEM1_DUMP; s_user_paths[F_MEM2DUMP_IDX] = s_user_paths[D_DUMP_IDX] + MEM2_DUMP; s_user_paths[F_ARAMDUMP_IDX] = s_user_paths[D_DUMP_IDX] + ARAM_DUMP; diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index 975ab5525604..7d2d5e737a49 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -54,6 +54,7 @@ enum D_DUMPSSL_IDX, D_DUMPDEBUG_IDX, D_DUMPDEBUG_BRANCHWATCH_IDX, + D_DUMPDEBUG_JITBLOCKS_IDX, D_LOAD_IDX, D_LOGS_IDX, D_MAILLOGS_IDX, diff --git a/Source/Core/Common/PerformanceCounter.cpp b/Source/Core/Common/PerformanceCounter.cpp deleted file mode 100644 index efe78e33db14..000000000000 --- a/Source/Core/Common/PerformanceCounter.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2014 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#if !defined(_WIN32) -#include "Common/PerformanceCounter.h" - -#include -#include - -#include - -#include "Common/CommonTypes.h" - -#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 -#if defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK > 0 -#define DOLPHIN_CLOCK CLOCK_MONOTONIC -#else -#define DOLPHIN_CLOCK CLOCK_REALTIME -#endif -#endif - -bool QueryPerformanceCounter(u64* out) -{ -#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 - timespec tp; - if (clock_gettime(DOLPHIN_CLOCK, &tp)) - return false; - *out = (u64)tp.tv_nsec + (u64)1000000000 * (u64)tp.tv_sec; - return true; -#else - *out = 0; - return false; -#endif -} - -bool QueryPerformanceFrequency(u64* out) -{ -#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 - *out = 1000000000; - return true; -#else - *out = 1; - return false; -#endif -} - -#endif diff --git a/Source/Core/Common/PerformanceCounter.h b/Source/Core/Common/PerformanceCounter.h deleted file mode 100644 index 080f40c6cff9..000000000000 --- a/Source/Core/Common/PerformanceCounter.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2014 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#if !defined(_WIN32) - -#include - -#include "Common/CommonTypes.h" - -typedef u64 LARGE_INTEGER; -bool QueryPerformanceCounter(u64* out); -bool QueryPerformanceFrequency(u64* lpFrequency); - -#endif diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index fb2c0c18fa70..9d477b50a500 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -523,7 +523,6 @@ add_library(core PowerPC/PPCSymbolDB.h PowerPC/PPCTables.cpp PowerPC/PPCTables.h - PowerPC/Profiler.h PowerPC/SignatureDB/CSVSignatureDB.cpp PowerPC/SignatureDB/CSVSignatureDB.h PowerPC/SignatureDB/DSYSignatureDB.cpp diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 432abbeb2446..0add7e42bfe7 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -509,6 +509,8 @@ const Info MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF{ const Info MAIN_DEBUG_JIT_BRANCH_OFF{{System::Main, "Debug", "JitBranchOff"}, false}; const Info MAIN_DEBUG_JIT_REGISTER_CACHE_OFF{{System::Main, "Debug", "JitRegisterCacheOff"}, false}; +const Info MAIN_DEBUG_JIT_ENABLE_PROFILING{{System::Main, "Debug", "JitEnableProfiling"}, + false}; // Main.BluetoothPassthrough diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h index 5c028d9b3607..d7e78bf6e9e4 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -335,6 +335,7 @@ extern const Info MAIN_DEBUG_JIT_PAIRED_OFF; extern const Info MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF; extern const Info MAIN_DEBUG_JIT_BRANCH_OFF; extern const Info MAIN_DEBUG_JIT_REGISTER_CACHE_OFF; +extern const Info MAIN_DEBUG_JIT_ENABLE_PROFILING; // Main.BluetoothPassthrough diff --git a/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.cpp b/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.cpp index 0c600561d19e..d2296641c2e8 100644 --- a/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.cpp +++ b/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.cpp @@ -316,7 +316,7 @@ void CachedInterpreter::Jit(u32 address) js.numFloatingPointInst = 0; js.curBlock = b; - b->normalEntry = GetCodePtr(); + b->normalEntry = b->near_begin = GetCodePtr(); for (u32 i = 0; i < code_block.m_num_instructions; i++) { @@ -382,6 +382,10 @@ void CachedInterpreter::Jit(u32 address) } m_code.emplace_back(); + b->near_end = GetCodePtr(); + b->far_begin = nullptr; + b->far_end = nullptr; + b->codeSize = static_cast(GetCodePtr() - b->normalEntry); b->originalSize = code_block.m_num_instructions; diff --git a/Source/Core/Core/PowerPC/Gekko.h b/Source/Core/Core/PowerPC/Gekko.h index 62a1743b4531..86c6dd863262 100644 --- a/Source/Core/Core/PowerPC/Gekko.h +++ b/Source/Core/Core/PowerPC/Gekko.h @@ -931,6 +931,7 @@ enum CPUEmuFeatureFlags : u32 FEATURE_FLAG_MSR_DR = 1 << 0, FEATURE_FLAG_MSR_IR = 1 << 1, FEATURE_FLAG_PERFMON = 1 << 2, + FEATURE_FLAG_END_OF_ENUMERATION, }; constexpr s32 SignExt16(s16 x) diff --git a/Source/Core/Core/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/PowerPC/Jit64/Jit.cpp index ce1dc906d396..867190e28487 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit.cpp @@ -19,7 +19,6 @@ #include "Common/GekkoDisassembler.h" #include "Common/IOFile.h" #include "Common/Logging/Log.h" -#include "Common/PerformanceCounter.h" #include "Common/StringUtil.h" #include "Common/Swap.h" #include "Common/x64ABI.h" @@ -43,7 +42,6 @@ #include "Core/PowerPC/MMU.h" #include "Core/PowerPC/PPCAnalyst.h" #include "Core/PowerPC/PowerPC.h" -#include "Core/PowerPC/Profiler.h" #include "Core/System.h" using namespace Gen; @@ -454,20 +452,11 @@ bool Jit64::Cleanup() did_something = true; } - if (jo.profile_blocks) + if (IsProfilingEnabled()) { ABI_PushRegistersAndAdjustStack({}, 0); - // get end tic - MOV(64, R(ABI_PARAM1), ImmPtr(&js.curBlock->profile_data.ticStop)); - ABI_CallFunction(QueryPerformanceCounter); - // tic counter += (end tic - start tic) - MOV(64, R(RSCRATCH2), ImmPtr(&js.curBlock->profile_data)); - MOV(64, R(RSCRATCH), MDisp(RSCRATCH2, offsetof(JitBlock::ProfileData, ticStop))); - SUB(64, R(RSCRATCH), MDisp(RSCRATCH2, offsetof(JitBlock::ProfileData, ticStart))); - ADD(64, R(RSCRATCH), MDisp(RSCRATCH2, offsetof(JitBlock::ProfileData, ticCounter))); - ADD(64, MDisp(RSCRATCH2, offsetof(JitBlock::ProfileData, downcountCounter)), - Imm32(js.downcountAmount)); - MOV(64, MDisp(RSCRATCH2, offsetof(JitBlock::ProfileData, ticCounter)), R(RSCRATCH)); + ABI_CallFunctionPC(&JitBlock::ProfileData::EndProfiling, js.curBlock->profile_data.get(), + js.downcountAmount); ABI_PopRegistersAndAdjustStack({}, 0); did_something = true; } @@ -773,7 +762,7 @@ void Jit64::Jit(u32 em_address, bool clear_cache_and_retry_on_failure) EnableBlockLink(); EnableOptimization(); - if (!jo.profile_blocks) + if (!IsProfilingEnabled()) { if (m_system.GetCPU().IsStepping()) { @@ -899,15 +888,9 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) } // Conditionally add profiling code. - if (jo.profile_blocks) - { - // get start tic - MOV(64, R(ABI_PARAM1), ImmPtr(&b->profile_data.ticStart)); - int offset = static_cast(offsetof(JitBlock::ProfileData, runCount)) - - static_cast(offsetof(JitBlock::ProfileData, ticStart)); - ADD(64, MDisp(ABI_PARAM1, offset), Imm8(1)); - ABI_CallFunction(QueryPerformanceCounter); - } + if (IsProfilingEnabled()) + ABI_CallFunctionP(&JitBlock::ProfileData::BeginProfiling, b->profile_data.get()); + #if defined(_DEBUG) || defined(DEBUGFAST) || defined(NAN_CHECK) // should help logged stack-traces become more accurate MOV(32, PPCSTATE(pc), Imm32(js.blockStart)); diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp index b8fada1dcb88..f596128ef918 100644 --- a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp @@ -10,7 +10,6 @@ #include "Common/Logging/Log.h" #include "Common/MathUtil.h" #include "Common/MsgHandler.h" -#include "Common/PerformanceCounter.h" #include "Common/StringUtil.h" #include "Core/ConfigManager.h" @@ -26,7 +25,6 @@ #include "Core/PowerPC/JitArm64/JitArm64_RegCache.h" #include "Core/PowerPC/JitInterface.h" #include "Core/PowerPC/PowerPC.h" -#include "Core/PowerPC/Profiler.h" #include "Core/System.h" using namespace Arm64Gen; @@ -408,7 +406,11 @@ void JitArm64::WriteExit(u32 destination, bool LK, u32 exit_address_after_return ARM64Reg exit_address_after_return_reg) { Cleanup(); - EndTimeProfile(js.curBlock); + if (IsProfilingEnabled()) + { + ABI_CallFunction(&JitBlock::ProfileData::EndProfiling, js.curBlock->profile_data.get(), + js.downcountAmount); + } DoDownCount(); LK &= m_enable_blr_optimization; @@ -509,7 +511,11 @@ void JitArm64::WriteExit(Arm64Gen::ARM64Reg dest, bool LK, u32 exit_address_afte MOV(DISPATCHER_PC, dest); Cleanup(); - EndTimeProfile(js.curBlock); + if (IsProfilingEnabled()) + { + ABI_CallFunction(&JitBlock::ProfileData::EndProfiling, js.curBlock->profile_data.get(), + js.downcountAmount); + } DoDownCount(); LK &= m_enable_blr_optimization; @@ -672,7 +678,11 @@ void JitArm64::WriteBLRExit(Arm64Gen::ARM64Reg dest) MOV(DISPATCHER_PC, dest); Cleanup(); - EndTimeProfile(js.curBlock); + if (IsProfilingEnabled()) + { + ABI_CallFunction(&JitBlock::ProfileData::EndProfiling, js.curBlock->profile_data.get(), + js.downcountAmount); + } // Check if {PPC_PC, feature_flags} matches the current state, then RET to ARM_PC. LDP(IndexType::Post, ARM64Reg::X2, ARM64Reg::X1, ARM64Reg::SP, 16); @@ -736,7 +746,11 @@ void JitArm64::WriteExceptionExit(ARM64Reg dest, bool only_external, bool always if (!always_exception) SetJumpTarget(no_exceptions); - EndTimeProfile(js.curBlock); + if (IsProfilingEnabled()) + { + ABI_CallFunction(&JitBlock::ProfileData::EndProfiling, js.curBlock->profile_data.get(), + js.downcountAmount); + } DoDownCount(); B(dispatcher); @@ -804,44 +818,6 @@ void JitArm64::DumpCode(const u8* start, const u8* end) WARN_LOG_FMT(DYNA_REC, "Code dump from {} to {}:\n{}", fmt::ptr(start), fmt::ptr(end), output); } -void JitArm64::BeginTimeProfile(JitBlock* b) -{ - MOVP2R(ARM64Reg::X0, &b->profile_data); - LDR(IndexType::Unsigned, ARM64Reg::X1, ARM64Reg::X0, offsetof(JitBlock::ProfileData, runCount)); - ADD(ARM64Reg::X1, ARM64Reg::X1, 1); - - // Fetch the current counter register - CNTVCT(ARM64Reg::X2); - - // stores runCount and ticStart - STP(IndexType::Signed, ARM64Reg::X1, ARM64Reg::X2, ARM64Reg::X0, - offsetof(JitBlock::ProfileData, runCount)); -} - -void JitArm64::EndTimeProfile(JitBlock* b) -{ - if (!jo.profile_blocks) - return; - - // Fetch the current counter register - CNTVCT(ARM64Reg::X1); - - MOVP2R(ARM64Reg::X0, &b->profile_data); - - LDR(IndexType::Unsigned, ARM64Reg::X2, ARM64Reg::X0, offsetof(JitBlock::ProfileData, ticStart)); - SUB(ARM64Reg::X1, ARM64Reg::X1, ARM64Reg::X2); - - // loads ticCounter and downcountCounter - LDP(IndexType::Signed, ARM64Reg::X2, ARM64Reg::X3, ARM64Reg::X0, - offsetof(JitBlock::ProfileData, ticCounter)); - ADD(ARM64Reg::X2, ARM64Reg::X2, ARM64Reg::X1); - ADDI2R(ARM64Reg::X3, ARM64Reg::X3, js.downcountAmount, ARM64Reg::X1); - - // stores ticCounter and downcountCounter - STP(IndexType::Signed, ARM64Reg::X2, ARM64Reg::X3, ARM64Reg::X0, - offsetof(JitBlock::ProfileData, ticCounter)); -} - void JitArm64::Run() { ProtectStack(); @@ -933,7 +909,7 @@ void JitArm64::Jit(u32 em_address, bool clear_cache_and_retry_on_failure) SetBlockLinkingEnabled(true); SetOptimizationEnabled(true); - if (!jo.profile_blocks) + if (!IsProfilingEnabled()) { if (cpu.IsStepping()) { @@ -1052,11 +1028,8 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) b->normalEntry = GetWritableCodePtr(); // Conditionally add profiling code. - if (jo.profile_blocks) - { - // get start tic - BeginTimeProfile(b); - } + if (IsProfilingEnabled()) + ABI_CallFunction(&JitBlock::ProfileData::BeginProfiling, b->profile_data.get()); if (code_block.m_gqr_used.Count() == 1 && js.pairedQuantizeAddresses.find(js.blockStart) == js.pairedQuantizeAddresses.end()) @@ -1246,7 +1219,11 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) FixupBranch no_breakpoint = CBZ(ARM64Reg::W0); Cleanup(); - EndTimeProfile(js.curBlock); + if (IsProfilingEnabled()) + { + ABI_CallFunction(&JitBlock::ProfileData::EndProfiling, b->profile_data.get(), + js.downcountAmount); + } DoDownCount(); B(dispatcher_exit); diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit.h b/Source/Core/Core/PowerPC/JitArm64/Jit.h index 6d0ded667a5f..a572b2e38fe4 100644 --- a/Source/Core/Core/PowerPC/JitArm64/Jit.h +++ b/Source/Core/Core/PowerPC/JitArm64/Jit.h @@ -307,10 +307,6 @@ class JitArm64 : public JitBase, public Arm64Gen::ARM64CodeBlock, public CommonA void GenerateQuantizedLoads(); void GenerateQuantizedStores(); - // Profiling - void BeginTimeProfile(JitBlock* b); - void EndTimeProfile(JitBlock* b); - void EmitUpdateMembase(); void MSRUpdated(u32 msr); void MSRUpdated(Arm64Gen::ARM64Reg msr); diff --git a/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp b/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp index c99938876028..5ec9af396751 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp +++ b/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp @@ -57,7 +57,7 @@ // After resetting the stack to the top, we call _resetstkoflw() to restore // the guard page at the 256kb mark. -const std::array*>, 22> JitBase::JIT_SETTINGS{{ +const std::array*>, 23> JitBase::JIT_SETTINGS{{ {&JitBase::bJITOff, &Config::MAIN_DEBUG_JIT_OFF}, {&JitBase::bJITLoadStoreOff, &Config::MAIN_DEBUG_JIT_LOAD_STORE_OFF}, {&JitBase::bJITLoadStorelXzOff, &Config::MAIN_DEBUG_JIT_LOAD_STORE_LXZ_OFF}, @@ -71,6 +71,7 @@ const std::array*>, 22> JitB {&JitBase::bJITSystemRegistersOff, &Config::MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF}, {&JitBase::bJITBranchOff, &Config::MAIN_DEBUG_JIT_BRANCH_OFF}, {&JitBase::bJITRegisterCacheOff, &Config::MAIN_DEBUG_JIT_REGISTER_CACHE_OFF}, + {&JitBase::m_enable_profiling, &Config::MAIN_DEBUG_JIT_ENABLE_PROFILING}, {&JitBase::m_enable_debugging, &Config::MAIN_ENABLE_DEBUGGING}, {&JitBase::m_enable_branch_following, &Config::MAIN_JIT_FOLLOW_BRANCH}, {&JitBase::m_enable_float_exceptions, &Config::MAIN_FLOAT_EXCEPTIONS}, diff --git a/Source/Core/Core/PowerPC/JitCommon/JitBase.h b/Source/Core/Core/PowerPC/JitCommon/JitBase.h index d3579368f42f..cf0ce78dbc6c 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitBase.h +++ b/Source/Core/Core/PowerPC/JitCommon/JitBase.h @@ -86,7 +86,6 @@ class JitBase : public CPUCoreBase bool memcheck; bool fp_exceptions; bool div_by_zero_exceptions; - bool profile_blocks; }; struct JitState { @@ -149,6 +148,7 @@ class JitBase : public CPUCoreBase bool bJITSystemRegistersOff = false; bool bJITBranchOff = false; bool bJITRegisterCacheOff = false; + bool m_enable_profiling = false; bool m_enable_debugging = false; bool m_enable_branch_following = false; bool m_enable_float_exceptions = false; @@ -163,7 +163,7 @@ class JitBase : public CPUCoreBase bool m_cleanup_after_stackfault = false; u8* m_stack_guard = nullptr; - static const std::array*>, 22> JIT_SETTINGS; + static const std::array*>, 23> JIT_SETTINGS; bool DoesConfigNeedRefresh(); void RefreshConfig(); @@ -187,6 +187,7 @@ class JitBase : public CPUCoreBase JitBase& operator=(JitBase&&) = delete; ~JitBase() override; + bool IsProfilingEnabled() const { return m_enable_profiling; } bool IsDebuggingEnabled() const { return m_enable_debugging; } static const u8* Dispatch(JitBase& jit); diff --git a/Source/Core/Core/PowerPC/JitCommon/JitCache.cpp b/Source/Core/Core/PowerPC/JitCommon/JitCache.cpp index d8d6e8d13b81..8029d06c2d36 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitCache.cpp +++ b/Source/Core/Core/PowerPC/JitCommon/JitCache.cpp @@ -32,6 +32,18 @@ bool JitBlock::OverlapsPhysicalRange(u32 address, u32 length) const physical_addresses.lower_bound(address + length); } +void JitBlock::ProfileData::BeginProfiling(ProfileData* data) +{ + data->run_count += 1; + data->time_start = Clock::now(); +} + +void JitBlock::ProfileData::EndProfiling(ProfileData* data, int downcount_amount) +{ + data->cycles_spent += downcount_amount; + data->time_spent += Clock::now() - data->time_start; +} + JitBaseBlockCache::JitBaseBlockCache(JitBase& jit) : m_jit{jit} { } @@ -98,7 +110,8 @@ JitBlock** JitBaseBlockCache::GetFastBlockMapFallback() return m_fast_block_map_fallback.data(); } -void JitBaseBlockCache::RunOnBlocks(std::function f) +void JitBaseBlockCache::RunOnBlocks(const Core::CPUThreadGuard&, + std::function f) const { for (const auto& e : block_map) f(e.second); @@ -107,7 +120,7 @@ void JitBaseBlockCache::RunOnBlocks(std::function f) JitBlock* JitBaseBlockCache::AllocateBlock(u32 em_address) { const u32 physical_address = m_jit.m_mmu.JitCache_TranslateAddress(em_address).address; - JitBlock& b = block_map.emplace(physical_address, JitBlock())->second; + JitBlock& b = block_map.emplace(physical_address, m_jit.IsProfilingEnabled())->second; b.effectiveAddress = em_address; b.physicalAddress = physical_address; b.feature_flags = m_jit.m_ppc_state.feature_flags; diff --git a/Source/Core/Core/PowerPC/JitCommon/JitCache.h b/Source/Core/Core/PowerPC/JitCommon/JitCache.h index feb872ee7b60..d44cc097d3eb 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitCache.h +++ b/Source/Core/Core/PowerPC/JitCommon/JitCache.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -64,6 +65,27 @@ static_assert(std::is_standard_layout_v, "JitBlockData must have a // address. struct JitBlock : public JitBlockData { + // Software profiling data for JIT block. + struct ProfileData + { + using Clock = std::chrono::steady_clock; + + static void BeginProfiling(ProfileData* data); + static void EndProfiling(ProfileData* data, int downcount_amount); + + std::size_t run_count = 0; + u64 cycles_spent = 0; + Clock::duration time_spent = {}; + + private: + Clock::time_point time_start; + }; + + explicit JitBlock(bool profiling_enabled) + : profile_data(profiling_enabled ? std::make_unique() : nullptr) + { + } + bool OverlapsPhysicalRange(u32 address, u32 length) const; // Information about exits to a known address from this block. @@ -83,15 +105,7 @@ struct JitBlock : public JitBlockData // This set stores all physical addresses of all occupied instructions. std::set physical_addresses; - // Block profiling data, structure is inlined in Jit.cpp - struct ProfileData - { - u64 ticCounter; - u64 downcountCounter; - u64 runCount; - u64 ticStart; - u64 ticStop; - } profile_data = {}; + std::unique_ptr profile_data; }; typedef void (*CompiledCode)(); @@ -146,7 +160,7 @@ class JitBaseBlockCache // Code Cache u8** GetEntryPoints(); JitBlock** GetFastBlockMapFallback(); - void RunOnBlocks(std::function f); + void RunOnBlocks(const Core::CPUThreadGuard& guard, std::function f) const; JitBlock* AllocateBlock(u32 em_address); void FinalizeBlock(JitBlock& block, bool block_link, const std::set& physical_addresses); diff --git a/Source/Core/Core/PowerPC/JitInterface.cpp b/Source/Core/Core/PowerPC/JitInterface.cpp index 8372bf302ad9..bd3189303594 100644 --- a/Source/Core/Core/PowerPC/JitInterface.cpp +++ b/Source/Core/Core/PowerPC/JitInterface.cpp @@ -4,22 +4,14 @@ #include "Core/PowerPC/JitInterface.h" #include -#include #include #include -#ifdef _WIN32 -#include -#else -#include "Common/PerformanceCounter.h" -#endif - #include #include "Common/Assert.h" #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" -#include "Common/IOFile.h" #include "Common/MsgHandler.h" #include "Core/Core.h" @@ -29,7 +21,6 @@ #include "Core/PowerPC/MMU.h" #include "Core/PowerPC/PPCSymbolDB.h" #include "Core/PowerPC/PowerPC.h" -#include "Core/PowerPC/Profiler.h" #include "Core/System.h" #ifdef _M_X86_64 @@ -90,14 +81,6 @@ CPUCoreBase* JitInterface::GetCore() const return m_jit.get(); } -void JitInterface::SetProfilingState(ProfilingState state) -{ - if (!m_jit) - return; - - m_jit->jo.profile_blocks = state == ProfilingState::Enabled; -} - void JitInterface::UpdateMembase() { if (!m_jit) @@ -123,58 +106,80 @@ void JitInterface::UpdateMembase() } } -void JitInterface::WriteProfileResults(const std::string& filename) const +static std::string_view GetDescription(const CPUEmuFeatureFlags flags) { - Profiler::ProfileStats prof_stats; - GetProfileResults(&prof_stats); - - File::IOFile f(filename, "w"); - if (!f) - { - PanicAlertFmt("Failed to open {}", filename); - return; - } - f.WriteString("origAddr\tblkName\trunCount\tcost\ttimeCost\tpercent\ttimePercent\tOvAllinBlkTime(" - "ms)\tblkCodeSize\n"); - for (auto& stat : prof_stats.block_stats) - { - std::string name = m_system.GetPPCSymbolDB().GetDescription(stat.addr); - double percent = 100.0 * (double)stat.cost / (double)prof_stats.cost_sum; - double timePercent = 100.0 * (double)stat.tick_counter / (double)prof_stats.timecost_sum; - f.WriteString(fmt::format("{0:08x}\t{1}\t{2}\t{3}\t{4}\t{5:.2f}\t{6:.2f}\t{7:.2f}\t{8}\n", - stat.addr, name, stat.run_count, stat.cost, stat.tick_counter, - percent, timePercent, - static_cast(stat.tick_counter) * 1000.0 / - static_cast(prof_stats.countsPerSec), - stat.block_size)); - } + static constexpr std::array + descriptions = { + "", "DR", "IR", "DR|IR", "PERFMON", "DR|PERFMON", "IR|PERFMON", "DR|IR|PERFMON", + }; + return descriptions[flags]; } -void JitInterface::GetProfileResults(Profiler::ProfileStats* prof_stats) const +void JitInterface::JitBlockLogDump(const Core::CPUThreadGuard& guard, std::FILE* file) const { - // Can't really do this with no m_jit core available + std::fputs( + "ppcFeatureFlags\tppcAddress\tppcSize\thostNearSize\thostFarSize\trunCount\tcyclesSpent" + "\tcyclesAverage\tcyclesPercent\ttimeSpent(ns)\ttimeAverage(ns)\ttimePercent\tsymbol\n", + file); + if (!m_jit) return; - prof_stats->cost_sum = 0; - prof_stats->timecost_sum = 0; - prof_stats->block_stats.clear(); - - const Core::CPUThreadGuard guard(m_system); - QueryPerformanceFrequency((LARGE_INTEGER*)&prof_stats->countsPerSec); - m_jit->GetBlockCache()->RunOnBlocks([&prof_stats](const JitBlock& block) { - const auto& data = block.profile_data; - u64 cost = data.downcountCounter; - u64 timecost = data.ticCounter; - // Todo: tweak. - if (data.runCount >= 1) - prof_stats->block_stats.emplace_back(block.effectiveAddress, cost, timecost, data.runCount, - block.codeSize); - prof_stats->cost_sum += cost; - prof_stats->timecost_sum += timecost; - }); - - sort(prof_stats->block_stats.begin(), prof_stats->block_stats.end()); + if (m_jit->IsProfilingEnabled()) + { + u64 overall_cycles_spent = 0; + JitBlock::ProfileData::Clock::duration overall_time_spent = {}; + m_jit->GetBlockCache()->RunOnBlocks(guard, [&](const JitBlock& block) { + overall_cycles_spent += block.profile_data->cycles_spent; + overall_time_spent += block.profile_data->time_spent; + }); + m_jit->GetBlockCache()->RunOnBlocks(guard, [&](const JitBlock& block) { + const Common::Symbol* const symbol = + m_jit->m_ppc_symbol_db.GetSymbolFromAddr(block.effectiveAddress); + const JitBlock::ProfileData* const data = block.profile_data.get(); + + const double cycles_percent = + overall_cycles_spent == 0 ? double{} : 100.0 * data->cycles_spent / overall_cycles_spent; + const double time_percent = overall_time_spent == JitBlock::ProfileData::Clock::duration{} ? + double{} : + 100.0 * data->time_spent.count() / overall_time_spent.count(); + const double cycles_average = data->run_count == 0 ? + double{} : + static_cast(data->cycles_spent) / data->run_count; + const double time_average = + data->run_count == 0 ? + double{} : + std::chrono::duration_cast>(data->time_spent) + .count() / + data->run_count; + + const std::size_t host_near_code_size = block.near_end - block.near_begin; + const std::size_t host_far_code_size = block.far_end - block.far_begin; + + fmt::println( + file, "{}\t{:08x}\t{}\t{}\t{}\t{}\t{}\t{:.6f}\t{:.6f}\t{}\t{:.6f}\t{:.6f}\t\"{}\"", + GetDescription(block.feature_flags), block.effectiveAddress, + block.originalSize * sizeof(UGeckoInstruction), host_near_code_size, host_far_code_size, + data->run_count, data->cycles_spent, cycles_average, cycles_percent, + std::chrono::duration_cast(data->time_spent).count(), + time_average, time_percent, symbol ? std::string_view{symbol->name} : ""); + }); + } + else + { + m_jit->GetBlockCache()->RunOnBlocks(guard, [&](const JitBlock& block) { + const Common::Symbol* const symbol = + m_jit->m_ppc_symbol_db.GetSymbolFromAddr(block.effectiveAddress); + + const std::size_t host_near_code_size = block.near_end - block.near_begin; + const std::size_t host_far_code_size = block.far_end - block.far_begin; + + fmt::println(file, "{}\t{:08x}\t{}\t{}\t{}\t-\t-\t-\t-\t-\t-\t-\t\"{}\"", + GetDescription(block.feature_flags), block.effectiveAddress, + block.originalSize * sizeof(UGeckoInstruction), host_near_code_size, + host_far_code_size, symbol ? std::string_view{symbol->name} : ""); + }); + } } std::variant diff --git a/Source/Core/Core/PowerPC/JitInterface.h b/Source/Core/Core/PowerPC/JitInterface.h index 4cf7c3179c44..17d0796bfddc 100644 --- a/Source/Core/Core/PowerPC/JitInterface.h +++ b/Source/Core/Core/PowerPC/JitInterface.h @@ -3,6 +3,9 @@ #pragma once +#include +#include +#include #include #include #include @@ -24,11 +27,6 @@ namespace PowerPC enum class CPUCore; } -namespace Profiler -{ -struct ProfileStats; -} - class JitInterface { public: @@ -45,11 +43,6 @@ class JitInterface CPUCoreBase* GetCore() const; // Debugging - enum class ProfilingState - { - Enabled, - Disabled - }; enum class GetHostCodeError { NoJitActive, @@ -63,9 +56,7 @@ class JitInterface }; void UpdateMembase(); - void SetProfilingState(ProfilingState state); - void WriteProfileResults(const std::string& filename) const; - void GetProfileResults(Profiler::ProfileStats* prof_stats) const; + void JitBlockLogDump(const Core::CPUThreadGuard& guard, std::FILE* file) const; std::variant GetHostCode(u32 address) const; // Memory Utilities diff --git a/Source/Core/Core/PowerPC/Profiler.h b/Source/Core/Core/PowerPC/Profiler.h deleted file mode 100644 index 3c4b3eb33b69..000000000000 --- a/Source/Core/Core/PowerPC/Profiler.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include - -#include "Common/CommonTypes.h" - -namespace Profiler -{ -struct BlockStat -{ - BlockStat(u32 _addr, u64 c, u64 ticks, u64 run, u32 size) - : addr(_addr), cost(c), tick_counter(ticks), run_count(run), block_size(size) - { - } - u32 addr; - u64 cost; - u64 tick_counter; - u64 run_count; - u32 block_size; - - bool operator<(const BlockStat& other) const { return cost > other.cost; } -}; -struct ProfileStats -{ - std::vector block_stats; - u64 cost_sum = 0; - u64 timecost_sum = 0; - u64 countsPerSec = 0; -}; - -} // namespace Profiler diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 5622bef02869..cd6fcfc16136 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -141,7 +141,6 @@ - @@ -447,7 +446,6 @@ - @@ -824,7 +822,6 @@ - diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp index 01280cacf435..46bd68b6d450 100644 --- a/Source/Core/DolphinQt/MenuBar.cpp +++ b/Source/Core/DolphinQt/MenuBar.cpp @@ -15,9 +15,12 @@ #include #include +#include + #include "Common/Align.h" #include "Common/CommonPaths.h" #include "Common/FileUtil.h" +#include "Common/IOFile.h" #include "Common/StringUtil.h" #include "Core/AchievementManager.h" @@ -150,6 +153,7 @@ void MenuBar::OnEmulationStateChanged(Core::State state) !Core::System::GetInstance().GetMovie().IsPlayingInput()); // JIT + const bool jit_exists = Core::System::GetInstance().GetJitInterface().GetCore() != nullptr; m_jit_interpreter_core->setEnabled(running); m_jit_block_linking->setEnabled(!running); m_jit_disable_cache->setEnabled(!running); @@ -158,6 +162,7 @@ void MenuBar::OnEmulationStateChanged(Core::State state) m_jit_clear_cache->setEnabled(running); m_jit_log_coverage->setEnabled(!running); m_jit_search_instruction->setEnabled(running); + m_jit_write_cache_log_dump->setEnabled(running && jit_exists); // Symbols m_symbols->setEnabled(running); @@ -198,6 +203,30 @@ void MenuBar::OnDebugModeToggled(bool enabled) } } +void MenuBar::OnWriteJitBlockLogDump() +{ + const std::string filename = fmt::format("{}{}.txt", File::GetUserPath(D_DUMPDEBUG_JITBLOCKS_IDX), + SConfig::GetInstance().GetGameID()); + File::IOFile f(filename, "w"); + if (!f) + { + ModalMessageBox::warning( + this, tr("Error"), + tr("Failed to open \"%1\" for writing.").arg(QString::fromStdString(filename))); + return; + } + auto& system = Core::System::GetInstance(); + system.GetJitInterface().JitBlockLogDump(Core::CPUThreadGuard{system}, f.GetHandle()); + if (static bool ignore = false; ignore == false) + { + const int button_pressed = ModalMessageBox::information( + this, tr("Success"), tr("Wrote to \"%1\".").arg(QString::fromStdString(filename)), + QMessageBox::Ok | QMessageBox::Ignore); + if (button_pressed == QMessageBox::Ignore) + ignore = true; + } +} + void MenuBar::AddFileMenu() { QMenu* file_menu = addMenu(tr("&File")); @@ -892,6 +921,17 @@ void MenuBar::AddJITMenu() m_jit->addSeparator(); + m_jit_profile_blocks = m_jit->addAction(tr("Enable JIT Block Profiling")); + m_jit_profile_blocks->setCheckable(true); + m_jit_profile_blocks->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_ENABLE_PROFILING)); + connect(m_jit_profile_blocks, &QAction::toggled, [](bool enabled) { + Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_ENABLE_PROFILING, enabled); + }); + m_jit_write_cache_log_dump = + m_jit->addAction(tr("Write JIT Block Log Dump"), this, &MenuBar::OnWriteJitBlockLogDump); + + m_jit->addSeparator(); + m_jit_off = m_jit->addAction(tr("JIT Off (JIT Core)")); m_jit_off->setCheckable(true); m_jit_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_OFF)); diff --git a/Source/Core/DolphinQt/MenuBar.h b/Source/Core/DolphinQt/MenuBar.h index c8a1f6311ade..b6620291d22a 100644 --- a/Source/Core/DolphinQt/MenuBar.h +++ b/Source/Core/DolphinQt/MenuBar.h @@ -185,6 +185,7 @@ class MenuBar final : public QMenuBar void OnRecordingStatusChanged(bool recording); void OnReadOnlyModeChanged(bool read_only); void OnDebugModeToggled(bool enabled); + void OnWriteJitBlockLogDump(); QString GetSignatureSelector() const; @@ -268,6 +269,8 @@ class MenuBar final : public QMenuBar QAction* m_jit_clear_cache; QAction* m_jit_log_coverage; QAction* m_jit_search_instruction; + QAction* m_jit_profile_blocks; + QAction* m_jit_write_cache_log_dump; QAction* m_jit_off; QAction* m_jit_loadstore_off; QAction* m_jit_loadstore_lbzx_off; diff --git a/Source/Core/UICommon/UICommon.cpp b/Source/Core/UICommon/UICommon.cpp index 5ff9364dd04d..0875b53383e0 100644 --- a/Source/Core/UICommon/UICommon.cpp +++ b/Source/Core/UICommon/UICommon.cpp @@ -76,6 +76,7 @@ static void CreateDumpPath(std::string path) File::CreateFullPath(File::GetUserPath(D_DUMPTEXTURES_IDX)); File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_IDX)); File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_BRANCHWATCH_IDX)); + File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_JITBLOCKS_IDX)); } static void CreateLoadPath(std::string path) @@ -257,6 +258,7 @@ void CreateDirectories() File::CreateFullPath(File::GetUserPath(D_DUMPTEXTURES_IDX)); File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_IDX)); File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_BRANCHWATCH_IDX)); + File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_JITBLOCKS_IDX)); File::CreateFullPath(File::GetUserPath(D_GAMESETTINGS_IDX)); File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX)); File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + USA_DIR DIR_SEP);