diff --git a/FEXCore/Source/Interface/Config/Config.json.in b/FEXCore/Source/Interface/Config/Config.json.in index 103229418..9668a6074 100644 --- a/FEXCore/Source/Interface/Config/Config.json.in +++ b/FEXCore/Source/Interface/Config/Config.json.in @@ -16,6 +16,13 @@ "Maximum number of instruction to store in a block" ] }, + "EnableCodeCachingWIP": { + "Type": "bool", + "Default": "false", + "Desc": [ + "Enable the code caching subsystem" + ] + }, "HostFeatures": { "Type": "strenum", "Default": "FEXCore::Config::HostFeatures::OFF", diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index e8e8926b1..169b3e181 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -156,6 +156,16 @@ public: return CodeCache; } + void SetCodeMapWriter(fextl::unique_ptr Writer) override { + CodeMapWriter = std::move(Writer); + } + + void FlushAndCloseCodeMap() override { + if (CodeMapWriter) { + CodeMapWriter.reset(); + } + } + void OnCodeBufferAllocated(const std::shared_ptr&) override; void ClearCodeCache(FEXCore::Core::InternalThreadState* Thread, bool NewCodeBuffer = true) override; void InvalidateCodeBuffersCodeRange(uint64_t Start, uint64_t Length) override; @@ -226,6 +236,7 @@ public: FEXCore::ThunkHandler* ThunkHandler {}; fextl::unique_ptr Dispatcher; CodeCache CodeCache; + fextl::unique_ptr CodeMapWriter; SignalDelegator* SignalDelegation {}; diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index 282ecabba..fa929b7b7 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -437,6 +437,10 @@ void ContextImpl::UnlockAfterFork(FEXCore::Core::InternalThreadState* LiveThread Profiler::PostForkAction(Child); if (Child) { + if (CodeMapWriter) { + CodeMapWriter->ResetAfterFork(); + } + CodeInvalidationMutex.StealAndDropActiveLocks(); if (Config.StrictInProcessSplitLocks) { StrictSplitLockMutex = 0; @@ -720,7 +724,8 @@ ContextImpl::CompileCodeResult ContextImpl::CompileCode(FEXCore::Core::InternalT if (SourcecodeResolver && Config.GDBSymbols()) { auto MappedSection = SyscallHandler->LookupExecutableFileSection(*Thread, GuestRIP); if (MappedSection) { - MappedSection->FileInfo.SourcecodeMap = SourcecodeResolver->GenerateMap(MappedSection->FileInfo.Filename, MappedSection->FileInfo.FileId); + MappedSection->FileInfo.SourcecodeMap = + SourcecodeResolver->GenerateMap(MappedSection->FileInfo.Filename, CodeMap::GetBaseFilename(MappedSection->FileInfo, false)); } } @@ -859,6 +864,13 @@ uintptr_t ContextImpl::CompileBlock(FEXCore::Core::CpuStateFrame* Frame, uint64_ Thread->LookupCache->AddBlockMapping(Thread, GuestAddr, CodePages, HostAddr); } + if (CodeMapWriter) { + auto Region = SyscallHandler->LookupExecutableFileSection(*Thread, GuestRIP); + if (Region && Region->FileStartVA != 0) { + CodeMapWriter->AppendBlock(*Region, GuestRIP); + } + } + return (uintptr_t)CodePtr; } diff --git a/FEXCore/include/FEXCore/Core/Context.h b/FEXCore/include/FEXCore/Core/Context.h index 6b44a30bd..be1b5c1cb 100644 --- a/FEXCore/include/FEXCore/Core/Context.h +++ b/FEXCore/include/FEXCore/Core/Context.h @@ -136,6 +136,8 @@ public: FEX_DEFAULT_VISIBILITY virtual FEXCore::CPUID::FunctionResults RunCPUIDFunctionName(uint32_t Function, uint32_t Leaf, uint32_t CPU) = 0; virtual AbstractCodeCache& GetCodeCache() = 0; + virtual void SetCodeMapWriter(fextl::unique_ptr) = 0; + virtual void FlushAndCloseCodeMap() = 0; FEX_DEFAULT_VISIBILITY virtual void ClearCodeCache(FEXCore::Core::InternalThreadState* Thread, bool NewCodeBuffer = true) = 0; FEX_DEFAULT_VISIBILITY virtual void InvalidateCodeBuffersCodeRange(uint64_t Start, uint64_t Length) = 0; diff --git a/Source/Tools/FEXInterpreter/FEXInterpreter.cpp b/Source/Tools/FEXInterpreter/FEXInterpreter.cpp index 7e28d1bcd..915c24d11 100644 --- a/Source/Tools/FEXInterpreter/FEXInterpreter.cpp +++ b/Source/Tools/FEXInterpreter/FEXInterpreter.cpp @@ -475,6 +475,9 @@ int main(int argc, char** argv, char** const envp) { auto SyscallHandler = Loader.Is64BitMode() ? FEX::HLE::x64::CreateHandler(CTX.get(), SignalDelegation.get(), ThunkHandler.get()) : FEX::HLE::x32::CreateHandler(CTX.get(), SignalDelegation.get(), ThunkHandler.get(), std::move(Allocator)); + if (FEXCore::Config::Get_ENABLECODECACHINGWIP()) { + CTX->SetCodeMapWriter(fextl::make_unique(*SyscallHandler)); + } // Load VDSO in to memory prior to mapping our ELFs. auto VDSOMapping = FEX::VDSO::LoadVDSOThunks(Loader.Is64BitMode(), SyscallHandler.get()); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator.cpp index 71c6dd4f8..364e132d5 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator.cpp @@ -712,6 +712,7 @@ void SignalDelegator::HandleGuestSignal(FEX::HLE::ThreadStateObject* ThreadObjec } else if (Handler.OldAction.handler == SIG_IGN || (Handler.OldAction.handler == SIG_DFL && Handler.DefaultBehaviour == DEFAULT_IGNORE)) { // Do nothing } else if (Handler.OldAction.handler == SIG_DFL && (Handler.DefaultBehaviour == DEFAULT_COREDUMP || Handler.DefaultBehaviour == DEFAULT_TERM)) { + CTX->FlushAndCloseCodeMap(); #ifndef FEX_DISABLE_TELEMETRY // In the case of signals that cause coredump or terminate, save telemetry early. diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp index d8d2795bf..8e632ed11 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp @@ -197,6 +197,8 @@ static fextl::string GetShebangInterpFilename(const fextl::string& Filename) { uint64_t ExecveHandler(FEXCore::Core::CpuStateFrame* Frame, const char* pathname, char* const* argv, char* const* envp, ExecveAtArgs Args) { auto SyscallHandler = FEX::HLE::_SyscallHandler; + Frame->Thread->CTX->FlushAndCloseCodeMap(); + fextl::string Filename {}; fextl::string RootFS = SyscallHandler->RootFSPath(); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h index 9a9f312ff..f6bcc4aab 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h @@ -101,7 +101,10 @@ struct ExecveAtArgs { uint64_t ExecveHandler(FEXCore::Core::CpuStateFrame* Frame, const char* pathname, char* const* argv, char* const* envp, ExecveAtArgs Args); -class SyscallHandler : public FEXCore::HLE::SyscallHandler, FEXCore::HLE::SourcecodeResolver, public FEXCore::Allocator::FEXAllocOperators { +class SyscallHandler : public FEXCore::HLE::SyscallHandler, + FEXCore::HLE::SourcecodeResolver, + public FEXCore::CodeMapOpener, + public FEXCore::Allocator::FEXAllocOperators { public: ThreadManager TM; FEX::HLE::SeccompEmulator SeccompEmulator; @@ -277,6 +280,8 @@ public: std::optional LookupExecutableFileSection(FEXCore::Core::InternalThreadState& Thread, uint64_t GuestAddr) final override; + int OpenCodeMapFile() override; + FEXCore::HLE::ExecutableRangeInfo QueryGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Address) override; ///// FORK tracking ///// diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/Thread.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/Thread.cpp index c99f7b408..47e1d0c97 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/Thread.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/Thread.cpp @@ -566,6 +566,8 @@ void RegisterThread(FEX::HLE::SyscallHandler* Handler) { }); REGISTER_SYSCALL_IMPL(exit_group, [](FEXCore::Core::CpuStateFrame* Frame, int status) -> uint64_t { + Frame->Thread->CTX->FlushAndCloseCodeMap(); + // Save telemetry if we're exiting. FEX::HLE::_SyscallHandler->GetSignalDelegator()->SaveTelemetry(); FEX::HLE::_SyscallHandler->TM.CleanupForExit(); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index eeb52f460..cbb89f4dc 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -8,6 +8,7 @@ $end_info$ */ #include "Common/FDUtils.h" +#include "Common/FEXServerClient.h" #include "Common/FileMappingBaseAddress.h" #include @@ -312,6 +313,34 @@ uint64_t SyscallHandler::GuestMremap(bool Is64Bit, FEXCore::Core::InternalThread return Result; } +int SyscallHandler::OpenCodeMapFile() { + // Query from FEXServer whether this is the first instance of this executable; if it is, also enable code dumping! + FEX_CONFIG_OPT(RootFSPath, ROOTFS); + FEX_CONFIG_OPT(Multiblock, MULTIBLOCK); + auto ProgramName = FEXCore::Config::Get(FEXCore::Config::CONFIG_APP_FILENAME); + LOGMAN_THROW_A_FMT(ProgramName && ProgramName.value()->c_str()[0] == '/', ""); + + // Check RootFS first, then the plain path + auto ProgramFD = open((RootFSPath() + ProgramName.value()->c_str()).c_str(), O_RDONLY); + if (ProgramFD == -1) { + ProgramFD = open(ProgramName.value()->c_str(), O_RDONLY); + } + if (ProgramFD == -1) { + return -1; + } + + int CodeMapFD = FEXServerClient::RequestCodeMapFD(FEXServerClient::GetServerFD(), ProgramFD, Multiblock); + close(ProgramFD); + if (CodeMapFD == -1) { + return -1; + } + + // Ensure the file descriptor is closed on exec + auto flags = fcntl(CodeMapFD, F_GETFD); + fcntl(CodeMapFD, F_SETFD, flags | FD_CLOEXEC); + return CodeMapFD; +} + uint64_t SyscallHandler::GuestMprotect(FEXCore::Core::InternalThreadState* Thread, void* addr, size_t len, int prot) { uint64_t Result {}; diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/ThreadManager.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/ThreadManager.cpp index 59dbd5c1e..9fa8b811c 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/ThreadManager.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/ThreadManager.cpp @@ -245,6 +245,9 @@ void ThreadManager::DestroyThread(FEX::HLE::ThreadStateObject* Thread, bool Need auto It = std::find(Threads.begin(), Threads.end(), Thread); LOGMAN_THROW_A_FMT(It != Threads.end(), "Thread wasn't in Threads"); Threads.erase(It); + if (Threads.empty()) { + Thread->Thread->CTX->FlushAndCloseCodeMap(); + } } Stat.DeallocateSlot(Thread->Thread->ThreadStats);