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.
This commit is contained in:
Tony Wasserka committed 2025-11-20 19:13:18 +01:00
1 parent b34b711161
commit b6cff01abb
3 files changed
+104 -1

No files matched your search

+33
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
#include "Common/AsyncNet.h"
#include "Common/Config.h"
#include "FDUtils.h"
#include "Common/FEXServerClient.h"
#include <FEXCore/Utils/CompilerDefs.h>
@@ -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;
}
/** @} */
/**
+12
View File
@@ -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);
/** @} */
/**
+59 -1
View File
@@ -4,8 +4,13 @@
#include "SquashFS.h"
#include <Common/AsyncNet.h>
#include <Common/Config.h>
#include <Common/FDUtils.h>
#include <Common/FEXServerClient.h>
#include <FEXCore/Core/CodeCache.h>
#include <FEXCore/HLE/SourcecodeResolver.h>
#include <fmt/ranges.h>
#include <atomic>
@@ -19,6 +24,8 @@
#include <sys/un.h>
#include <vector>
#include <xxhash.h>
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<size_t> 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() {