From b6cff01abb013ec69175b77f225bdb2e70c76f6c Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 25 Sep 2025 20:35:49 +0200 Subject: [PATCH] FEXServer: Add support for querying code maps Managing code maps in FEXServer rather than in FEXInterpreter makes it easier to handle multiple concurrent processes sharing code caches for the main executable and libraries. --- Source/Common/FEXServerClient.cpp | 33 ++++++++++++++ Source/Common/FEXServerClient.h | 12 ++++++ Source/Tools/FEXServer/ProcessPipe.cpp | 60 +++++++++++++++++++++++++- 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/Source/Common/FEXServerClient.cpp b/Source/Common/FEXServerClient.cpp index 5d1652027..358f0801f 100644 --- a/Source/Common/FEXServerClient.cpp +++ b/Source/Common/FEXServerClient.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT #include "Common/AsyncNet.h" #include "Common/Config.h" +#include "FDUtils.h" #include "Common/FEXServerClient.h" #include @@ -375,6 +376,38 @@ int RequestPIDFD(int ServerSocket) { return RequestPIDFDPacket(ServerSocket, PacketType::TYPE_GET_PID_FD); } +int RequestCodeMapFD(int ServerSocket, int ProgramFD, bool HasMultiblock) { + fasio::tcp_socket Socket {ServerSocket}; + FEXServerRequestPacket Req { + .Header { + .Type = HasMultiblock ? PacketType::TYPE_QUERY_CODE_MAP : PacketType::TYPE_QUERY_CODE_MAP_NO_MULTIBLOCK, + }, + }; + + // Send request + fasio::error ec; + { + fasio::mutable_buffer WriteBuffer {std::as_writable_bytes(std::span {&Req, 1})}; + WriteBuffer.FD = &ProgramFD; + write(Socket, WriteBuffer, ec); + if (ec != fasio::error::success) { + return -1; + } + } + + // Wait for success response and log FD + FEXServerResultPacket Res {}; + fasio::mutable_buffer ResBuffer {std::as_writable_bytes(std::span {&Res, 1})}; + int NewFD = -1; + ResBuffer.FD = &NewFD; + read(Socket, ResBuffer, ec); + if (ec != fasio::error::success || Res.Header.Type != PacketType::TYPE_SUCCESS) { + return -1; + } + + return NewFD; +} + /** @} */ /** diff --git a/Source/Common/FEXServerClient.h b/Source/Common/FEXServerClient.h index 5bf30dde1..b2fdc659e 100644 --- a/Source/Common/FEXServerClient.h +++ b/Source/Common/FEXServerClient.h @@ -19,6 +19,8 @@ enum class PacketType { TYPE_GET_LOG_FD, TYPE_GET_ROOTFS_PATH, TYPE_GET_PID_FD, + TYPE_QUERY_CODE_MAP, + TYPE_QUERY_CODE_MAP_NO_MULTIBLOCK, // Result only TYPE_SUCCESS, @@ -113,6 +115,16 @@ fextl::string RequestRootFSPath(int ServerSocket); */ int RequestPIDFD(int ServerSocket); +/** + * @brief Request FEXServer to create a new code map for disk cache population + * + * @param ServerSocket - Socket to the server + * @param ProgramFD - FD for program binary + * + * @return FD to write code map to + */ +int RequestCodeMapFD(int ServerSocket, int ProgramFD, bool HasMultiblock); + /** @} */ /** diff --git a/Source/Tools/FEXServer/ProcessPipe.cpp b/Source/Tools/FEXServer/ProcessPipe.cpp index e9203a139..ff787a61f 100644 --- a/Source/Tools/FEXServer/ProcessPipe.cpp +++ b/Source/Tools/FEXServer/ProcessPipe.cpp @@ -4,8 +4,13 @@ #include "SquashFS.h" #include +#include +#include #include +#include +#include + #include #include @@ -19,6 +24,8 @@ #include #include +#include + namespace ProcessPipe { constexpr int USER_PERMS = S_IRWXU | S_IRWXG | S_IRWXO; int ServerLockFD {-1}; @@ -34,6 +41,8 @@ constexpr size_t static MAX_FD_DISTANCE = 32; rlimit MaxFDs {}; std::atomic NumFilesOpened {}; +static std::string CodeMapDirectory; + size_t GetNumFilesOpen() { // Walk /proc/self/fd/ to see how many open files we currently have const std::filesystem::path self {"/proc/self/fd/"}; @@ -267,7 +276,8 @@ void HandleSocketData(fasio::tcp_socket& Socket) { // Get the current number of FDs of the process before we start handling sockets. GetMaxFDs(); - fasio::mutable_buffer buffer = {std::as_writable_bytes(std::span(Data))}; + int inFD = -1; + fasio::mutable_buffer buffer = {std::as_writable_bytes(std::span(Data)), nullptr, &inFD}; { fasio::error ec; @@ -369,6 +379,47 @@ void HandleSocketData(fasio::tcp_socket& Socket) { buffer += sizeof(FEXServerClient::FEXServerRequestPacket::Header); break; } + + case FEXServerClient::PacketType::TYPE_QUERY_CODE_MAP: + case FEXServerClient::PacketType::TYPE_QUERY_CODE_MAP_NO_MULTIBLOCK: { + char Tmp[PATH_MAX]; + int TmpLen = FEX::get_fdpath(inFD, Tmp); + assert(TmpLen != -1); + std::filesystem::path BinaryPath = std::string_view(Tmp, TmpLen); + // TODO: Move to common code + const auto filename_hash = XXH3_64bits(Tmp, TmpLen); + const bool HasMultiblock = (Req->Header.Type == FEXServerClient::PacketType::TYPE_QUERY_CODE_MAP); + + FEXServerClient::FEXServerResultPacket Res { + .Header { + .Type = FEXServerClient::PacketType::TYPE_SUCCESS, + }, + }; + + // Find first code map that doesn't exist yet + int Index = 0; + std::string Filename; + do { + Filename = fmt::format("{}/{}.{}.bin", CodeMapDirectory, + FEXCore::CodeMap::GetBaseFilename( + FEXCore::ExecutableFileInfo {nullptr, filename_hash, (fextl::string)BinaryPath.string()}, !HasMultiblock), + Index++); + } while (std::filesystem::exists(Filename)); + + std::filesystem::create_directories(CodeMapDirectory); + auto CodeMapFD = open(Filename.c_str(), O_CREAT | O_CLOEXEC | O_WRONLY, 0644); + + fasio::mutable_buffer Data = {.Data = std::as_writable_bytes(std::span(&Res, 1)), + .FD = (CodeMapFD != -1 ? std::optional {&CodeMapFD} : std::nullopt)}; + fasio::error ec; + write(Socket, Data, ec); + buffer += sizeof(FEXServerClient::FEXServerRequestPacket::Header); + close(inFD); + inFD = -1; + close(CodeMapFD); + break; + } + // Invalid case FEXServerClient::PacketType::TYPE_ERROR: default: @@ -378,6 +429,11 @@ void HandleSocketData(fasio::tcp_socket& Socket) { return; } } + + if (inFD != -1) { + LogMan::Msg::EFmt("Received unused FD argument"); + close(inFD); + } } void CloseConnections() { @@ -413,6 +469,8 @@ void WaitForRequests() { void SetConfiguration(bool Foreground, uint32_t PersistentTimeout) { ProcessPipe::Foreground = Foreground; ProcessPipe::RequestTimeout = PersistentTimeout; + + CodeMapDirectory = FEX::Config::GetCacheDirectory() + "codemap"; } void Shutdown() {