2025-01-22 16:18:30 +01:00

287 lines
6.9 KiB
C++

#include <hxcpp.h>
#include <map>
#include <vector>
#include <hx/Thread.h>
#include <hx/OS.h>
#ifdef HX_WINRT
#define PROFILE_PRINT WINRT_LOG
#else
#if defined(ANDROID)
#include <android/log.h>
#define DBGLOG(...) __android_log_print(ANDROID_LOG_INFO, "HXCPP", __VA_ARGS__)
#else
#include <stdio.h>
#define DBGLOG printf
#endif
#define PROFILE_PRINT(...) \
if (out) { \
fprintf(out, __VA_ARGS__); \
} \
else { \
DBGLOG(__VA_ARGS__); \
}
#endif
namespace hx
{
// Profiler functionality separated into this class
class Profiler
{
public:
Profiler(const String &inDumpFile)
: mT0(0)
{
mDumpFile = inDumpFile;
// When a profiler exists, the profiler thread needs to exist
gThreadMutex.Lock();
gThreadRefCount += 1;
if (gThreadRefCount == 1) {
HxCreateDetachedThread(ProfileMainLoop, 0);
}
gThreadMutex.Unlock();
}
~Profiler()
{
gThreadMutex.Lock();
gThreadRefCount -= 1;
gThreadMutex.Unlock();
}
void sample(hx::StackContext *stack)
{
if (mT0 == gProfileClock) {
return;
}
// Latch the profile clock and calculate the time since the last profile
// clock tick
int clock = gProfileClock;
int delta = clock - mT0;
if (delta < 0) {
delta = 1;
}
mT0 = clock;
int depth = stack->getDepth();
std::map<const char *, bool> alreadySeen;
// Add children time in to each stack element
for (int i = 0; i < (depth - 1); i++) {
const char *fullName = stack->getFullNameAtDepth(i);
ProfileEntry &pe = mProfileStats[fullName];
if (!alreadySeen.count(fullName)) {
pe.total += delta;
alreadySeen[fullName] = true;
}
// For everything except the very bottom of the stack, add the time to
// that child's total with this entry
pe.children[stack->getFullNameAtDepth(i + 1)] += delta;
}
// Add the time into the actual function being executed
if (depth > 0) {
mProfileStats[stack->getFullNameAtDepth(depth - 1)].self += delta;
}
}
void DumpStats()
{
FILE *out = 0;
if (mDumpFile.length > 0)
{
out = fopen(mDumpFile.c_str(), "wb");
if (!out)
{
return;
}
}
std::vector<ResultsEntry> results;
results.reserve(mProfileStats.size());
int total = 0;
std::map<const char *, ProfileEntry>::iterator iter =
mProfileStats.begin();
while (iter != mProfileStats.end()) {
ProfileEntry &pe = iter->second;
ResultsEntry re;
re.fullName = iter->first;
re.self = pe.self;
re.total = pe.total;
re.childrenPlusSelf = re.self;
ChildEntry internal;
internal.fullName = "(internal)";
internal.self = re.self;
std::map<const char *, int>::iterator childIter =
pe.children.begin();
int childTotal = 0;
while (childIter != pe.children.end()) {
ChildEntry ce;
ce.fullName = childIter->first;
ce.self = childIter->second;
re.childrenPlusSelf += ce.self;
re.children.push_back(ce);
childIter++;
}
re.children.push_back(internal);
std::sort(re.children.begin(), re.children.end());
results.push_back(re);
total += re.self;
iter++;
}
std::sort(results.begin(), results.end());
double scale = total ? (100.0 / total) : 1.0;
int size = results.size();
for (int i = 0; i < size; i++) {
ResultsEntry &re = results[i];
PROFILE_PRINT("%s %.2f%%/%.2f%%\n", re.fullName, re.total * scale,
re.self * scale);
if (re.children.size() == 1) {
continue;
}
int childrenSize = re.children.size();
for (int j = 0; j < childrenSize; j++) {
ChildEntry &ce = re.children[j];
PROFILE_PRINT(" %s %.1f%%\n", ce.fullName,
(100.0 * ce.self) / re.childrenPlusSelf);
}
}
if (out) {
fclose(out);
}
}
private:
struct ProfileEntry
{
ProfileEntry()
: self(0), total(0)
{
}
int self;
std::map<const char *, int> children;
int total;
};
struct ChildEntry
{
bool operator <(const ChildEntry &inRHS) const
{
return self > inRHS.self;
}
const char *fullName;
int self;
};
struct ResultsEntry
{
bool operator <(const ResultsEntry &inRHS) const
{
return ((total > inRHS.total) ||
((total == inRHS.total) && (self < inRHS.self)));
}
const char *fullName;
int self;
std::vector<ChildEntry> children;
int total;
int childrenPlusSelf;
};
static THREAD_FUNC_TYPE ProfileMainLoop(void *)
{
int millis = 1;
while (gThreadRefCount > 0) {
HxSleep(millis);
int count = gProfileClock + 1;
gProfileClock = (count < 0) ? 0 : count;
}
THREAD_FUNC_RET
}
String mDumpFile;
int mT0;
std::map<const char *, ProfileEntry> mProfileStats;
static HxMutex gThreadMutex;
static int gThreadRefCount;
static int gProfileClock;
};
/* static */ HxMutex Profiler::gThreadMutex;
/* static */ int Profiler::gThreadRefCount;
/* static */ int Profiler::gProfileClock;
void profDestroy(Profiler * prof)
{
delete prof;
}
void profAttach(Profiler *prof, StackContext *ctx)
{
}
void profDetach(Profiler *, StackContext *ctx)
{
delete ctx->mProfiler;
ctx->mProfiler = 0;
}
void profSample(Profiler *prof, StackContext *ctx)
{
prof->sample(ctx);
}
} // end namespace hx
void __hxcpp_start_profiler(String inDumpFile)
{
hx::StackContext *stack = hx::StackContext::getCurrent();
delete stack->mProfiler;
stack->mProfiler = new hx::Profiler(inDumpFile);
}
void __hxcpp_stop_profiler()
{
hx::StackContext *stack = hx::StackContext::getCurrent();
stack->mProfiler->DumpStats();
delete stack->mProfiler;
stack->mProfiler = 0;
}