2015-05-23 22:32:32 -06:00
|
|
|
// Copyright 2014 Dolphin Emulator Project
|
2021-07-04 19:22:19 -06:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2015-05-23 22:32:32 -06:00
|
|
|
|
2014-10-19 18:31:15 -06:00
|
|
|
#include <chrono>
|
2022-06-13 18:20:13 -06:00
|
|
|
#include <fmt/format.h>
|
2014-10-19 18:31:15 -06:00
|
|
|
|
|
|
|
#include "Common/CommonTypes.h"
|
2023-08-16 15:28:33 -06:00
|
|
|
#include "Common/ScopeGuard.h"
|
2014-10-19 18:31:15 -06:00
|
|
|
#include "Common/Timer.h"
|
2023-08-16 15:28:33 -06:00
|
|
|
#include "Core/Core.h"
|
2014-10-19 18:31:15 -06:00
|
|
|
#include "Core/MemTools.h"
|
|
|
|
#include "Core/PowerPC/JitCommon/JitBase.h"
|
2018-12-02 06:16:17 -07:00
|
|
|
#include "Core/PowerPC/JitInterface.h"
|
2023-03-19 18:30:29 -06:00
|
|
|
#include "Core/System.h"
|
2014-10-19 18:31:15 -06:00
|
|
|
|
|
|
|
// include order is important
|
2015-03-01 06:52:09 -07:00
|
|
|
#include <gtest/gtest.h> // NOLINT
|
2014-10-19 18:31:15 -06:00
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
PAGE_GRAN = 0x10000
|
|
|
|
#else
|
|
|
|
PAGE_GRAN = 0x1000
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
class PageFaultFakeJit : public JitBase
|
|
|
|
{
|
|
|
|
public:
|
2023-03-19 18:30:29 -06:00
|
|
|
explicit PageFaultFakeJit(Core::System& system) : JitBase(system) {}
|
|
|
|
|
2014-10-19 18:31:15 -06:00
|
|
|
// CPUCoreBase methods
|
|
|
|
void Init() override {}
|
|
|
|
void Shutdown() override {}
|
|
|
|
void ClearCache() override {}
|
|
|
|
void Run() override {}
|
|
|
|
void SingleStep() override {}
|
2018-03-24 13:59:20 -06:00
|
|
|
const char* GetName() const override { return nullptr; }
|
2014-10-19 18:31:15 -06:00
|
|
|
// JitBase methods
|
|
|
|
JitBaseBlockCache* GetBlockCache() override { return nullptr; }
|
|
|
|
void Jit(u32 em_address) override {}
|
2024-07-26 23:28:44 -06:00
|
|
|
void EraseSingleBlock(const JitBlock&) override {}
|
2024-07-27 00:59:19 -06:00
|
|
|
std::vector<MemoryStats> GetMemoryStats() const override { return {}; }
|
2024-04-15 22:52:40 -06:00
|
|
|
std::size_t DisassembleNearCode(const JitBlock&, std::ostream&) const override { return 0; }
|
|
|
|
std::size_t DisassembleFarCode(const JitBlock&, std::ostream&) const override { return 0; }
|
2014-10-19 18:31:15 -06:00
|
|
|
const CommonAsmRoutinesBase* GetAsmRoutines() override { return nullptr; }
|
|
|
|
virtual bool HandleFault(uintptr_t access_address, SContext* ctx) override
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
2014-10-19 18:31:15 -06:00
|
|
|
m_pre_unprotect_time = std::chrono::high_resolution_clock::now();
|
2016-08-07 11:03:07 -06:00
|
|
|
Common::UnWriteProtectMemory(m_data, PAGE_GRAN, /*allowExecute*/ false);
|
2014-10-19 18:31:15 -06:00
|
|
|
m_post_unprotect_time = std::chrono::high_resolution_clock::now();
|
|
|
|
return true;
|
2016-06-24 02:43:46 -06:00
|
|
|
}
|
2014-10-19 18:31:15 -06:00
|
|
|
|
2021-09-03 22:43:19 -06:00
|
|
|
void* m_data = nullptr;
|
2014-10-19 18:31:15 -06:00
|
|
|
std::chrono::time_point<std::chrono::high_resolution_clock> m_pre_unprotect_time,
|
|
|
|
m_post_unprotect_time;
|
|
|
|
};
|
|
|
|
|
2020-08-18 22:54:08 -06:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#define ASAN_DISABLE __declspec(no_sanitize_address)
|
|
|
|
#else
|
|
|
|
#define ASAN_DISABLE
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void ASAN_DISABLE perform_invalid_access(void* data)
|
|
|
|
{
|
|
|
|
*(volatile int*)data = 5;
|
|
|
|
}
|
|
|
|
|
2014-10-19 18:31:15 -06:00
|
|
|
TEST(PageFault, PageFault)
|
|
|
|
{
|
2022-05-19 13:17:46 -06:00
|
|
|
if (!EMM::IsExceptionHandlerSupported())
|
2023-08-29 13:07:03 -06:00
|
|
|
GTEST_SKIP() << "Skipping PageFault test because exception handler is unsupported.";
|
|
|
|
|
2014-10-19 18:31:15 -06:00
|
|
|
EMM::InstallExceptionHandler();
|
2016-08-07 11:03:07 -06:00
|
|
|
void* data = Common::AllocateMemoryPages(PAGE_GRAN);
|
2014-10-19 18:31:15 -06:00
|
|
|
EXPECT_NE(data, nullptr);
|
2016-08-07 11:03:07 -06:00
|
|
|
Common::WriteProtectMemory(data, PAGE_GRAN, false);
|
2014-10-19 18:31:15 -06:00
|
|
|
|
2023-08-16 15:28:33 -06:00
|
|
|
Core::DeclareAsCPUThread();
|
|
|
|
Common::ScopeGuard cpu_thread_guard([] { Core::UndeclareAsCPUThread(); });
|
|
|
|
|
2023-03-25 09:17:46 -06:00
|
|
|
auto& system = Core::System::GetInstance();
|
|
|
|
auto unique_pfjit = std::make_unique<PageFaultFakeJit>(system);
|
|
|
|
auto& pfjit = *unique_pfjit;
|
|
|
|
system.GetJitInterface().SetJit(std::move(unique_pfjit));
|
2014-10-19 18:31:15 -06:00
|
|
|
pfjit.m_data = data;
|
|
|
|
|
|
|
|
auto start = std::chrono::high_resolution_clock::now();
|
2020-08-18 22:54:08 -06:00
|
|
|
perform_invalid_access(data);
|
2014-10-19 18:31:15 -06:00
|
|
|
auto end = std::chrono::high_resolution_clock::now();
|
|
|
|
|
2022-06-13 18:20:13 -06:00
|
|
|
auto difference_in_nanoseconds = [](auto start, auto end) {
|
|
|
|
return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
|
|
|
|
};
|
2014-10-19 18:31:15 -06:00
|
|
|
|
|
|
|
EMM::UninstallExceptionHandler();
|
|
|
|
|
2022-06-13 18:20:13 -06:00
|
|
|
fmt::print("page fault timing:\n");
|
|
|
|
fmt::print("start->HandleFault {} ns\n",
|
|
|
|
difference_in_nanoseconds(start, pfjit.m_pre_unprotect_time));
|
|
|
|
fmt::print("UnWriteProtectMemory {} ns\n",
|
|
|
|
difference_in_nanoseconds(pfjit.m_pre_unprotect_time, pfjit.m_post_unprotect_time));
|
|
|
|
fmt::print("HandleFault->end {} ns\n",
|
|
|
|
difference_in_nanoseconds(pfjit.m_post_unprotect_time, end));
|
|
|
|
fmt::print("total {} ns\n", difference_in_nanoseconds(start, end));
|
2023-03-25 09:17:46 -06:00
|
|
|
|
|
|
|
system.GetJitInterface().SetJit(nullptr);
|
2014-10-19 18:31:15 -06:00
|
|
|
}
|