From 7698229fd93ff7208142ff196763d860c126fab2 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Mon, 31 Oct 2022 11:14:23 +0100 Subject: [PATCH] Profiler: Add Tracy backend This differs from the existing GPUVis backend in a number of ways: * Tracy is optimized for minimal overhead and nanosecond-resolution profiling * Tracy supports live tracing (in addition to capture-based operation) * Tracy has a richer feature set and a more polished UI (notably, statistics and histograms are generated out-of-the-box) * GPUVis supports tracing multiple processes, whereas Tracy is single-process only To use this backend, one of the environment variables FEX_PROFILE_TARGET_NAME or FEX_PROFILE_TARGET_PATH must be defined to select the application under profile by name or by path suffix. --- CMakeLists.txt | 16 +++++++- FEXCore/Source/CMakeLists.txt | 1 + FEXCore/Source/Utils/Profiler.cpp | 41 ++++++++++++++++++- FEXCore/include/FEXCore/Utils/Profiler.h | 16 +++++--- Source/Tools/FEXLoader/FEXLoader.cpp | 5 ++- .../LinuxSyscalls/SignalDelegator.cpp | 4 ++ .../LinuxSyscalls/SyscallsSMCTracking.cpp | 3 ++ 7 files changed, 78 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba9abf3765..d5849929f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ option(ENABLE_VIXL_SIMULATOR "Enable use of VIXL simulator for emulation (only u option(ENABLE_VIXL_DISASSEMBLER "Enables debug disassembler output with VIXL" FALSE) option(USE_LEGACY_BINFMTMISC "Uses legacy method of setting up binfmt_misc" FALSE) option(ENABLE_FEXCORE_PROFILER "Enables use of the FEXCore timeline profiling capabilities" FALSE) -set (FEXCORE_PROFILER_BACKEND "gpuvis" CACHE STRING "Set which backend you want to use for the FEXCore profiler") +set (FEXCORE_PROFILER_BACKEND "gpuvis" CACHE STRING "Set which backend to use for the FEXCore profiler (gpuvis, tracy)") option(ENABLE_GLIBC_ALLOCATOR_HOOK_FAULT "Enables glibc memory allocation hooking with fault for CI testing") option(USE_PDB_DEBUGINFO "Builds debug info in PDB format" FALSE) @@ -61,6 +61,16 @@ if (ENABLE_FEXCORE_PROFILER) if (FEXCORE_PROFILER_BACKEND STREQUAL "GPUVIS") add_definitions(-DFEXCORE_PROFILER_BACKEND=1) + elseif (FEXCORE_PROFILER_BACKEND STREQUAL "TRACY") + add_definitions(-DFEXCORE_PROFILER_BACKEND=2) + # Required so that Tracy will only start in the selected guest application + add_definitions(-DTRACY_MANUAL_LIFETIME=1) + add_definitions(-DTRACY_DELAYED_INIT=1) + # This interferes with FEX's signal handling + add_definitions(-DTRACY_NO_CRASH_HANDLER=1) + # Tracy can gather call stack samples in regular intervals, but this + # isn't useful for us since it would usually sample opaque JIT code + add_definitions(-DTRACY_NO_SAMPLING=1) else() message(FATAL_ERROR "Unknown FEXCore profiler backend ${FEXCORE_PROFILER_BACKEND}") endif() @@ -270,6 +280,10 @@ if (BUILD_TESTS OR ENABLE_VIXL_DISASSEMBLER OR ENABLE_VIXL_SIMULATOR) include_directories(SYSTEM External/vixl/src/) endif() +if (ENABLE_FEXCORE_PROFILER AND FEXCORE_PROFILER_BACKEND STREQUAL "TRACY") + add_subdirectory(External/tracy) +endif() + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") # This means we were attempted to get compiled with GCC message(FATAL_ERROR "FEX doesn't support getting compiled with GCC!") diff --git a/FEXCore/Source/CMakeLists.txt b/FEXCore/Source/CMakeLists.txt index 13be501484..724a773a05 100644 --- a/FEXCore/Source/CMakeLists.txt +++ b/FEXCore/Source/CMakeLists.txt @@ -335,6 +335,7 @@ endfunction() # Build FEXCore_Config static library add_library(FEXCore_Base STATIC ${FEXCORE_BASE_SRCS}) target_link_libraries(FEXCore_Base ${LIBS}) +target_link_libraries(FEXCore_Base TracyClient) AddDefaultOptionsToTarget(FEXCore_Base) function(AddObject Name Type) diff --git a/FEXCore/Source/Utils/Profiler.cpp b/FEXCore/Source/Utils/Profiler.cpp index 068f3cd693..2c2d2a46d0 100644 --- a/FEXCore/Source/Utils/Profiler.cpp +++ b/FEXCore/Source/Utils/Profiler.cpp @@ -17,6 +17,7 @@ #define BACKEND_OFF 0 #define BACKEND_GPUVIS 1 +#define BACKEND_TRACY 2 #ifdef ENABLE_FEXCORE_PROFILER #ifndef _WIN32 @@ -110,35 +111,73 @@ void TraceObject(std::string_view const Format) { } } } // namespace GPUVis +#elif FEXCORE_PROFILER_BACKEND == BACKEND_TRACY +#include "tracy/TracyC.h" +namespace Tracy { +void Init() {} + +void Shutdown() {} + +void TraceObject(std::string_view const Format, uint64_t Duration) {} + +void TraceObject(std::string_view const Format) {} +} // namespace Tracy #else #error Unknown profiler backend #endif #endif +extern "C" bool g_Enable = false; namespace FEXCore::Profiler { #ifdef ENABLE_FEXCORE_PROFILER -void Init() { +void Init(bool Enable) { #if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS GPUVis::Init(); +#elif FEXCORE_PROFILER_BACKEND == BACKEND_TRACY + Tracy::Init(); + g_Enable = Enable; + if (g_Enable) { + tracy::StartupProfiler(); + } +#endif +} + +bool IsActive() { +#if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS + // Always active + return true; +#elif FEXCORE_PROFILER_BACKEND == BACKEND_TRACY + // Active if previously enabled + return g_Enable; #endif } void Shutdown() { #if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS GPUVis::Shutdown(); +#elif FEXCORE_PROFILER_BACKEND == BACKEND_TRACY + if (g_Enable) { + tracy::ShutdownProfiler(); + } + Tracy::Shutdown(); #endif } void TraceObject(std::string_view const Format, uint64_t Duration) { #if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS GPUVis::TraceObject(Format, Duration); +#elif FEXCORE_PROFILER_BACKEND == BACKEND_TRACY + Tracy::TraceObject(Format, Duration); #endif } void TraceObject(std::string_view const Format) { #if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS GPUVis::TraceObject(Format); +#elif FEXCORE_PROFILER_BACKEND == BACKEND_TRACY + Tracy::TraceObject(Format); #endif } + #endif } // namespace FEXCore::Profiler diff --git a/FEXCore/include/FEXCore/Utils/Profiler.h b/FEXCore/include/FEXCore/Utils/Profiler.h index 059d49d064..14a5e741da 100644 --- a/FEXCore/include/FEXCore/Utils/Profiler.h +++ b/FEXCore/include/FEXCore/Utils/Profiler.h @@ -4,11 +4,15 @@ #include #include +#if defined(ENABLE_FEXCORE_PROFILER) && FEXCORE_PROFILER_BACKEND == 2 +#include "tracy/Tracy.hpp" +#endif namespace FEXCore::Profiler { #ifdef ENABLE_FEXCORE_PROFILER -FEX_DEFAULT_VISIBILITY void Init(); +FEX_DEFAULT_VISIBILITY void Init(bool Active); +FEX_DEFAULT_VISIBILITY bool IsActive(); FEX_DEFAULT_VISIBILITY void Shutdown(); FEX_DEFAULT_VISIBILITY void TraceObject(std::string_view const Format); FEX_DEFAULT_VISIBILITY void TraceObject(std::string_view const Format, uint64_t Duration); @@ -25,18 +29,20 @@ class ProfilerBlock final { std::string_view const Format; }; -#define UniqueScopeName2(name, line) name##line -#define UniqueScopeName(name, line) UniqueScopeName2(name, line) +//#define UniqueScopeName2(name, line) name##line +//#define UniqueScopeName(name, line) UniqueScopeName2(name, line) // Declare an instantaneous profiler event. #define FEXCORE_PROFILE_INSTANT(name) FEXCore::Profiler::TraceObject(name) // Declare a scoped profile block variable with a fixed name. -#define FEXCORE_PROFILE_SCOPED(name) FEXCore::Profiler::ProfilerBlock UniqueScopeName(ScopedBlock_, __LINE__)(name) +//#define FEXCORE_PROFILE_SCOPED(name) FEXCore::Profiler::ProfilerBlock UniqueScopeName(ScopedBlock_, __LINE__)(name) +#define FEXCORE_PROFILE_SCOPED(name) \ + ZoneNamedN( ___tracy_scoped_zone, name, ::FEXCore::Profiler::IsActive()) #else [[maybe_unused]] -static void Init() {} +static void Init(bool Active) {} [[maybe_unused]] static void Shutdown() {} [[maybe_unused]] diff --git a/Source/Tools/FEXLoader/FEXLoader.cpp b/Source/Tools/FEXLoader/FEXLoader.cpp index dd0aec576c..751e5313ca 100644 --- a/Source/Tools/FEXLoader/FEXLoader.cpp +++ b/Source/Tools/FEXLoader/FEXLoader.cpp @@ -414,7 +414,10 @@ int main(int argc, char** argv, char** const envp) { std::this_thread::sleep_for(std::chrono::seconds(StartupSleep())); } - FEXCore::Profiler::Init(); + const char* ProfileTargetName = getenv("FEX_PROFILE_TARGET_NAME"); // Match by application name + const char* ProfileTargetPath = getenv("FEX_PROFILE_TARGET_PATH"); // Match by path suffix + FEXCore::Profiler::Init((ProfileTargetName && Program.ProgramName == ProfileTargetName) || + (ProfileTargetPath && Program.ProgramPath.ends_with(ProfileTargetPath))); FEXCore::Telemetry::Initialize(); if (!LDPath().empty() && Program.ProgramPath.starts_with(LDPath())) { diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator.cpp index 151e8d9b66..098e4b8624 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator.cpp @@ -18,6 +18,7 @@ desc: Handles host -> host and host -> guest signal routing, emulates procmask & #include #include #include +#include #include #include @@ -59,6 +60,7 @@ static FEX::HLE::ThreadStateObject* GetThreadFromAltStack(const stack_t& alt_sta static void SignalHandlerThunk(int Signal, siginfo_t* Info, void* UContext) { ucontext_t* _context = (ucontext_t*)UContext; auto ThreadObject = GetThreadFromAltStack(_context->uc_stack); + FEXCORE_PROFILE_SCOPED("GuestSignalHandler"); ThreadObject->SignalInfo.Delegator->HandleSignal(ThreadObject, Signal, Info, UContext); } @@ -916,6 +918,8 @@ SignalDelegator::SignalDelegator(FEXCore::Context::Context* _CTX, const std::str return false; } + FEXCORE_PROFILE_SCOPED("HandleAuxSIGBUS"); + const auto Delegator = FEX::HLE::ThreadManager::GetStateObjectFromFEXCoreThread(Thread)->SignalInfo.Delegator; const auto Result = FEXCore::ArchHelpers::Arm64::HandleUnalignedAccess(Thread, Delegator->GetUnalignedHandlerType(), PC, ArchHelpers::Context::GetArmGPRs(ucontext)); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index b81242d95a..8af346095e 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -18,6 +18,7 @@ desc: SMC/MMan Tracking #include #include #include +#include #include #include @@ -54,6 +55,8 @@ bool SyscallHandler::HandleSegfault(FEXCore::Core::InternalThreadState* Thread, // Can't use the deferred signal lock in the SIGSEGV handler. auto lk = FEXCore::MaskSignalsAndLockMutex(_SyscallHandler->VMATracking.Mutex); + FEXCORE_PROFILE_SCOPED("SMC segfault"); + auto VMATracking = &_SyscallHandler->VMATracking; // If the write spans two pages, they will be flushed one at a time (generating two faults)