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() {