Files
FEX-Emu--FEX/FEXCore/Source/Utils/Profiler.cpp
T
Ryan Houdek 657c27556c Profiler/GPUViz: Stop allocating memory
These parsing strings are tiny, less than 64 bytes all the time. Just
stack allocate the buffer. Makes it safer to use during extenuating
circumstances as well, like SIGBUS and SIGSEGV.
2025-01-27 18:49:15 -08:00

149 lines
3.8 KiB
C++

// 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
#ifndef _WIN32
static inline uint64_t GetTime() {
// We want the time in the least amount of overhead possible
// clock_gettime will do a VDSO call with the least amount of overhead
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1'000'000'000ULL + ts.tv_nsec;
}
#else
static inline uint64_t GetTime() {
// GetTime needs to return nanoseconds, query the interface.
static uint64_t FrequencyScale = {};
if (!FrequencyScale) [[unlikely]] {
LARGE_INTEGER Frequency {};
while (!QueryPerformanceFrequency(&Frequency))
;
constexpr uint64_t NanosecondsInSecond = 1'000'000'000ULL;
// On WINE this will always result in a scale of 100.
FrequencyScale = NanosecondsInSecond / Frequency.QuadPart;
}
LARGE_INTEGER ticks;
while (!QueryPerformanceCounter(&ticks))
;
return ticks.QuadPart * FrequencyScale;
}
#endif
#if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS
namespace FEXCore::Profiler {
ProfilerBlock::ProfilerBlock(std::string_view const Format)
: DurationBegin {GetTime()}
, Format {Format} {}
ProfilerBlock::~ProfilerBlock() {
auto Duration = GetTime() - DurationBegin;
TraceObject(Format, Duration);
}
} // namespace FEXCore::Profiler
namespace GPUVis {
// ftrace FD for writing trace data.
// Needs to be a raw FD since we hold this open for the entire application execution.
static int TraceFD {-1};
// Need to search the paths to find the real trace path
static std::array<const char*, 2> TraceFSDirectories {
"/sys/kernel/tracing",
"/sys/kernel/debug/tracing",
};
void Init() {
for (auto Path : TraceFSDirectories) {
#ifdef _WIN32
constexpr auto flags = O_WRONLY;
#else
constexpr auto flags = O_WRONLY | O_CLOEXEC;
#endif
fextl::string FilePath = fextl::fmt::format("{}/trace_marker", Path);
TraceFD = open(FilePath.c_str(), flags);
if (TraceFD != -1) {
// Opened TraceFD, early exit
break;
}
}
}
void Shutdown() {
if (TraceFD != -1) {
close(TraceFD);
TraceFD = -1;
}
}
void TraceObject(std::string_view const Format, uint64_t Duration) {
if (TraceFD != -1) {
// Print the duration as something that began negative duration ago
const auto StringSize = Format.size() + strlen(" (lduration=-)\n") + 22;
auto Event = reinterpret_cast<char*>(alloca(StringSize));
auto Res = ::fmt::format_to_n(Event, StringSize, "{} (lduration=-{})\n", Format, Duration);
write(TraceFD, Event, Res.size);
}
}
void TraceObject(std::string_view const Format) {
if (TraceFD != -1) {
const auto StringSize = Format.size() + 1;
auto Event = reinterpret_cast<char*>(alloca(StringSize));
auto Res = ::fmt::format_to_n(Event, StringSize, "{}\n", Format);
write(TraceFD, Event, Res.size);
}
}
} // namespace GPUVis
#else
#error Unknown profiler backend
#endif
#endif
namespace FEXCore::Profiler {
#ifdef ENABLE_FEXCORE_PROFILER
void Init() {
#if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS
GPUVis::Init();
#endif
}
void Shutdown() {
#if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS
GPUVis::Shutdown();
#endif
}
void TraceObject(std::string_view const Format, uint64_t Duration) {
#if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS
GPUVis::TraceObject(Format, Duration);
#endif
}
void TraceObject(std::string_view const Format) {
#if FEXCORE_PROFILER_BACKEND == BACKEND_GPUVIS
GPUVis::TraceObject(Format);
#endif
}
#endif
} // namespace FEXCore::Profiler