mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 13:00:15 +02:00
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. Additionally, FEX_PROFILE_WAIT_FOR_FORK=1 may be needed for games that fork on startup.
This commit is contained in:
1 parent
8ad54e7bd5
commit
391f9aa97d
8 files changed
+154
-24
No files matched your search
+21
-1
@@ -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,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()
|
||||
@@ -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!")
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
// 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>
|
||||
@@ -15,10 +12,11 @@
|
||||
#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
|
||||
@@ -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()}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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:
|
||||
@@ -86,14 +106,9 @@ private:
|
||||
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 {
|
||||
@@ -127,7 +142,9 @@ private:
|
||||
|
||||
#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]]
|
||||
|
||||
@@ -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())) {
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in new issue
Block a user