diff --git a/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.h b/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.h index 27cf1262ba..41aa82cf79 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.h +++ b/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.h @@ -36,7 +36,7 @@ #else #error No context definition for OS #endif -#elif defined(__APPLE__) +#elif defined(__APPLE__) && !defined(USE_SIGACTION_ON_APPLE) #include #include #if _M_X86_64 @@ -61,6 +61,26 @@ #else #error No context definition for OS #endif +#elif defined(__APPLE__) + #include + typedef _STRUCT_MCONTEXT64 SContext; + #define CTX_RAX __ss.__rax + #define CTX_RBX __ss.__rbx + #define CTX_RCX __ss.__rcx + #define CTX_RDX __ss.__rdx + #define CTX_RDI __ss.__rdi + #define CTX_RSI __ss.__rsi + #define CTX_RBP __ss.__rbp + #define CTX_RSP __ss.__rsp + #define CTX_R8 __ss.__r8 + #define CTX_R9 __ss.__r9 + #define CTX_R10 __ss.__r10 + #define CTX_R11 __ss.__r11 + #define CTX_R12 __ss.__r12 + #define CTX_R13 __ss.__r13 + #define CTX_R14 __ss.__r14 + #define CTX_R15 __ss.__r15 + #define CTX_RIP __ss.__rip #elif defined(__linux__) #include #if _M_X86_64 diff --git a/Source/Core/Core/x64MemTools.cpp b/Source/Core/Core/x64MemTools.cpp index 17e023731b..facc477d07 100644 --- a/Source/Core/Core/x64MemTools.cpp +++ b/Source/Core/Core/x64MemTools.cpp @@ -5,12 +5,9 @@ #include #include -#ifdef __APPLE__ -#include "Common/Thread.h" -#endif - #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" +#include "Common/Thread.h" #include "Common/x64Analyzer.h" #include "Core/MemTools.h" @@ -92,7 +89,7 @@ void InstallExceptionHandler() void UninstallExceptionHandler() {} -#elif defined(__APPLE__) +#elif defined(__APPLE__) && !defined(USE_SIGACTION_ON_APPLE) void CheckKR(const char* name, kern_return_t kr) { @@ -216,7 +213,7 @@ void UninstallExceptionHandler() {} static void sigsegv_handler(int sig, siginfo_t *info, void *raw_context) { - if (sig != SIGSEGV) + if (sig != SIGSEGV && sig != SIGBUS) { // We are not interested in other signals - handle it as usual. return; @@ -233,10 +230,19 @@ static void sigsegv_handler(int sig, siginfo_t *info, void *raw_context) // Get all the information we can out of the context. mcontext_t *ctx = &context->uc_mcontext; // assume it's not a write - if (!JitInterface::HandleFault(bad_address, ctx)) + if (!JitInterface::HandleFault(bad_address, +#ifdef __APPLE__ + *ctx +#else + ctx +#endif + )) { // retry and crash signal(SIGSEGV, SIG_DFL); +#ifdef __APPLE__ + signal(SIGBUS, SIG_DFL); +#endif } } @@ -254,6 +260,9 @@ void InstallExceptionHandler() sa.sa_flags = SA_SIGINFO; sigemptyset(&sa.sa_mask); sigaction(SIGSEGV, &sa, nullptr); +#ifdef __APPLE__ + sigaction(SIGBUS, &sa, nullptr); +#endif } void UninstallExceptionHandler() diff --git a/Source/UnitTests/Core/CMakeLists.txt b/Source/UnitTests/Core/CMakeLists.txt index 93f635a3ea..5bb809d9df 100644 --- a/Source/UnitTests/Core/CMakeLists.txt +++ b/Source/UnitTests/Core/CMakeLists.txt @@ -1 +1,2 @@ add_dolphin_test(MMIOTest MMIOTest.cpp) +add_dolphin_test(PageFaultTest PageFaultTest.cpp) diff --git a/Source/UnitTests/Core/PageFaultTest.cpp b/Source/UnitTests/Core/PageFaultTest.cpp new file mode 100644 index 0000000000..62a3e4bffa --- /dev/null +++ b/Source/UnitTests/Core/PageFaultTest.cpp @@ -0,0 +1,77 @@ +#include + +#include "Common/CommonTypes.h" +#include "Common/Timer.h" +#include "Core/MemTools.h" +#include "Core/PowerPC/JitCommon/JitBase.h" + +// include order is important +#include + +#if _M_X86_64 || _M_ARM_32 +enum +{ +#ifdef _WIN32 + PAGE_GRAN = 0x10000 +#else + PAGE_GRAN = 0x1000 +#endif +}; + +class PageFaultFakeJit : public JitBase +{ +public: + // CPUCoreBase methods + void Init() override {} + void Shutdown() override {} + void ClearCache() override {} + void Run() override {} + void SingleStep() override {} + const char *GetName() override { return nullptr; } + + // JitBase methods + JitBaseBlockCache *GetBlockCache() override { return nullptr; } + void Jit(u32 em_address) override {} + const CommonAsmRoutinesBase *GetAsmRoutines() override { return nullptr; } + + virtual bool HandleFault(uintptr_t access_address, SContext* ctx) override + { + m_pre_unprotect_time = std::chrono::high_resolution_clock::now(); + UnWriteProtectMemory(m_data, PAGE_GRAN, /*allowExecute*/ false); + m_post_unprotect_time = std::chrono::high_resolution_clock::now(); + return true; + } + + void* m_data; + std::chrono::time_point + m_pre_unprotect_time, m_post_unprotect_time; +}; + + +TEST(PageFault, PageFault) +{ + EMM::InstallExceptionHandler(); + void* data = AllocateMemoryPages(PAGE_GRAN); + EXPECT_NE(data, nullptr); + WriteProtectMemory(data, PAGE_GRAN, false); + + PageFaultFakeJit pfjit; + jit = &pfjit; + pfjit.m_data = data; + + auto start = std::chrono::high_resolution_clock::now(); + *(volatile int*) data = 5; + auto end = std::chrono::high_resolution_clock::now(); + + #define AS_NS(diff) ((unsigned long long)std::chrono::duration_cast(diff).count()) + + EMM::UninstallExceptionHandler(); + jit = nullptr; + + printf("page fault timing:\n"); + printf("start->HandleFault %llu ns\n", AS_NS(pfjit.m_pre_unprotect_time - start)); + printf("UnWriteProtectMemory %llu ns\n", AS_NS(pfjit.m_post_unprotect_time - pfjit.m_pre_unprotect_time)); + printf("HandleFault->end %llu ns\n", AS_NS(end - pfjit.m_post_unprotect_time)); + printf("total %llu ns\n", AS_NS(end - start)); +} +#endif