Skip to content

Commit

Permalink
Merge pull request #4300 from neobrain/feature_profiler_tracy
Browse files Browse the repository at this point in the history
Profiler: Add Tracy backend
  • Loading branch information
lioncash authored Feb 13, 2025
2 parents 9eccc01 + 391f9aa commit 6651f9e
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 24 deletions.
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
22 changes: 21 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,22 @@ 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)
if (MINGW_BUILD)
message(FATAL_ERROR "Tracy profiler not supported")
endif()
else()
message(FATAL_ERROR "Unknown FEXCore profiler backend ${FEXCORE_PROFILER_BACKEND}")
endif()
Expand Down Expand Up @@ -270,6 +286,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 @@ -338,6 +338,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
111 changes: 99 additions & 12 deletions FEXCore/Source/Utils/Profiler.cpp
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
// SPDX-License-Identifier: MIT
#include <array>
#include <cstdint>
#include <fcntl.h>
#include <limits.h>
#ifndef _WIN32
#include <linux/magic.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <time.h>
#endif

#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/Profiler.h>
#include <FEXCore/fextl/fmt.h>
#include <FEXCore/fextl/string.h>

#define BACKEND_OFF 0
#define BACKEND_GPUVIS 1

#ifdef ENABLE_FEXCORE_PROFILER
#if FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_GPUVIS
#include <array>
#include <limits.h>
#include <time.h>
#ifndef _WIN32
static inline uint64_t GetTime() {
// We want the time in the least amount of overhead possible
Expand Down Expand Up @@ -49,7 +47,6 @@ static inline uint64_t GetTime() {

#endif

#if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS
namespace FEXCore::Profiler {
ProfilerBlock::ProfilerBlock(std::string_view const Format)
: DurationBegin {GetTime()}
Expand Down Expand Up @@ -114,35 +111,125 @@ void TraceObject(std::string_view const Format) {
}
}
} // namespace GPUVis
#elif FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_TRACY
#include "tracy/Tracy.hpp"
namespace Tracy {
static int EnableAfterFork = 0;
static bool Enable = false;

void Init(std::string_view ProgramName, std::string_view ProgramPath) {
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 Matched = (ProfileTargetName && ProgramName == ProfileTargetName) || (ProfileTargetPath && ProgramPath.ends_with(ProfileTargetPath));
if (Matched && WaitForFork) {
EnableAfterFork = std::atoi(WaitForFork);
}
Enable = Matched && !EnableAfterFork;
if (Enable) {
tracy::StartupProfiler();
LogMan::Msg::IFmt("Tracy profiling started");
} else if (EnableAfterFork) {
LogMan::Msg::IFmt("Tracy profiling will start after fork");
}
}

void PostForkAction(bool IsChild) {
if (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) {
Enable = false;
return;
}

if (EnableAfterFork > 1) {
--EnableAfterFork;
LogMan::Msg::IFmt("Tracy profiling will start after {} forks", EnableAfterFork);
} else if (EnableAfterFork == 1) {
Enable = true;
EnableAfterFork = 0;
tracy::StartupProfiler();
LogMan::Msg::IFmt("Tracy profiling started");
}
}

void Shutdown() {
if (Tracy::Enable) {
LogMan::Msg::IFmt("Stopping Tracy profiling");
tracy::ShutdownProfiler();
}
}

void TraceObject(std::string_view const Format, uint64_t Duration) {}

void TraceObject(std::string_view const Format) {
if (Tracy::Enable) {
TracyMessage(Format.data(), Format.size());
}
}
} // namespace Tracy
#else
#error Unknown profiler backend
#endif
#endif

namespace FEXCore::Profiler {

#ifdef ENABLE_FEXCORE_PROFILER
void Init() {
#if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS
void Init(std::string_view ProgramName, std::string_view ProgramPath) {
#if FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_GPUVIS
GPUVis::Init();
#elif FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_TRACY
Tracy::Init(ProgramName, ProgramPath);
#endif
}

void PostForkAction(bool IsChild) {
#if FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_TRACY
Tracy::PostForkAction(IsChild);
#endif
}

bool IsActive() {
#if FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_GPUVIS
// Always active
return true;
#elif FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_TRACY
// Active if previously enabled
if (Tracy::Enable) {
LogMan::Msg::EFmt("PROFILE ENABLED");
}
return Tracy::Enable;
#endif
}

void Shutdown() {
#if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS
#if FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_GPUVIS
GPUVis::Shutdown();
#elif FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_TRACY
Tracy::Shutdown();
#endif
}

void TraceObject(std::string_view const Format, uint64_t Duration) {
#if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS
#if FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_GPUVIS
GPUVis::TraceObject(Format, Duration);
#elif FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_TRACY
Tracy::TraceObject(Format, Duration);
#endif
}

void TraceObject(std::string_view const Format) {
#if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS
#if FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_GPUVIS
GPUVis::TraceObject(Format);
#elif FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_TRACY
Tracy::TraceObject(Format);
#endif
}

#endif
} // namespace FEXCore::Profiler
33 changes: 25 additions & 8 deletions FEXCore/include/FEXCore/Utils/Profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@

#include <FEXCore/Utils/CompilerDefs.h>

#define FEXCORE_PROFILER_BACKEND_OFF 0
#define FEXCORE_PROFILER_BACKEND_GPUVIS 1
#define FEXCORE_PROFILER_BACKEND_TRACY 2

#if defined(ENABLE_FEXCORE_PROFILER) && FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_TRACY
#include "tracy/Tracy.hpp"
#endif

namespace FEXCore::Profiler {
// FEXCore live-stats
constexpr uint8_t STATS_VERSION = 2;
Expand Down Expand Up @@ -69,11 +77,23 @@ static inline uint64_t GetCycleCounter() {
}
#endif

FEX_DEFAULT_VISIBILITY void Init();
FEX_DEFAULT_VISIBILITY void Init(std::string_view ProgramName, std::string_view 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);

#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)

#if FEXCORE_PROFILER_BACKEND == FEXCORE_PROFILER_BACKEND_TRACY
// Declare a scoped profile block variable with a fixed name.
#define FEXCORE_PROFILE_SCOPED(name) ZoneNamedN(___tracy_scoped_zone, name, ::FEXCore::Profiler::IsActive())
#else
// A class that follows scoping rules to generate a profile duration block
class ProfilerBlock final {
public:
Expand All @@ -86,14 +106,9 @@ class ProfilerBlock final {
std::string_view const Format;
};

#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)
#endif

template<typename T, size_t FlatOffset = 0>
class AccumulationBlock final {
Expand Down Expand Up @@ -127,7 +142,9 @@ class AccumulationBlock final {

#else
[[maybe_unused]]
static void Init() {}
static void Init(std::string_view ProgramName, std::string_view 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
2 changes: 1 addition & 1 deletion Source/Windows/ARM64EC/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ NTSTATUS ProcessInit() {
// Not applicable to Windows
FEXCore::Config::EraseSet(FEXCore::Config::ConfigOption::CONFIG_TSOAUTOMIGRATION, "0");

FEXCore::Profiler::Init();
FEXCore::Profiler::Init("", "");

FEXCore::Context::InitializeStaticTables(FEXCore::Context::MODE_64BIT);

Expand Down
2 changes: 1 addition & 1 deletion Source/Windows/WOW64/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ void BTCpuProcessInit() {
// Not applicable to Windows
FEXCore::Config::EraseSet(FEXCore::Config::ConfigOption::CONFIG_TSOAUTOMIGRATION, "0");

FEXCore::Profiler::Init();
FEXCore::Profiler::Init("", "");

FEXCore::Context::InitializeStaticTables(FEXCore::Context::MODE_32BIT);

Expand Down

0 comments on commit 6651f9e

Please sign in to comment.