Update Files
This commit is contained in:
286
Kha/Backends/Kinc-hxcpp/khacpp/src/hx/Profiler.cpp
Normal file
286
Kha/Backends/Kinc-hxcpp/khacpp/src/hx/Profiler.cpp
Normal file
@ -0,0 +1,286 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user