Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Profiler: Add Tracy backend #4300

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@
[submodule "External/jemalloc_glibc"]
path = External/jemalloc_glibc
url = https://github.com/FEX-Emu/jemalloc.git
[submodule "External/tracy"]
path = External/tracy
url = https://github.com/wolfpld/tracy
19 changes: 18 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -61,6 +61,19 @@ 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)
add_definitions(-DTRACY_ENABLE=1)
# 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)
# This pulls in libbacktrace which allocators in global constructors (before FEX can set up its allocator hooks)
add_definitions(-DTRACY_NO_CALLSTACK=1)
else()
message(FATAL_ERROR "Unknown FEXCore profiler backend ${FEXCORE_PROFILER_BACKEND}")
endif()
Expand Down Expand Up @@ -270,6 +283,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!")
Expand Down
1 change: 1 addition & 0 deletions External/tracy
Submodule tracy added at 5d542d
4 changes: 4 additions & 0 deletions FEXCore/Source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,10 @@ add_library(FEXCore_Base STATIC ${FEXCORE_BASE_SRCS})
target_link_libraries(FEXCore_Base ${LIBS})
AddDefaultOptionsToTarget(FEXCore_Base)

if (ENABLE_FEXCORE_PROFILER AND FEXCORE_PROFILER_BACKEND STREQUAL "TRACY")
target_link_libraries(FEXCore_Base TracyClient)
endif()

function(AddObject Name Type)
add_library(${Name} ${Type} ${SRCS})

Expand Down
1 change: 1 addition & 0 deletions FEXCore/Source/Interface/Core/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ void ContextImpl::DestroyThread(FEXCore::Core::InternalThreadState* Thread) {
void ContextImpl::UnlockAfterFork(FEXCore::Core::InternalThreadState* LiveThread, bool Child) {
Allocator::UnlockAfterFork(LiveThread, Child);

Profiler::PostForkAction(Child);
if (Child) {
CodeInvalidationMutex.StealAndDropActiveLocks();
if (Config.StrictInProcessSplitLocks) {
Expand Down
81 changes: 80 additions & 1 deletion FEXCore/Source/Utils/Profiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#define BACKEND_OFF 0
#define BACKEND_GPUVIS 1
#define BACKEND_TRACY 2

#ifdef ENABLE_FEXCORE_PROFILER
#ifndef _WIN32
Expand Down Expand Up @@ -114,35 +115,113 @@ 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

namespace FEXCore::Profiler {

#ifdef ENABLE_FEXCORE_PROFILER
void Init() {
static int g_EnableAfterFork = 0;
static bool g_Enable = false;

void Init(const fextl::string& ProgramName, const fextl::string& ProgramPath) {
#if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS
GPUVis::Init();
#elif FEXCORE_PROFILER_BACKEND == BACKEND_TRACY
const char* ProfileTargetName = getenv("FEX_PROFILE_TARGET_NAME"); // Match by application name
const char* ProfileTargetPath = getenv("FEX_PROFILE_TARGET_PATH"); // Match by path suffix
const char* WaitForFork = getenv("FEX_PROFILE_WAIT_FOR_FORK"); // Don't enable profiling until the process forks N times
bool Enable = (ProfileTargetName && ProgramName == ProfileTargetName) || (ProfileTargetPath && ProgramPath.ends_with(ProfileTargetPath));
if (Enable && WaitForFork) {
g_EnableAfterFork = std::atoi(WaitForFork);
}
g_Enable = Enable && !g_EnableAfterFork;
if (g_Enable) {
Tracy::Init();
tracy::StartupProfiler();
LogMan::Msg::IFmt("Tracy profiling started");
} else if (g_EnableAfterFork) {
LogMan::Msg::IFmt("Tracy profiling will start after {} forks", g_EnableAfterFork);
}
#endif
}

void PostForkAction(bool IsChild) {
#if FEXCORE_PROFILER_BACKEND == BACKEND_TRACY
if (g_Enable) {
// Tracy does not support multiprocess profiling
LogMan::Msg::EFmt("Warning: Profiling a process with forks is not supported. Set the environment variable "
"FEX_PROFILE_WAIT_FOR_FORK=<n> to start profiling after the n-th fork.");
}

if (IsChild) {
g_Enable = false;
return;
}

if (g_EnableAfterFork > 1) {
--g_EnableAfterFork;
LogMan::Msg::IFmt("Tracy profiling will start after {} forks", g_EnableAfterFork);
} else if (g_EnableAfterFork == 1) {
g_Enable = true;
g_EnableAfterFork = 0;
Tracy::Init();
tracy::StartupProfiler();
LogMan::Msg::IFmt("Tracy profiling started");
}
#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) {
LogMan::Msg::IFmt("Stopping Tracy profiling");
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
21 changes: 16 additions & 5 deletions FEXCore/include/FEXCore/Utils/Profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
#include <cstdint>
#include <string_view>

#include <FEXCore/fextl/string.h>
#include <FEXCore/Utils/CompilerDefs.h>

#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(const fextl::string& ProgramName, const fextl::string& ProgramPath);
FEX_DEFAULT_VISIBILITY void PostForkAction(bool IsChild);
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);
Expand All @@ -25,18 +32,22 @@ 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(const fextl::string& ProgramName, const fextl::string& ProgramPath) {}
[[maybe_unused]]
static void PostForkAction(bool IsChild) {}
[[maybe_unused]]
static void Shutdown() {}
[[maybe_unused]]
Expand Down
3 changes: 2 additions & 1 deletion Source/Tools/FEXLoader/FEXLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,6 @@ int main(int argc, char** argv, char** const envp) {
std::this_thread::sleep_for(std::chrono::seconds(StartupSleep()));
}

FEXCore::Profiler::Init();
FEXCore::Telemetry::Initialize();

if (!LDPath().empty() && Program.ProgramPath.starts_with(LDPath())) {
Expand Down Expand Up @@ -493,6 +492,8 @@ int main(int argc, char** argv, char** const envp) {
free(data);
}

FEXCore::Profiler::Init(Program.ProgramName, Program.ProgramPath);

// System allocator is now system allocator or FEX
FEXCore::Context::InitializeStaticTables(Loader.Is64BitMode() ? FEXCore::Context::MODE_64BIT : FEXCore::Context::MODE_32BIT);

Expand Down
4 changes: 4 additions & 0 deletions Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ desc: Handles host -> host and host -> guest signal routing, emulates procmask &
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/MathUtils.h>
#include <FEXCore/Utils/FPState.h>
#include <FEXCore/Utils/Profiler.h>
#include <FEXCore/Utils/ArchHelpers/Arm64.h>
#include <FEXHeaderUtils/Syscalls.h>

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ desc: SMC/MMan Tracking
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/MathUtils.h>
#include <FEXCore/Utils/Profiler.h>
#include <FEXCore/Utils/SignalScopeGuards.h>
#include <FEXCore/Utils/TypeDefines.h>

Expand Down Expand Up @@ -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<std::shared_lock>(_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)
Expand Down