diff --git a/CMakeLists.txt b/CMakeLists.txt index 970e702a9f..7d11f18e7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ option(ENABLE_STRICT_WERROR "Enables stricter -Werror for CI" FALSE) option(ENABLE_WERROR "Enables -Werror" FALSE) option(ENABLE_STATIC_PIE "Enables static-pie build" FALSE) option(ENABLE_JEMALLOC "Enables jemalloc allocator" TRUE) +option(ENABLE_OFFLINE_TELEMETRY "Enables FEX offline telemetry" TRUE) set (X86_C_COMPILER "x86_64-linux-gnu-gcc" CACHE STRING "c compiler for compiling x86 guest libs") set (X86_CXX_COMPILER "x86_64-linux-gnu-g++" CACHE STRING "c++ compiler for compiling x86 guest libs") @@ -82,6 +83,11 @@ if (ENABLE_LLD) link_libraries(${LD_OVERRIDE}) endif() +if (NOT ENABLE_OFFLINE_TELEMETRY) + # Disable FEX offline telemetry entirely if asked + add_definitions(-DFEX_DISABLE_TELEMETRY=1) +endif() + if (ENABLE_STATIC_PIE) if (_M_ARM_64 AND ENABLE_LLD) message (FATAL_ERROR "Static linking does not currently work with AArch64+LLD. Use GNU ld for now.") diff --git a/External/FEXCore/Source/CMakeLists.txt b/External/FEXCore/Source/CMakeLists.txt index 9cc0b3f8b0..afc11b45a7 100644 --- a/External/FEXCore/Source/CMakeLists.txt +++ b/External/FEXCore/Source/CMakeLists.txt @@ -129,6 +129,7 @@ set (SRCS Utils/Allocator.cpp Utils/Allocator/64BitAllocator.cpp Utils/LogManager.cpp + Utils/Telemetry.cpp Utils/Threads.cpp ) diff --git a/External/FEXCore/Source/Common/Paths.cpp b/External/FEXCore/Source/Common/Paths.cpp index 9e5125110f..c924a78152 100644 --- a/External/FEXCore/Source/Common/Paths.cpp +++ b/External/FEXCore/Source/Common/Paths.cpp @@ -3,12 +3,43 @@ #include #include +#include #include +#include namespace FEXCore::Paths { std::unique_ptr CachePath; std::unique_ptr EntryCache; + char const* FindUserHomeThroughUID() { + auto passwd = getpwuid(geteuid()); + if (passwd) { + return passwd->pw_dir; + } + return nullptr; + } + + const char *GetHomeDirectory() { + char const *HomeDir = getenv("HOME"); + + // Try to get home directory from uid + if (!HomeDir) { + HomeDir = FindUserHomeThroughUID(); + } + + // try the PWD + if (!HomeDir) { + HomeDir = getenv("PWD"); + } + + // Still doesn't exit? You get local + if (!HomeDir) { + HomeDir = "."; + } + + return HomeDir; + } + void InitializePaths() { CachePath = std::make_unique(); EntryCache = std::make_unique(); diff --git a/External/FEXCore/Source/Common/Paths.h b/External/FEXCore/Source/Common/Paths.h index f538c04a5b..451a4079fe 100644 --- a/External/FEXCore/Source/Common/Paths.h +++ b/External/FEXCore/Source/Common/Paths.h @@ -4,6 +4,9 @@ namespace FEXCore::Paths { void InitializePaths(); void ShutdownPaths(); + + const char *GetHomeDirectory(); + std::string GetCachePath(); std::string GetEntryCachePath(); } diff --git a/External/FEXCore/Source/Interface/Config/Config.cpp b/External/FEXCore/Source/Interface/Config/Config.cpp index e02ff8776c..9f41cda167 100644 --- a/External/FEXCore/Source/Interface/Config/Config.cpp +++ b/External/FEXCore/Source/Interface/Config/Config.cpp @@ -1,43 +1,30 @@ #include "Common/StringConv.h" +#include "Common/Paths.h" #include #include "Interface/Context/Context.h" #include #include -#include #include #include -#include namespace FEXCore::Config { - char const* FindUserHomeThroughUID() { - auto passwd = getpwuid(geteuid()); - if (passwd) { - return passwd->pw_dir; - } - return nullptr; - } - - const char *GetHomeDirectory() { - char const *HomeDir = getenv("HOME"); - - // Try to get home directory from uid - if (!HomeDir) { - HomeDir = FindUserHomeThroughUID(); - } + std::string GetDataDirectory() { + std::string DataDir{}; - // try the PWD - if (!HomeDir) { - HomeDir = getenv("PWD"); + char const *HomeDir = Paths::GetHomeDirectory(); + char const *DataXDG = getenv("XDG_DATA_HOME"); + char const *DataOverride = getenv("FEX_APP_DATA_LOCATION"); + if (DataOverride) { + // Data override will override the complete directory + DataDir = DataOverride; } - - // Still doesn't exit? You get local - if (!HomeDir) { - HomeDir = "."; + else { + DataDir = DataXDG ?: HomeDir; + DataDir += "/.fex-emu/"; } - - return HomeDir; + return DataDir; } std::string GetConfigDirectory(bool Global) { @@ -46,7 +33,7 @@ namespace FEXCore::Config { ConfigDir = GLOBAL_DATA_DIRECTORY; } else { - char const *HomeDir = GetHomeDirectory(); + char const *HomeDir = Paths::GetHomeDirectory(); char const *ConfigXDG = getenv("XDG_CONFIG_HOME"); char const *ConfigOverride = getenv("FEX_APP_CONFIG_LOCATION"); if (ConfigOverride) { @@ -109,23 +96,6 @@ namespace FEXCore::Config { return ConfigFile; } - std::string GetDataDirectory() { - std::string DataDir{}; - - char const *HomeDir = GetHomeDirectory(); - char const *DataXDG = getenv("XDG_DATA_HOME"); - char const *DataOverride = getenv("FEX_APP_DATA_LOCATION"); - if (DataOverride) { - // Data override will override the complete directory - DataDir = DataOverride; - } - else { - DataDir = DataXDG ?: HomeDir; - DataDir += "/.fex-emu/"; - } - return DataDir; - } - void SetConfig(FEXCore::Context::Context *CTX, ConfigOption Option, uint64_t Config) { } diff --git a/External/FEXCore/Source/Interface/Core/ArchHelpers/Arm64.cpp b/External/FEXCore/Source/Interface/Core/ArchHelpers/Arm64.cpp index 8d91cb6c07..562503fad3 100644 --- a/External/FEXCore/Source/Interface/Core/ArchHelpers/Arm64.cpp +++ b/External/FEXCore/Source/Interface/Core/ArchHelpers/Arm64.cpp @@ -2,6 +2,7 @@ #include "Interface/Core/ArchHelpers/MContext.h" #include +#include #include #include @@ -9,6 +10,9 @@ #include namespace FEXCore::ArchHelpers::Arm64 { +FEXCORE_TELEMETRY_STATIC_INIT(SplitLock, TYPE_HAS_SPLIT_LOCKS); +FEXCORE_TELEMETRY_STATIC_INIT(SplitLock16B, TYPE_16BYTE_SPLIT); + static __uint128_t LoadAcquire128(uint64_t Addr) { __uint128_t Result{}; uint64_t Lower; @@ -94,8 +98,15 @@ bool HandleCASPAL(void *_ucontext, void *_info, uint32_t Instr) { // Both cross-cacheline and cross 16byte both need dual CAS loops that can tear // ARMv8.4 LSE2 solves all atomic issues except cross-cacheline + // Check for Split lock across a cacheline + if ((Addr & 63) > 56) { + FEXCORE_TELEMETRY_SET(SplitLock, 1); + } + uint64_t AlignmentMask = 0b1111; if ((Addr & AlignmentMask) > 8) { + FEXCORE_TELEMETRY_SET(SplitLock16B, 1); + uint64_t Alignment = Addr & 0b111; Addr &= ~0b111ULL; uint64_t AddrUpper = Addr + 8; @@ -430,9 +441,16 @@ uint16_t DoCAS16( uint64_t Addr, CASExpectedFn ExpectedFunction, CASDesiredFn DesiredFunction) { + + if ((Addr & 63) == 63) { + FEXCORE_TELEMETRY_SET(SplitLock, 1); + } + // 16 bit uint64_t AlignmentMask = 0b1111; if ((Addr & AlignmentMask) == 15) { + FEXCORE_TELEMETRY_SET(SplitLock16B, 1); + // Address crosses over 16byte or 64byte threshold // Need a dual 8bit CAS loop uint64_t AddrUpper = Addr + 1; @@ -706,9 +724,16 @@ uint32_t DoCAS32( uint64_t Addr, CASExpectedFn ExpectedFunction, CASDesiredFn DesiredFunction) { + + if ((Addr & 63) > 60) { + FEXCORE_TELEMETRY_SET(SplitLock, 1); + } + // 32 bit uint64_t AlignmentMask = 0b1111; if ((Addr & AlignmentMask) > 12) { + FEXCORE_TELEMETRY_SET(SplitLock16B, 1); + // Address crosses over 16byte threshold // Needs dual 4 byte CAS loop uint64_t Alignment = Addr & 0b11; @@ -936,9 +961,16 @@ uint64_t DoCAS64( uint64_t Addr, CASExpectedFn ExpectedFunction, CASDesiredFn DesiredFunction) { + + if ((Addr & 63) > 56) { + FEXCORE_TELEMETRY_SET(SplitLock, 1); + } + // 64bit uint64_t AlignmentMask = 0b1111; if ((Addr & AlignmentMask) > 8) { + FEXCORE_TELEMETRY_SET(SplitLock16B, 1); + uint64_t Alignment = Addr & 0b111; Addr &= ~0b111ULL; uint64_t AddrUpper = Addr + 8; diff --git a/External/FEXCore/Source/Interface/Core/Core.cpp b/External/FEXCore/Source/Interface/Core/Core.cpp index 9d1bcf03c0..1493d36b80 100644 --- a/External/FEXCore/Source/Interface/Core/Core.cpp +++ b/External/FEXCore/Source/Interface/Core/Core.cpp @@ -1091,7 +1091,9 @@ namespace FEXCore::Context { if (NewBlock == 0) { LogMan::Msg::E("CompileBlockJit: Failed to compile code %lX - aborting process", GuestRIP); - abort(); + // Return similar behaviour of SIGILL abort + Frame->Thread->StatusCode = 128 + SIGILL; + Stop(false /* Ignore current thread */); } } diff --git a/External/FEXCore/Source/Interface/Core/Frontend.cpp b/External/FEXCore/Source/Interface/Core/Frontend.cpp index f9fcc01c3c..a79030e937 100644 --- a/External/FEXCore/Source/Interface/Core/Frontend.cpp +++ b/External/FEXCore/Source/Interface/Core/Frontend.cpp @@ -15,6 +15,7 @@ desc: Extracts instruction & block meta info, frontend multiblock logic #include #include #include +#include #include #include @@ -696,6 +697,7 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16 return NormalOp(&X87Ops[X87Op], X87Op); } else if (Info->Type == FEXCore::X86Tables::TYPE_VEX_TABLE_PREFIX) { + FEXCORE_TELEMETRY_SET(VEXOpTelem, 1); uint16_t map_select = 1; uint16_t pp = 0; @@ -723,6 +725,7 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16 if (LocalInfo->Type >= FEXCore::X86Tables::TYPE_VEX_GROUP_12 && LocalInfo->Type <= FEXCore::X86Tables::TYPE_VEX_GROUP_17) { + FEXCORE_TELEMETRY_SET(VEXOpTelem, 1); // We have ModRM uint8_t ModRMByte = ReadByte(); DecodeInst->ModRM = ModRMByte; @@ -740,6 +743,8 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16 return NormalOp(LocalInfo, Op); } else if (Info->Type == FEXCore::X86Tables::TYPE_GROUP_EVEX) { + FEXCORE_TELEMETRY_SET(EVEXOpTelem, 1); + /* uint8_t P1 = */ ReadByte(); /* uint8_t P2 = */ ReadByte(); /* uint8_t P3 = */ ReadByte(); diff --git a/External/FEXCore/Source/Interface/Core/Frontend.h b/External/FEXCore/Source/Interface/Core/Frontend.h index 46b8a3b971..9caffbf943 100644 --- a/External/FEXCore/Source/Interface/Core/Frontend.h +++ b/External/FEXCore/Source/Interface/Core/Frontend.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -89,5 +90,8 @@ class Decoder final { }; const uint8_t *AdjustAddrForSpecialRegion(uint8_t const* _InstStream, uint64_t EntryPoint, uint64_t RIP); + + FEXCORE_TELEMETRY_INIT(VEXOpTelem, TYPE_USES_VEX_OPS); + FEXCORE_TELEMETRY_INIT(EVEXOpTelem, TYPE_USES_EVEX_OPS); }; } diff --git a/External/FEXCore/Source/Utils/Telemetry.cpp b/External/FEXCore/Source/Utils/Telemetry.cpp new file mode 100644 index 0000000000..6ccb69a3fd --- /dev/null +++ b/External/FEXCore/Source/Utils/Telemetry.cpp @@ -0,0 +1,61 @@ + +#include "Common/Paths.h" + +#include +#include +#include + +#include +#include +#include + +namespace FEXCore::Telemetry { +#ifndef FEX_DISABLE_TELEMETRY + static std::array TelemetryValues = {{ }}; + const std::array TelemetryNames { + "64byte Split Locks", + "16Byte Split atomics", + "VEX instructions (AVX)", + "EVEX instructions (AVX512)", + }; + void Initialize() { + auto DataDirectory = Config::GetDataDirectory(); + DataDirectory += "Telemetry/"; + + // Ensure the folder structure is created for our configuration + std::error_code ec{}; + if (!std::filesystem::exists(DataDirectory, ec) && + !std::filesystem::create_directories(DataDirectory, ec)) { + LogMan::Msg::I("Couldn't create telemetry Folder"); + } + } + + void Shutdown(std::filesystem::path &ApplicationName) { + auto DataDirectory = Config::GetDataDirectory(); + DataDirectory += "Telemetry/" + ApplicationName.string() + ".telem"; + + std::error_code ec{}; + if (std::filesystem::exists(DataDirectory, ec)) { + // If the file exists, retain a single backup + auto Backup = DataDirectory + ".1"; + std::filesystem::copy_file(DataDirectory, Backup, std::filesystem::copy_options::overwrite_existing, ec); + } + + std::fstream fs(DataDirectory, std::ios_base::out | std::ios_base::trunc); + if (fs.is_open()) { + for (size_t i = 0; i < TelemetryType::TYPE_LAST; ++i) { + auto &Name = TelemetryNames.at(i); + auto &Data = TelemetryValues.at(i); + fs << Name << ": " << *Data << std::endl; + } + + fs.flush(); + fs.close(); + } + } + + Value &GetObject(TelemetryType Type) { + return TelemetryValues.at(Type); + } +#endif +} diff --git a/External/FEXCore/include/FEXCore/Utils/Telemetry.h b/External/FEXCore/include/FEXCore/Utils/Telemetry.h new file mode 100644 index 0000000000..69642de1d5 --- /dev/null +++ b/External/FEXCore/include/FEXCore/Utils/Telemetry.h @@ -0,0 +1,67 @@ +#pragma once +#include +#include +#include +#include + +namespace FEXCore::Telemetry { +#ifndef FEX_DISABLE_TELEMETRY + class Value; + + class Value final { + public: + Value() = default; + Value(uint64_t Default) : Data {Default} {} + + uint64_t operator*() const { return Data; } + void operator=(uint64_t Value) { Data = Value; } + void operator|=(uint64_t Value) { Data |= Value; } + void operator++(int) { Data++; } + + std::atomic *GetAddr() { return &Data; } + + private: + std::atomic Data; + }; + + enum TelemetryType { + TYPE_HAS_SPLIT_LOCKS, + TYPE_16BYTE_SPLIT, + TYPE_USES_VEX_OPS, + TYPE_USES_EVEX_OPS, + TYPE_LAST, + }; + + Value &GetObject(TelemetryType Type); + + void Initialize(); + void Shutdown(std::filesystem::path &ApplicationName); + +// Telemetry object declaration +// This returns the internal structure to the telemetry data structures +// One must be careful with placing these in the hot path of code execution +// It can be fairly costly, especially in the static version where it puts barriers in the code +#define FEXCORE_TELEMETRY_STATIC_INIT(Name, Type) static FEXCore::Telemetry::Value &Name = FEXCore::Telemetry::GetObject(FEXCore::Telemetry::Type) +#define FEXCORE_TELEMETRY_INIT(Name, Type) FEXCore::Telemetry::Value &Name = FEXCore::Telemetry::GetObject(FEXCore::Telemetry::Type) +// Telemetry ALU operations +// These are typically 3-4 instructions depending on what you're doing +#define FEXCORE_TELEMETRY_SET(Name, Value) Name = Value +#define FEXCORE_TELEMETRY_OR(Name, Value) Name |= Value +#define FEXCORE_TELEMETRY_INC(Name) Name++ + +// Returns a pointer to std::atomic. Can be useful if you are attempting to JIT telemetry accesses for debug purposes +// Not recommended to do telemetry inside JIT code in production code +#define FEXCORE_TELEMETRY_Addr(Name) Name->GetAddr() +#else + static inline void Initialize() {} + static inline void Shutdown(std::filesystem::path &) {} + +#define FEXCORE_TELEMETRY_STATIC_INIT(Name, Type) +#define FEXCORE_TELEMETRY_INIT(Name, Type) +#define FEXCORE_TELEMETRY(Name, Value) do {} while(0) +#define FEXCORE_TELEMETRY_SET(Name, Value) do {} while(0) +#define FEXCORE_TELEMETRY_OR(Name, Value) do {} while(0) +#define FEXCORE_TELEMETRY_INC(Name) do {} while(0) +#define FEXCORE_TELEMETRY_Addr(Name) reinterpret_cast*>(nullptr) +#endif +} diff --git a/Source/Tests/FEXLoader.cpp b/Source/Tests/FEXLoader.cpp index 3d457b5b32..180378f279 100644 --- a/Source/Tests/FEXLoader.cpp +++ b/Source/Tests/FEXLoader.cpp @@ -21,6 +21,7 @@ desc: Glues the ELF loader, FEXCore and LinuxSyscalls to launch an elf under fex #include #include #include +#include #include #include @@ -409,6 +410,7 @@ int main(int argc, char **argv, char **const envp) { } } + FEXCore::Telemetry::Initialize(); InterpreterHandler(&Program, LDPath(), &Args); std::error_code ec{}; @@ -590,7 +592,7 @@ int main(int argc, char **argv, char **const envp) { FEXCore::Allocator::ClearHooks(); // Allocator is now original system allocator - + FEXCore::Telemetry::Shutdown(ProgramName); if (ShutdownReason == FEXCore::Context::ExitReason::EXIT_SHUTDOWN) { return ProgramStatus; }