mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-04-27 07:05:42 -04:00
Misc: Purge unused code and report startup error to host
This commit is contained in:
parent
ca3cfbaa99
commit
fa104acdd1
@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#include "crash_handler.h"
|
#include "crash_handler.h"
|
||||||
|
#include "dynamic_library.h"
|
||||||
#include "file_system.h"
|
#include "file_system.h"
|
||||||
#include "string_util.h"
|
#include "string_util.h"
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
@ -74,7 +75,7 @@ static bool WriteMinidump(HMODULE hDbgHelp, HANDLE hFile, HANDLE hProcess, DWORD
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::wstring s_write_directory;
|
static std::wstring s_write_directory;
|
||||||
static HMODULE s_dbghelp_module = nullptr;
|
static DynamicLibrary s_dbghelp_module;
|
||||||
static PVOID s_veh_handle = nullptr;
|
static PVOID s_veh_handle = nullptr;
|
||||||
static bool s_in_crash_handler = false;
|
static bool s_in_crash_handler = false;
|
||||||
|
|
||||||
@ -115,8 +116,8 @@ static void WriteMinidumpAndCallstack(PEXCEPTION_POINTERS exi)
|
|||||||
MiniDumpWithThreadInfo | MiniDumpWithIndirectlyReferencedMemory);
|
MiniDumpWithThreadInfo | MiniDumpWithIndirectlyReferencedMemory);
|
||||||
const HANDLE hMinidumpFile = CreateFileW(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr);
|
const HANDLE hMinidumpFile = CreateFileW(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr);
|
||||||
if (hMinidumpFile == INVALID_HANDLE_VALUE ||
|
if (hMinidumpFile == INVALID_HANDLE_VALUE ||
|
||||||
!WriteMinidump(s_dbghelp_module, hMinidumpFile, GetCurrentProcess(), GetCurrentProcessId(), GetCurrentThreadId(),
|
!WriteMinidump(static_cast<HMODULE>(s_dbghelp_module.GetHandle()), hMinidumpFile, GetCurrentProcess(),
|
||||||
exi, minidump_type))
|
GetCurrentProcessId(), GetCurrentThreadId(), exi, minidump_type))
|
||||||
{
|
{
|
||||||
static const char error_message[] = "Failed to write minidump file.\n";
|
static const char error_message[] = "Failed to write minidump file.\n";
|
||||||
if (hFile != INVALID_HANDLE_VALUE)
|
if (hFile != INVALID_HANDLE_VALUE)
|
||||||
@ -170,7 +171,9 @@ bool CrashHandler::Install()
|
|||||||
{
|
{
|
||||||
// load dbghelp at install/startup, that way we're not LoadLibrary()'ing after a crash
|
// load dbghelp at install/startup, that way we're not LoadLibrary()'ing after a crash
|
||||||
// .. because that probably wouldn't go down well.
|
// .. because that probably wouldn't go down well.
|
||||||
s_dbghelp_module = StackWalker::LoadDbgHelpLibrary();
|
HMODULE mod = StackWalker::LoadDbgHelpLibrary();
|
||||||
|
if (mod)
|
||||||
|
s_dbghelp_module.Adopt(mod);
|
||||||
|
|
||||||
s_veh_handle = AddVectoredExceptionHandler(0, ExceptionHandler);
|
s_veh_handle = AddVectoredExceptionHandler(0, ExceptionHandler);
|
||||||
return (s_veh_handle != nullptr);
|
return (s_veh_handle != nullptr);
|
||||||
@ -189,21 +192,6 @@ void CrashHandler::WriteDumpForCaller()
|
|||||||
WriteMinidumpAndCallstack(nullptr);
|
WriteMinidumpAndCallstack(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandler::Uninstall()
|
|
||||||
{
|
|
||||||
if (s_veh_handle)
|
|
||||||
{
|
|
||||||
RemoveVectoredExceptionHandler(s_veh_handle);
|
|
||||||
s_veh_handle = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s_dbghelp_module)
|
|
||||||
{
|
|
||||||
FreeLibrary(s_dbghelp_module);
|
|
||||||
s_dbghelp_module = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(ENABLE_LIBBACKTRACE)
|
#elif defined(ENABLE_LIBBACKTRACE)
|
||||||
|
|
||||||
#include <backtrace.h>
|
#include <backtrace.h>
|
||||||
@ -392,11 +380,6 @@ void CrashHandler::WriteDumpForCaller()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandler::Uninstall()
|
|
||||||
{
|
|
||||||
// We can't really unchain the signal handlers... so, YOLO.
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
bool CrashHandler::Install()
|
bool CrashHandler::Install()
|
||||||
@ -412,8 +395,4 @@ void CrashHandler::WriteDumpForCaller()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandler::Uninstall()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -8,5 +8,4 @@ namespace CrashHandler {
|
|||||||
bool Install();
|
bool Install();
|
||||||
void SetWriteDirectory(std::string_view dump_directory);
|
void SetWriteDirectory(std::string_view dump_directory);
|
||||||
void WriteDumpForCaller();
|
void WriteDumpForCaller();
|
||||||
void Uninstall();
|
|
||||||
} // namespace CrashHandler
|
} // namespace CrashHandler
|
||||||
|
@ -101,6 +101,15 @@ bool DynamicLibrary::Open(const char* filename, Error* error)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DynamicLibrary::Adopt(void* handle)
|
||||||
|
{
|
||||||
|
AssertMsg(handle, "Handle is valid");
|
||||||
|
|
||||||
|
Close();
|
||||||
|
|
||||||
|
m_handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
void DynamicLibrary::Close()
|
void DynamicLibrary::Close()
|
||||||
{
|
{
|
||||||
if (!IsOpen())
|
if (!IsOpen())
|
||||||
|
@ -45,6 +45,9 @@ public:
|
|||||||
/// Returns true if the library was loaded and can be used.
|
/// Returns true if the library was loaded and can be used.
|
||||||
bool Open(const char* filename, Error* error);
|
bool Open(const char* filename, Error* error);
|
||||||
|
|
||||||
|
/// Adopts, or takes ownership of an existing opened library.
|
||||||
|
void Adopt(void* handle);
|
||||||
|
|
||||||
/// Unloads the library, any function pointers from this library are no longer valid.
|
/// Unloads the library, any function pointers from this library are no longer valid.
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
@ -61,6 +64,9 @@ public:
|
|||||||
return *ptr != nullptr;
|
return *ptr != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the opaque OS-specific handle.
|
||||||
|
void* GetHandle() const { return m_handle; }
|
||||||
|
|
||||||
/// Move assignment, transfer ownership.
|
/// Move assignment, transfer ownership.
|
||||||
DynamicLibrary& operator=(DynamicLibrary&& move);
|
DynamicLibrary& operator=(DynamicLibrary&& move);
|
||||||
|
|
||||||
|
@ -138,8 +138,8 @@ void Error::SetHResult(std::string_view prefix, long err)
|
|||||||
static_cast<DWORD>(std::size(buf)), nullptr);
|
static_cast<DWORD>(std::size(buf)), nullptr);
|
||||||
if (r > 0)
|
if (r > 0)
|
||||||
{
|
{
|
||||||
m_description =
|
m_description = fmt::format("{}HRESULT {:08X}: {}", prefix, static_cast<unsigned>(err),
|
||||||
fmt::format("{}HRESULT {:08X}: {}", prefix, err, StringUtil::WideStringToUTF8String(std::wstring_view(buf, r)));
|
StringUtil::WideStringToUTF8String(std::wstring_view(buf, r)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -174,21 +174,18 @@ static constexpr size_t TOTAL_SIZE = LUT_OFFSET + LUT_SIZE;
|
|||||||
#define FIXUP_WORD_WRITE_VALUE(size, offset, value) \
|
#define FIXUP_WORD_WRITE_VALUE(size, offset, value) \
|
||||||
((size == MemoryAccessSize::Word) ? (value) : ((value) << (((offset) & 3u) * 8)))
|
((size == MemoryAccessSize::Word) ? (value) : ((value) << (((offset) & 3u) * 8)))
|
||||||
|
|
||||||
bool Bus::AllocateMemory()
|
bool Bus::AllocateMemory(Error* error)
|
||||||
{
|
{
|
||||||
Error error;
|
|
||||||
s_shmem_handle =
|
s_shmem_handle =
|
||||||
MemMap::CreateSharedMemory(MemMap::GetFileMappingName("duckstation").c_str(), MemoryMap::TOTAL_SIZE, &error);
|
MemMap::CreateSharedMemory(MemMap::GetFileMappingName("duckstation").c_str(), MemoryMap::TOTAL_SIZE, error);
|
||||||
if (!s_shmem_handle)
|
if (!s_shmem_handle)
|
||||||
{
|
{
|
||||||
#ifndef __linux__
|
#ifndef __linux__
|
||||||
error.AddSuffix("\nYou may need to close some programs to free up additional memory.");
|
Error::AddSuffix(error, "\nYou may need to close some programs to free up additional memory.");
|
||||||
#else
|
#else
|
||||||
error.AddSuffix(
|
Error::AddSuffix(
|
||||||
"\nYou may need to close some programs to free up additional memory, or increase the size of /dev/shm.");
|
error, "\nYou may need to close some programs to free up additional memory, or increase the size of /dev/shm.");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Host::ReportFatalError("Memory Allocation Failed", error.GetDescription());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +195,7 @@ bool Bus::AllocateMemory()
|
|||||||
MemoryMap::RAM_SIZE, PageProtect::ReadWrite));
|
MemoryMap::RAM_SIZE, PageProtect::ReadWrite));
|
||||||
if (!g_ram || !g_unprotected_ram)
|
if (!g_ram || !g_unprotected_ram)
|
||||||
{
|
{
|
||||||
Host::ReportFatalError("Memory Allocation Failed", "Failed to map memory for RAM");
|
Error::SetStringView(error, "Failed to map memory for RAM");
|
||||||
ReleaseMemory();
|
ReleaseMemory();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -209,7 +206,7 @@ bool Bus::AllocateMemory()
|
|||||||
MemoryMap::BIOS_SIZE, PageProtect::ReadWrite));
|
MemoryMap::BIOS_SIZE, PageProtect::ReadWrite));
|
||||||
if (!g_bios)
|
if (!g_bios)
|
||||||
{
|
{
|
||||||
Host::ReportFatalError("Memory Allocation Failed", "Failed to map memory for BIOS");
|
Error::SetStringView(error, "Failed to map memory for BIOS");
|
||||||
ReleaseMemory();
|
ReleaseMemory();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -220,7 +217,7 @@ bool Bus::AllocateMemory()
|
|||||||
MemoryMap::LUT_SIZE, PageProtect::ReadWrite));
|
MemoryMap::LUT_SIZE, PageProtect::ReadWrite));
|
||||||
if (!g_memory_handlers)
|
if (!g_memory_handlers)
|
||||||
{
|
{
|
||||||
Host::ReportFatalError("Memory Allocation Failed", "Failed to map memory for LUTs");
|
Error::SetStringView(error, "Failed to map memory for LUTs");
|
||||||
ReleaseMemory();
|
ReleaseMemory();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -232,8 +229,7 @@ bool Bus::AllocateMemory()
|
|||||||
#ifdef ENABLE_MMAP_FASTMEM
|
#ifdef ENABLE_MMAP_FASTMEM
|
||||||
if (!s_fastmem_arena.Create(FASTMEM_ARENA_SIZE))
|
if (!s_fastmem_arena.Create(FASTMEM_ARENA_SIZE))
|
||||||
{
|
{
|
||||||
// TODO: maybe make this non-fatal?
|
Error::SetStringView(error, "Failed to create fastmem arena");
|
||||||
Host::ReportFatalError("Memory Allocation Failed", "Failed to create fastmem arena");
|
|
||||||
ReleaseMemory();
|
ReleaseMemory();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class Error;
|
||||||
|
|
||||||
class StateWrapper;
|
class StateWrapper;
|
||||||
|
|
||||||
namespace Bus {
|
namespace Bus {
|
||||||
@ -109,7 +111,7 @@ enum : u32
|
|||||||
static constexpr size_t FASTMEM_ARENA_SIZE = UINT64_C(0x100000000);
|
static constexpr size_t FASTMEM_ARENA_SIZE = UINT64_C(0x100000000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool AllocateMemory();
|
bool AllocateMemory(Error* error);
|
||||||
void ReleaseMemory();
|
void ReleaseMemory();
|
||||||
|
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
|
@ -172,12 +172,6 @@ std::optional<u32> Controller::GetBindIndex(ControllerType type, std::string_vie
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller::VibrationCapabilities Controller::GetControllerVibrationCapabilities(std::string_view type)
|
|
||||||
{
|
|
||||||
const ControllerInfo* info = GetControllerInfo(type);
|
|
||||||
return info ? info->vibration_caps : VibrationCapabilities::NoVibration;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<u32, u32> Controller::ConvertPadToPortAndSlot(u32 index)
|
std::tuple<u32, u32> Controller::ConvertPadToPortAndSlot(u32 index)
|
||||||
{
|
{
|
||||||
if (index > 4) // [5,6,7]
|
if (index > 4) // [5,6,7]
|
||||||
|
@ -104,9 +104,6 @@ public:
|
|||||||
/// Gets the integer code for an axis in the specified controller type.
|
/// Gets the integer code for an axis in the specified controller type.
|
||||||
static std::optional<u32> GetBindIndex(ControllerType type, std::string_view bind_name);
|
static std::optional<u32> GetBindIndex(ControllerType type, std::string_view bind_name);
|
||||||
|
|
||||||
/// Returns the vibration configuration for the specified controller type.
|
|
||||||
static VibrationCapabilities GetControllerVibrationCapabilities(std::string_view type);
|
|
||||||
|
|
||||||
/// Returns general information for the specified controller type.
|
/// Returns general information for the specified controller type.
|
||||||
static const ControllerInfo* GetControllerInfo(ControllerType type);
|
static const ControllerInfo* GetControllerInfo(ControllerType type);
|
||||||
static const ControllerInfo* GetControllerInfo(std::string_view name);
|
static const ControllerInfo* GetControllerInfo(std::string_view name);
|
||||||
|
@ -72,8 +72,6 @@ static void SetRegAccess(InstructionInfo* inst, Reg reg, bool write);
|
|||||||
static void AddBlockToPageList(Block* block);
|
static void AddBlockToPageList(Block* block);
|
||||||
static void RemoveBlockFromPageList(Block* block);
|
static void RemoveBlockFromPageList(Block* block);
|
||||||
|
|
||||||
static Common::PageFaultHandler::HandlerResult ExceptionHandler(void* exception_pc, void* fault_address, bool is_write);
|
|
||||||
|
|
||||||
static Block* CreateCachedInterpreterBlock(u32 pc);
|
static Block* CreateCachedInterpreterBlock(u32 pc);
|
||||||
[[noreturn]] static void ExecuteCachedInterpreter();
|
[[noreturn]] static void ExecuteCachedInterpreter();
|
||||||
template<PGXPMode pgxp_mode>
|
template<PGXPMode pgxp_mode>
|
||||||
@ -99,8 +97,7 @@ static void UnlinkBlockExits(Block* block);
|
|||||||
static void ClearASMFunctions();
|
static void ClearASMFunctions();
|
||||||
static void CompileASMFunctions();
|
static void CompileASMFunctions();
|
||||||
static bool CompileBlock(Block* block);
|
static bool CompileBlock(Block* block);
|
||||||
static Common::PageFaultHandler::HandlerResult HandleFastmemException(void* exception_pc, void* fault_address,
|
static PageFaultHandler::HandlerResult HandleFastmemException(void* exception_pc, void* fault_address, bool is_write);
|
||||||
bool is_write);
|
|
||||||
static void BackpatchLoadStore(void* host_pc, const LoadstoreBackpatchInfo& info);
|
static void BackpatchLoadStore(void* host_pc, const LoadstoreBackpatchInfo& info);
|
||||||
static void RemoveBackpatchInfoForRange(const void* host_code, u32 size);
|
static void RemoveBackpatchInfoForRange(const void* host_code, u32 size);
|
||||||
|
|
||||||
@ -165,7 +162,7 @@ bool CPU::CodeCache::IsUsingFastmem()
|
|||||||
return IsUsingAnyRecompiler() && g_settings.cpu_fastmem_mode != CPUFastmemMode::Disabled;
|
return IsUsingAnyRecompiler() && g_settings.cpu_fastmem_mode != CPUFastmemMode::Disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPU::CodeCache::ProcessStartup()
|
bool CPU::CodeCache::ProcessStartup(Error* error)
|
||||||
{
|
{
|
||||||
AllocateLUTs();
|
AllocateLUTs();
|
||||||
|
|
||||||
@ -178,24 +175,19 @@ bool CPU::CodeCache::ProcessStartup()
|
|||||||
#endif
|
#endif
|
||||||
if (!has_buffer && !s_code_buffer.Allocate(RECOMPILER_CODE_CACHE_SIZE, RECOMPILER_FAR_CODE_CACHE_SIZE))
|
if (!has_buffer && !s_code_buffer.Allocate(RECOMPILER_CODE_CACHE_SIZE, RECOMPILER_FAR_CODE_CACHE_SIZE))
|
||||||
{
|
{
|
||||||
Host::ReportFatalError("Error", "Failed to initialize code space");
|
Error::SetStringView(error, "Failed to initialize code space");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!Common::PageFaultHandler::InstallHandler(ExceptionHandler))
|
if (!PageFaultHandler::Install(error))
|
||||||
{
|
|
||||||
Host::ReportFatalError("Error", "Failed to install page fault handler");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::CodeCache::ProcessShutdown()
|
void CPU::CodeCache::ProcessShutdown()
|
||||||
{
|
{
|
||||||
Common::PageFaultHandler::RemoveHandler(ExceptionHandler);
|
|
||||||
|
|
||||||
#ifdef ENABLE_RECOMPILER_SUPPORT
|
#ifdef ENABLE_RECOMPILER_SUPPORT
|
||||||
s_code_buffer.Destroy();
|
s_code_buffer.Destroy();
|
||||||
#endif
|
#endif
|
||||||
@ -747,7 +739,7 @@ void CPU::CodeCache::ClearBlocks()
|
|||||||
std::memset(s_lut_block_pointers.get(), 0, sizeof(Block*) * GetLUTSlotCount(false));
|
std::memset(s_lut_block_pointers.get(), 0, sizeof(Block*) * GetLUTSlotCount(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::PageFaultHandler::HandlerResult CPU::CodeCache::ExceptionHandler(void* exception_pc, void* fault_address,
|
PageFaultHandler::HandlerResult PageFaultHandler::HandlePageFault(void* exception_pc, void* fault_address,
|
||||||
bool is_write)
|
bool is_write)
|
||||||
{
|
{
|
||||||
if (static_cast<const u8*>(fault_address) >= Bus::g_ram &&
|
if (static_cast<const u8*>(fault_address) >= Bus::g_ram &&
|
||||||
@ -759,14 +751,14 @@ Common::PageFaultHandler::HandlerResult CPU::CodeCache::ExceptionHandler(void* e
|
|||||||
const u32 page_index = Bus::GetRAMCodePageIndex(guest_address);
|
const u32 page_index = Bus::GetRAMCodePageIndex(guest_address);
|
||||||
Log_DevFmt("Page fault on protected RAM @ 0x{:08X} (page #{}), invalidating code cache.", guest_address,
|
Log_DevFmt("Page fault on protected RAM @ 0x{:08X} (page #{}), invalidating code cache.", guest_address,
|
||||||
page_index);
|
page_index);
|
||||||
InvalidateBlocksWithPageIndex(page_index);
|
CPU::CodeCache::InvalidateBlocksWithPageIndex(page_index);
|
||||||
return Common::PageFaultHandler::HandlerResult::ContinueExecution;
|
return PageFaultHandler::HandlerResult::ContinueExecution;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_RECOMPILER_SUPPORT
|
#ifdef ENABLE_RECOMPILER_SUPPORT
|
||||||
return HandleFastmemException(exception_pc, fault_address, is_write);
|
return CPU::CodeCache::HandleFastmemException(exception_pc, fault_address, is_write);
|
||||||
#else
|
#else
|
||||||
return Common::PageFaultHandler::HandlerResult::ExecuteNextHandler;
|
return PageFaultHandler::HandlerResult::ExecuteNextHandler;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1593,7 +1585,7 @@ void CPU::CodeCache::AddLoadStoreInfo(void* code_address, u32 code_size, u32 gue
|
|||||||
s_fastmem_backpatch_info.emplace(code_address, info);
|
s_fastmem_backpatch_info.emplace(code_address, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(void* exception_pc, void* fault_address,
|
PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(void* exception_pc, void* fault_address,
|
||||||
bool is_write)
|
bool is_write)
|
||||||
{
|
{
|
||||||
// TODO: Catch general RAM writes, not just fastmem
|
// TODO: Catch general RAM writes, not just fastmem
|
||||||
@ -1606,7 +1598,7 @@ Common::PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(v
|
|||||||
(static_cast<u8*>(fault_address) - static_cast<u8*>(g_state.fastmem_base)) >=
|
(static_cast<u8*>(fault_address) - static_cast<u8*>(g_state.fastmem_base)) >=
|
||||||
static_cast<ptrdiff_t>(Bus::FASTMEM_ARENA_SIZE))
|
static_cast<ptrdiff_t>(Bus::FASTMEM_ARENA_SIZE))
|
||||||
{
|
{
|
||||||
return Common::PageFaultHandler::HandlerResult::ExecuteNextHandler;
|
return PageFaultHandler::HandlerResult::ExecuteNextHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
guest_address = static_cast<PhysicalMemoryAddress>(
|
guest_address = static_cast<PhysicalMemoryAddress>(
|
||||||
@ -1618,7 +1610,7 @@ Common::PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(v
|
|||||||
{
|
{
|
||||||
Log_DevFmt("Ignoring fault due to RAM write @ 0x{:08X}", guest_address);
|
Log_DevFmt("Ignoring fault due to RAM write @ 0x{:08X}", guest_address);
|
||||||
InvalidateBlocksWithPageIndex(Bus::GetRAMCodePageIndex(guest_address));
|
InvalidateBlocksWithPageIndex(Bus::GetRAMCodePageIndex(guest_address));
|
||||||
return Common::PageFaultHandler::HandlerResult::ContinueExecution;
|
return PageFaultHandler::HandlerResult::ContinueExecution;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1635,7 +1627,7 @@ Common::PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(v
|
|||||||
if (iter == s_fastmem_backpatch_info.end())
|
if (iter == s_fastmem_backpatch_info.end())
|
||||||
{
|
{
|
||||||
Log_ErrorFmt("No backpatch info found for {}", exception_pc);
|
Log_ErrorFmt("No backpatch info found for {}", exception_pc);
|
||||||
return Common::PageFaultHandler::HandlerResult::ExecuteNextHandler;
|
return PageFaultHandler::HandlerResult::ExecuteNextHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadstoreBackpatchInfo& info = iter->second;
|
LoadstoreBackpatchInfo& info = iter->second;
|
||||||
@ -1670,7 +1662,7 @@ Common::PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(v
|
|||||||
// and store the pc in the faulting list, so that we don't emit another fastmem loadstore
|
// and store the pc in the faulting list, so that we don't emit another fastmem loadstore
|
||||||
s_fastmem_faulting_pcs.insert(info.guest_pc);
|
s_fastmem_faulting_pcs.insert(info.guest_pc);
|
||||||
s_fastmem_backpatch_info.erase(iter);
|
s_fastmem_backpatch_info.erase(iter);
|
||||||
return Common::PageFaultHandler::HandlerResult::ContinueExecution;
|
return PageFaultHandler::HandlerResult::ContinueExecution;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPU::CodeCache::HasPreviouslyFaultedOnPC(u32 guest_pc)
|
bool CPU::CodeCache::HasPreviouslyFaultedOnPC(u32 guest_pc)
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
#include "cpu_types.h"
|
#include "cpu_types.h"
|
||||||
|
|
||||||
|
class Error;
|
||||||
|
|
||||||
namespace CPU::CodeCache {
|
namespace CPU::CodeCache {
|
||||||
|
|
||||||
/// Returns true if any recompiler is in use.
|
/// Returns true if any recompiler is in use.
|
||||||
@ -15,7 +17,7 @@ bool IsUsingAnyRecompiler();
|
|||||||
bool IsUsingFastmem();
|
bool IsUsingFastmem();
|
||||||
|
|
||||||
/// Allocates resources, call once at startup.
|
/// Allocates resources, call once at startup.
|
||||||
bool ProcessStartup();
|
bool ProcessStartup(Error* error);
|
||||||
|
|
||||||
/// Frees resources, call once at shutdown.
|
/// Frees resources, call once at shutdown.
|
||||||
void ProcessShutdown();
|
void ProcessShutdown();
|
||||||
|
@ -254,7 +254,7 @@ static TinyString GetTimestampStringForFileName()
|
|||||||
return TinyString::from_format("{:%Y-%m-%d-%H-%M-%S}", fmt::localtime(std::time(nullptr)));
|
return TinyString::from_format("{:%Y-%m-%d-%H-%M-%S}", fmt::localtime(std::time(nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::Internal::CPUThreadInitialize()
|
bool System::Internal::CPUThreadInitialize(Error* error)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// On Win32, we have a bunch of things which use COM (e.g. SDL, Cubeb, etc).
|
// On Win32, we have a bunch of things which use COM (e.g. SDL, Cubeb, etc).
|
||||||
@ -263,15 +263,15 @@ bool System::Internal::CPUThreadInitialize()
|
|||||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
Host::ReportErrorAsync("Error", fmt::format("CoInitializeEx() failed: {:08X}", static_cast<unsigned>(hr)));
|
Error::SetHResult(error, "CoInitializeEx() failed: ", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!Bus::AllocateMemory())
|
if (!Bus::AllocateMemory(error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!CPU::CodeCache::ProcessStartup())
|
if (!CPU::CodeCache::ProcessStartup(error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// This will call back to Host::LoadSettings() -> ReloadSources().
|
// This will call back to Host::LoadSettings() -> ReloadSources().
|
||||||
|
@ -490,7 +490,7 @@ void UpdateDiscordPresence(bool update_session_time);
|
|||||||
|
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
/// Called on process startup.
|
/// Called on process startup.
|
||||||
bool CPUThreadInitialize();
|
bool CPUThreadInitialize(Error* error);
|
||||||
|
|
||||||
/// Called on process shutdown.
|
/// Called on process shutdown.
|
||||||
void CPUThreadShutdown();
|
void CPUThreadShutdown();
|
||||||
|
@ -1639,11 +1639,15 @@ void EmuThread::run()
|
|||||||
m_started_semaphore.release();
|
m_started_semaphore.release();
|
||||||
|
|
||||||
// input source setup must happen on emu thread
|
// input source setup must happen on emu thread
|
||||||
if (!System::Internal::CPUThreadInitialize())
|
|
||||||
{
|
{
|
||||||
|
Error startup_error;
|
||||||
|
if (!System::Internal::CPUThreadInitialize(&startup_error))
|
||||||
|
{
|
||||||
|
Host::ReportFatalError("Fatal Startup Error", startup_error.GetDescription());
|
||||||
moveToThread(m_ui_thread);
|
moveToThread(m_ui_thread);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// bind buttons/axises
|
// bind buttons/axises
|
||||||
createBackgroundControllerPollTimer();
|
createBackgroundControllerPollTimer();
|
||||||
|
@ -658,8 +658,14 @@ int main(int argc, char* argv[])
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!System::Internal::CPUThreadInitialize())
|
{
|
||||||
|
Error startup_error;
|
||||||
|
if (!System::Internal::CPUThreadInitialize(&startup_error))
|
||||||
|
{
|
||||||
|
Log_ErrorFmt("CPUThreadInitialize() failed: {}", startup_error.GetDescription());
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RegTestHost::HookSignals();
|
RegTestHost::HookSignals();
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#include "page_fault_handler.h"
|
#include "page_fault_handler.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/error.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -11,19 +12,15 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
Log_SetChannel(Common::PageFaultHandler);
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include "common/windows_headers.h"
|
#include "common/windows_headers.h"
|
||||||
#elif defined(__linux__) || defined(__ANDROID__)
|
#elif defined(__linux__) || defined(__ANDROID__)
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <ucontext.h>
|
#include <ucontext.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#define USE_SIGSEGV 1
|
|
||||||
#elif defined(__APPLE__) || defined(__FreeBSD__)
|
#elif defined(__APPLE__) || defined(__FreeBSD__)
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#define USE_SIGSEGV 1
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
@ -32,14 +29,14 @@ Log_SetChannel(Common::PageFaultHandler);
|
|||||||
#include <mach/task.h>
|
#include <mach/task.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Common::PageFaultHandler {
|
namespace PageFaultHandler {
|
||||||
|
|
||||||
static std::recursive_mutex s_exception_handler_mutex;
|
static std::recursive_mutex s_exception_handler_mutex;
|
||||||
static Handler s_exception_handler_callback;
|
static bool s_in_exception_handler = false;
|
||||||
static bool s_in_exception_handler;
|
static bool s_installed = false;
|
||||||
|
} // namespace PageFaultHandler
|
||||||
|
|
||||||
#if defined(CPU_ARCH_ARM64)
|
#if defined(CPU_ARCH_ARM64)
|
||||||
static bool IsStoreInstruction(const void* ptr)
|
[[maybe_unused]] static bool IsStoreInstruction(const void* ptr)
|
||||||
{
|
{
|
||||||
u32 bits;
|
u32 bits;
|
||||||
std::memcpy(&bits, ptr, sizeof(bits));
|
std::memcpy(&bits, ptr, sizeof(bits));
|
||||||
@ -74,7 +71,7 @@ static bool IsStoreInstruction(const void* ptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#elif defined(CPU_ARCH_RISCV64)
|
#elif defined(CPU_ARCH_RISCV64)
|
||||||
static bool IsStoreInstruction(const void* ptr)
|
[[maybe_unused]] static bool IsStoreInstruction(const void* ptr)
|
||||||
{
|
{
|
||||||
u32 bits;
|
u32 bits;
|
||||||
std::memcpy(&bits, ptr, sizeof(bits));
|
std::memcpy(&bits, ptr, sizeof(bits));
|
||||||
@ -83,10 +80,13 @@ static bool IsStoreInstruction(const void* ptr)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_WIN32) && (defined(CPU_ARCH_X64) || defined(CPU_ARCH_ARM64))
|
#if defined(_WIN32)
|
||||||
static PVOID s_veh_handle;
|
|
||||||
|
|
||||||
static LONG ExceptionHandler(PEXCEPTION_POINTERS exi)
|
namespace PageFaultHandler {
|
||||||
|
static LONG ExceptionHandler(PEXCEPTION_POINTERS exi);
|
||||||
|
}
|
||||||
|
|
||||||
|
LONG PageFaultHandler::ExceptionHandler(PEXCEPTION_POINTERS exi)
|
||||||
{
|
{
|
||||||
// Executing the handler concurrently from multiple threads wouldn't go down well.
|
// Executing the handler concurrently from multiple threads wouldn't go down well.
|
||||||
std::unique_lock lock(s_exception_handler_mutex);
|
std::unique_lock lock(s_exception_handler_mutex);
|
||||||
@ -112,21 +112,42 @@ static LONG ExceptionHandler(PEXCEPTION_POINTERS exi)
|
|||||||
|
|
||||||
s_in_exception_handler = true;
|
s_in_exception_handler = true;
|
||||||
|
|
||||||
const HandlerResult handled = s_exception_handler_callback(exception_pc, exception_address, is_write);
|
const HandlerResult handled = HandlePageFault(exception_pc, exception_address, is_write);
|
||||||
|
|
||||||
s_in_exception_handler = false;
|
s_in_exception_handler = false;
|
||||||
|
|
||||||
return (handled == HandlerResult::ContinueExecution) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
|
return (handled == HandlerResult::ContinueExecution) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(USE_SIGSEGV)
|
bool PageFaultHandler::Install(Error* error)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(s_exception_handler_mutex);
|
||||||
|
AssertMsg(!s_installed, "Page fault handler has already been installed.");
|
||||||
|
|
||||||
|
PVOID handle = AddVectoredExceptionHandler(1, ExceptionHandler);
|
||||||
|
if (!handle)
|
||||||
|
{
|
||||||
|
Error::SetWin32(error, "AddVectoredExceptionHandler() failed: ", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_installed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
namespace PageFaultHandler {
|
||||||
|
static void SignalHandler(int sig, siginfo_t* info, void* ctx);
|
||||||
|
static void CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx);
|
||||||
|
|
||||||
static struct sigaction s_old_sigsegv_action;
|
static struct sigaction s_old_sigsegv_action;
|
||||||
#if defined(__APPLE__) || defined(__aarch64__)
|
#if defined(__APPLE__) || defined(__aarch64__)
|
||||||
static struct sigaction s_old_sigbus_action;
|
static struct sigaction s_old_sigbus_action;
|
||||||
#endif
|
#endif
|
||||||
|
} // namespace PageFaultHandler
|
||||||
|
|
||||||
static void CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx)
|
void PageFaultHandler::CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx)
|
||||||
{
|
{
|
||||||
#if defined(__aarch64__)
|
#if defined(__aarch64__)
|
||||||
const struct sigaction& sa = (signal == SIGBUS) ? s_old_sigbus_action : s_old_sigsegv_action;
|
const struct sigaction& sa = (signal == SIGBUS) ? s_old_sigbus_action : s_old_sigsegv_action;
|
||||||
@ -152,7 +173,7 @@ static void CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SignalHandler(int sig, siginfo_t* info, void* ctx)
|
void PageFaultHandler::SignalHandler(int sig, siginfo_t* info, void* ctx)
|
||||||
{
|
{
|
||||||
// Executing the handler concurrently from multiple threads wouldn't go down well.
|
// Executing the handler concurrently from multiple threads wouldn't go down well.
|
||||||
std::unique_lock lock(s_exception_handler_mutex);
|
std::unique_lock lock(s_exception_handler_mutex);
|
||||||
@ -222,7 +243,7 @@ static void SignalHandler(int sig, siginfo_t* info, void* ctx)
|
|||||||
|
|
||||||
s_in_exception_handler = true;
|
s_in_exception_handler = true;
|
||||||
|
|
||||||
const HandlerResult result = s_exception_handler_callback(exception_pc, exception_address, is_write);
|
const HandlerResult result = HandlePageFault(exception_pc, exception_address, is_write);
|
||||||
|
|
||||||
s_in_exception_handler = false;
|
s_in_exception_handler = false;
|
||||||
|
|
||||||
@ -235,22 +256,11 @@ static void SignalHandler(int sig, siginfo_t* info, void* ctx)
|
|||||||
CallExistingSignalHandler(sig, info, ctx);
|
CallExistingSignalHandler(sig, info, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
bool PageFaultHandler::Install(Error* error)
|
||||||
|
|
||||||
bool InstallHandler(Handler handler)
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock(s_exception_handler_mutex);
|
std::unique_lock lock(s_exception_handler_mutex);
|
||||||
AssertMsg(!s_exception_handler_callback, "A page fault handler is already registered.");
|
AssertMsg(!s_installed, "Page fault handler has already been installed.");
|
||||||
if (!s_exception_handler_callback)
|
|
||||||
{
|
|
||||||
#if defined(_WIN32) && (defined(CPU_ARCH_X64) || defined(CPU_ARCH_ARM64))
|
|
||||||
s_veh_handle = AddVectoredExceptionHandler(1, ExceptionHandler);
|
|
||||||
if (!s_veh_handle)
|
|
||||||
{
|
|
||||||
Log_ErrorPrint("Failed to add vectored exception handler");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#elif defined(USE_SIGSEGV)
|
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
@ -261,52 +271,24 @@ bool InstallHandler(Handler handler)
|
|||||||
sa.sa_flags |= SA_NODEFER;
|
sa.sa_flags |= SA_NODEFER;
|
||||||
#endif
|
#endif
|
||||||
if (sigaction(SIGSEGV, &sa, &s_old_sigsegv_action) != 0)
|
if (sigaction(SIGSEGV, &sa, &s_old_sigsegv_action) != 0)
|
||||||
|
{
|
||||||
|
Error::SetErrno(error, "sigaction() for SIGSEGV failed: ", errno);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
#if defined(__APPLE__) || defined(__aarch64__)
|
#if defined(__APPLE__) || defined(__aarch64__)
|
||||||
// MacOS uses SIGBUS for memory permission violations
|
// MacOS uses SIGBUS for memory permission violations
|
||||||
if (sigaction(SIGBUS, &sa, &s_old_sigbus_action) != 0)
|
if (sigaction(SIGBUS, &sa, &s_old_sigbus_action) != 0)
|
||||||
|
{
|
||||||
|
Error::SetErrno(error, "sigaction() for SIGBUS failed: ", errno);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, 0);
|
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, 0);
|
||||||
#endif
|
#endif
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
s_exception_handler_callback = handler;
|
s_installed = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RemoveHandler(Handler handler)
|
|
||||||
{
|
|
||||||
std::unique_lock lock(s_exception_handler_mutex);
|
|
||||||
AssertMsg(!s_exception_handler_callback || s_exception_handler_callback == handler,
|
|
||||||
"Not removing the same handler previously registered.");
|
|
||||||
if (!s_exception_handler_callback)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
s_exception_handler_callback = nullptr;
|
|
||||||
|
|
||||||
#if defined(_WIN32) && (defined(CPU_ARCH_X64) || defined(CPU_ARCH_ARM64))
|
|
||||||
RemoveVectoredExceptionHandler(s_veh_handle);
|
|
||||||
s_veh_handle = nullptr;
|
|
||||||
#elif defined(USE_SIGSEGV)
|
|
||||||
struct sigaction sa;
|
|
||||||
#if defined(__APPLE__) || defined(__aarch64__)
|
|
||||||
sigaction(SIGBUS, &s_old_sigbus_action, &sa);
|
|
||||||
s_old_sigbus_action = {};
|
|
||||||
#endif
|
#endif
|
||||||
#if !defined(__APPLE__) || defined(__aarch64__)
|
|
||||||
sigaction(SIGSEGV, &s_old_sigsegv_action, &sa);
|
|
||||||
s_old_sigsegv_action = {};
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Common::PageFaultHandler
|
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
namespace Common::PageFaultHandler {
|
class Error;
|
||||||
|
|
||||||
|
namespace PageFaultHandler {
|
||||||
enum class HandlerResult
|
enum class HandlerResult
|
||||||
{
|
{
|
||||||
ContinueExecution,
|
ContinueExecution,
|
||||||
ExecuteNextHandler,
|
ExecuteNextHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
using Handler = HandlerResult (*)(void* exception_pc, void* fault_address, bool is_write);
|
HandlerResult HandlePageFault(void* exception_pc, void* fault_address, bool is_write);
|
||||||
|
bool Install(Error* error = nullptr);
|
||||||
bool InstallHandler(Handler callback);
|
} // namespace PageFaultHandler
|
||||||
bool RemoveHandler(Handler callback);
|
|
||||||
|
|
||||||
} // namespace Common::PageFaultHandler
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user