mirror of https://github.com/OpenRCT2/OpenRCT2.git
194 lines
5.5 KiB
C++
194 lines
5.5 KiB
C++
/*****************************************************************************
|
|
* Copyright (c) 2014-2024 OpenRCT2 developers
|
|
*
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
*
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
*****************************************************************************/
|
|
#pragma once
|
|
|
|
#include "ProfilingMacros.hpp"
|
|
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
namespace OpenRCT2::Profiling
|
|
{
|
|
void Enable();
|
|
void Disable();
|
|
bool IsEnabled();
|
|
|
|
struct Function
|
|
{
|
|
virtual ~Function() = default;
|
|
|
|
// This returns the full function prototype not just the name.
|
|
virtual const char* GetName() const noexcept = 0;
|
|
|
|
// Total calls made to the function.
|
|
virtual uint64_t GetCallCount() const noexcept = 0;
|
|
|
|
// Returns a small window of the most recent times.
|
|
virtual std::vector<double> GetTimeSamples() const = 0;
|
|
|
|
// Returns all time accumulated in microseconds.
|
|
virtual double GetTotalTime() const = 0;
|
|
|
|
// Returns the min. time in microseconds.
|
|
virtual double GetMinTime() const = 0;
|
|
|
|
// Returns the max. time in microseconds.
|
|
virtual double GetMaxTime() const = 0;
|
|
|
|
// Returns a list of all functions that called this function.
|
|
virtual std::vector<Function*> GetParents() const = 0;
|
|
|
|
// Returns a list of function this function is calling.
|
|
virtual std::vector<Function*> GetChildren() const = 0;
|
|
};
|
|
|
|
namespace Detail
|
|
{
|
|
static constexpr auto MaxSamplesSize = 1024;
|
|
static constexpr auto MaxNameSize = 250;
|
|
|
|
std::vector<Function*>& GetRegistry();
|
|
|
|
struct FunctionInternal : Function
|
|
{
|
|
FunctionInternal()
|
|
{
|
|
GetRegistry().push_back(this);
|
|
}
|
|
|
|
virtual ~FunctionInternal() = default;
|
|
|
|
// Functions marked MT require locking, this is per function basis.
|
|
mutable std::mutex Mutex;
|
|
|
|
std::array<char, MaxNameSize> Name{};
|
|
|
|
// Call count of function.
|
|
std::atomic<uint64_t> CallCount{};
|
|
|
|
// Function times in microseconds.
|
|
std::array<double, MaxSamplesSize> Samples{};
|
|
|
|
// Used internally to write into Samples without a lock.
|
|
std::atomic<size_t> SampleIterator{};
|
|
|
|
double MinTimeUs{};
|
|
|
|
double MaxTimeUs{};
|
|
|
|
double TotalTimeUs{};
|
|
|
|
// Functions that called us.
|
|
std::unordered_set<Function*> Parents;
|
|
|
|
// Functions that this function called.
|
|
std::unordered_set<Function*> Children;
|
|
|
|
uint64_t GetCallCount() const noexcept override
|
|
{
|
|
return CallCount.load();
|
|
}
|
|
|
|
std::vector<double> GetTimeSamples() const override
|
|
{
|
|
const auto numSamples = std::min(SampleIterator.load(), Samples.size());
|
|
return { Samples.begin(), Samples.begin() + numSamples };
|
|
}
|
|
|
|
std::vector<Function*> GetParents() const override
|
|
{
|
|
std::scoped_lock lock(Mutex);
|
|
return { Parents.begin(), Parents.end() };
|
|
}
|
|
|
|
std::vector<Function*> GetChildren() const override
|
|
{
|
|
std::scoped_lock lock(Mutex);
|
|
return { Children.begin(), Children.end() };
|
|
}
|
|
|
|
double GetTotalTime() const override
|
|
{
|
|
std::scoped_lock lock(Mutex);
|
|
return TotalTimeUs;
|
|
}
|
|
|
|
double GetMinTime() const override
|
|
{
|
|
std::scoped_lock lock(Mutex);
|
|
return MinTimeUs;
|
|
}
|
|
|
|
double GetMaxTime() const override
|
|
{
|
|
std::scoped_lock lock(Mutex);
|
|
return MaxTimeUs;
|
|
}
|
|
};
|
|
|
|
template<typename TName> struct FunctionWrapper : FunctionInternal
|
|
{
|
|
const char* GetName() const noexcept override
|
|
{
|
|
return TName::Str();
|
|
}
|
|
};
|
|
|
|
// Wrapper to avoid static initialization inside the function.
|
|
// This avoids the compiler generating thread-safe initialization
|
|
// by making a unique type per function which hosts a global using
|
|
// the inline keyword for the variable (C++17).
|
|
template<typename TName> struct Storage
|
|
{
|
|
static inline FunctionWrapper<TName> Data;
|
|
};
|
|
|
|
void FunctionEnter(Function& func);
|
|
void FunctionExit(Function& func);
|
|
|
|
} // namespace Detail
|
|
|
|
template<typename T> class ScopedProfiling
|
|
{
|
|
bool _enabled;
|
|
T& _func;
|
|
|
|
public:
|
|
ScopedProfiling(T& func)
|
|
: _enabled{ IsEnabled() }
|
|
, _func(func)
|
|
{
|
|
if (_enabled)
|
|
{
|
|
Detail::FunctionEnter(_func);
|
|
}
|
|
}
|
|
~ScopedProfiling()
|
|
{
|
|
if (!_enabled)
|
|
return;
|
|
Detail::FunctionExit(_func);
|
|
}
|
|
};
|
|
|
|
// Clears all the current data of each function.
|
|
void ResetData();
|
|
|
|
// Returns all functions.
|
|
const std::vector<Function*>& GetData();
|
|
|
|
bool ExportCSV(const std::string& filePath);
|
|
|
|
} // namespace OpenRCT2::Profiling
|