Torque3D/Engine/source/platform/profiler.cpp
Thomas "elfprince13" Dickerson bcc5459818 whitespace
2017-01-11 23:34:46 -05:00

780 lines
24 KiB
C++

//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#if defined(TORQUE_OS_WIN)
#include<Windows.h> // for SetThreadAffinityMask
#endif
#if defined(TORQUE_OS_MAC)
#include <mach/mach_time.h>
#endif
#include "core/stream/fileStream.h"
#include "core/frameAllocator.h"
#include "core/strings/stringFunctions.h"
#include "core/stringTable.h"
#include "platform/profiler.h"
#include "platform/threads/thread.h"
#include "console/engineAPI.h"
#ifdef TORQUE_ENABLE_PROFILER
ProfilerRootData *ProfilerRootData::sRootList = NULL;
Profiler *gProfiler = NULL;
// Uncomment the following line to enable a debugging aid for mismatched profiler blocks.
//#define TORQUE_PROFILER_DEBUG
// Machinery to record the stack of node names, as a debugging aid to find
// mismatched PROFILE_START and PROFILE_END blocks. We profile from the
// beginning to catch profile block errors that occur when torque is starting up.
#ifdef TORQUE_PROFILER_DEBUG
Vector<StringTableEntry> gProfilerNodeStack;
#define TORQUE_PROFILE_AT_ENGINE_START true
#define PROFILER_DEBUG_PUSH_NODE( nodename ) \
gProfilerNodeStack.push_back( nodename );
#define PROFILER_DEBUG_POP_NODE() \
gProfilerNodeStack.pop_back();
#else
#define TORQUE_PROFILE_AT_ENGINE_START false
#define PROFILER_DEBUG_PUSH_NODE( nodename ) ;
#define PROFILER_DEBUG_POP_NODE() ;
#endif
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
// platform specific get hires times...
void startHighResolutionTimer(U32 time[2])
{
//time[0] = Platform::getRealMilliseconds();
__asm
{
push eax
push edx
push ecx
rdtsc
mov ecx, time
mov DWORD PTR [ecx], eax
mov DWORD PTR [ecx + 4], edx
pop ecx
pop edx
pop eax
}
}
U32 endHighResolutionTimer(U32 time[2])
{
U32 ticks;
//ticks = Platform::getRealMilliseconds() - time[0];
//return ticks;
__asm
{
push eax
push edx
push ecx
//db 0fh, 31h
rdtsc
mov ecx, time
sub edx, DWORD PTR [ecx+4]
sbb eax, DWORD PTR [ecx]
mov DWORD PTR ticks, eax
pop ecx
pop edx
pop eax
}
return ticks;
}
#elif defined(TORQUE_SUPPORTS_GCC_INLINE_X86_ASM)
// platform specific get hires times...
void startHighResolutionTimer(U32 time[2])
{
__asm__ __volatile__(
"rdtsc\n"
: "=a" (time[0]), "=d" (time[1])
);
}
U32 endHighResolutionTimer(U32 time[2])
{
U32 ticks;
__asm__ __volatile__(
"rdtsc\n"
"sub 0x4(%%ecx), %%edx\n"
"sbb (%%ecx), %%eax\n"
: "=a" (ticks) : "c" (time)
);
return ticks;
}
#elif defined(TORQUE_OS_MAC)
void startHighResolutionTimer(U32 time[2]) {
U64 now = mach_absolute_time();
AssertFatal(sizeof(U32[2]) == sizeof(U64), "Can't pack mach_absolute_time into U32[2]");
memcpy(time, &now, sizeof(U64));
}
U32 endHighResolutionTimer(U32 time[2]) {
static mach_timebase_info_data_t sTimebaseInfo = {0, 0};
U64 now = mach_absolute_time();
AssertFatal(sizeof(U32[2]) == sizeof(U64), "Can't pack mach_absolute_time into U32[2]");
U64 then;
memcpy(&then, time, sizeof(U64));
if(sTimebaseInfo.denom == 0){
mach_timebase_info(&sTimebaseInfo);
}
// Handle the micros/nanos conversion first, because shedding a few bits is better than overflowing.
U64 elapsedMicros = ((now - then) / 1000) * sTimebaseInfo.numer / sTimebaseInfo.denom;
return (U32)elapsedMicros; // Just truncate, and hope we didn't overflow
}
#else
void startHighResolutionTimer(U32 time[2])
{
time[0] = Platform::getRealMilliseconds();
}
U32 endHighResolutionTimer(U32 time[2])
{
U32 ticks = Platform::getRealMilliseconds() - time[0];
return ticks;
}
#endif
Profiler::Profiler()
{
mMaxStackDepth = MaxStackDepth;
mCurrentHash = 0;
mCurrentProfilerData = (ProfilerData *) malloc(sizeof(ProfilerData));
mCurrentProfilerData->mRoot = NULL;
mCurrentProfilerData->mNextForRoot = NULL;
mCurrentProfilerData->mNextProfilerData = NULL;
mCurrentProfilerData->mNextHash = NULL;
mCurrentProfilerData->mParent = NULL;
mCurrentProfilerData->mNextSibling = NULL;
mCurrentProfilerData->mFirstChild = NULL;
mCurrentProfilerData->mLastSeenProfiler = NULL;
mCurrentProfilerData->mHash = 0;
mCurrentProfilerData->mSubDepth = 0;
mCurrentProfilerData->mInvokeCount = 0;
mCurrentProfilerData->mTotalTime = 0;
mCurrentProfilerData->mSubTime = 0;
#ifdef TORQUE_ENABLE_PROFILE_PATH
mCurrentProfilerData->mPath = "";
#endif
mRootProfilerData = mCurrentProfilerData;
for(U32 i = 0; i < ProfilerData::HashTableSize; i++)
mCurrentProfilerData->mChildHash[i] = 0;
mProfileList = NULL;
mEnabled = TORQUE_PROFILE_AT_ENGINE_START;
mNextEnable = TORQUE_PROFILE_AT_ENGINE_START;
mStackDepth = 0;
gProfiler = this;
mDumpToConsole = false;
mDumpToFile = false;
mDumpFileName[0] = '\0';
}
Profiler::~Profiler()
{
reset();
free(mRootProfilerData);
gProfiler = NULL;
}
void Profiler::reset()
{
mEnabled = false; // in case we're in a profiler call.
ProfilerData * head = mProfileList;
ProfilerData * curr = head;
while ( curr )
{
head = curr->mNextProfilerData;
free( curr );
if ( head )
curr = head;
else
curr = NULL;
}
mProfileList = NULL;
for(ProfilerRootData *walk = ProfilerRootData::sRootList; walk; walk = walk->mNextRoot)
{
walk->mFirstProfilerData = 0;
walk->mTotalTime = 0;
walk->mSubTime = 0;
walk->mTotalInvokeCount = 0;
}
mCurrentProfilerData = mRootProfilerData;
mCurrentProfilerData->mNextForRoot = 0;
mCurrentProfilerData->mFirstChild = 0;
for(U32 i = 0; i < ProfilerData::HashTableSize; i++)
mCurrentProfilerData->mChildHash[i] = 0;
mCurrentProfilerData->mInvokeCount = 0;
mCurrentProfilerData->mTotalTime = 0;
mCurrentProfilerData->mSubTime = 0;
mCurrentProfilerData->mSubDepth = 0;
mCurrentProfilerData->mLastSeenProfiler = 0;
}
static Profiler aProfiler; // allocate the global profiler
ProfilerRootData::ProfilerRootData(const char *name)
{
for(ProfilerRootData *walk = sRootList; walk; walk = walk->mNextRoot)
if(!dStrcmp(walk->mName, name))
AssertFatal( false, avar( "Duplicate profile name: %s", name ) );
mName = name;
mNameHash = _StringTable::hashString(name);
mNextRoot = sRootList;
sRootList = this;
mTotalTime = 0;
mTotalInvokeCount = 0;
mFirstProfilerData = NULL;
mEnabled = true;
}
void Profiler::validate()
{
for(ProfilerRootData *walk = ProfilerRootData::sRootList; walk; walk = walk->mNextRoot)
{
for(ProfilerData *dp = walk->mFirstProfilerData; dp; dp = dp->mNextForRoot)
{
if(dp->mRoot != walk)
Platform::debugBreak();
// check if it's in the parent's list...
ProfilerData *wk;
for(wk = dp->mParent->mFirstChild; wk; wk = wk->mNextSibling)
if(wk == dp)
break;
if(!wk)
Platform::debugBreak();
for(wk = dp->mParent->mChildHash[walk->mNameHash & (ProfilerData::HashTableSize - 1)] ;
wk; wk = wk->mNextHash)
if(wk == dp)
break;
if(!wk)
Platform::debugBreak();
}
}
}
#ifdef TORQUE_ENABLE_PROFILE_PATH
const char * Profiler::getProfilePath()
{
#ifdef TORQUE_MULTITHREAD
// Ignore non-main-thread profiler activity.
if( !ThreadManager::isMainThread() )
return "[non-main thread]";
#endif
return (mEnabled && mCurrentProfilerData) ? mCurrentProfilerData->mPath : "na";
}
#endif
#ifdef TORQUE_ENABLE_PROFILE_PATH
const char * Profiler::constructProfilePath(ProfilerData * pd)
{
if (pd->mParent)
{
const bool saveEnable = gProfiler->mEnabled;
gProfiler->mEnabled = false;
const char * connector = " -> ";
U32 len = dStrlen(pd->mParent->mPath);
if (!len)
connector = "";
len += dStrlen(connector);
len += dStrlen(pd->mRoot->mName);
U32 mark = FrameAllocator::getWaterMark();
char * buf = (char*)FrameAllocator::alloc(len+1);
dStrcpy(buf,pd->mParent->mPath);
dStrcat(buf,connector);
dStrcat(buf,pd->mRoot->mName);
const char * ret = StringTable->insert(buf);
FrameAllocator::setWaterMark(mark);
gProfiler->mEnabled = saveEnable;
return ret;
}
return "root";
}
#endif
void Profiler::hashPush(ProfilerRootData *root)
{
#ifdef TORQUE_MULTITHREAD
// Ignore non-main-thread profiler activity.
if( !ThreadManager::isMainThread() )
return;
#endif
mStackDepth++;
PROFILER_DEBUG_PUSH_NODE(root->mName);
AssertFatal(mStackDepth <= mMaxStackDepth,
"Stack overflow in profiler. You may have mismatched PROFILE_START and PROFILE_ENDs");
if(!mEnabled)
return;
ProfilerData *nextProfiler = NULL;
if(!root->mEnabled || mCurrentProfilerData->mRoot == root)
{
mCurrentProfilerData->mSubDepth++;
return;
}
if(mCurrentProfilerData->mLastSeenProfiler &&
mCurrentProfilerData->mLastSeenProfiler->mRoot == root)
nextProfiler = mCurrentProfilerData->mLastSeenProfiler;
if(!nextProfiler)
{
// first see if it's in the hash table...
U32 index = root->mNameHash & (ProfilerData::HashTableSize - 1);
nextProfiler = mCurrentProfilerData->mChildHash[index];
while(nextProfiler)
{
if(nextProfiler->mRoot == root)
break;
nextProfiler = nextProfiler->mNextHash;
}
if(!nextProfiler)
{
nextProfiler = (ProfilerData *) malloc(sizeof(ProfilerData));
for(U32 i = 0; i < ProfilerData::HashTableSize; i++)
nextProfiler->mChildHash[i] = 0;
nextProfiler->mRoot = root;
nextProfiler->mNextForRoot = root->mFirstProfilerData;
root->mFirstProfilerData = nextProfiler;
nextProfiler->mNextProfilerData = mProfileList;
mProfileList = nextProfiler;
nextProfiler->mNextHash = mCurrentProfilerData->mChildHash[index];
mCurrentProfilerData->mChildHash[index] = nextProfiler;
nextProfiler->mParent = mCurrentProfilerData;
nextProfiler->mNextSibling = mCurrentProfilerData->mFirstChild;
mCurrentProfilerData->mFirstChild = nextProfiler;
nextProfiler->mFirstChild = NULL;
nextProfiler->mLastSeenProfiler = NULL;
nextProfiler->mHash = root->mNameHash;
nextProfiler->mInvokeCount = 0;
nextProfiler->mTotalTime = 0;
nextProfiler->mSubTime = 0;
nextProfiler->mSubDepth = 0;
#ifdef TORQUE_ENABLE_PROFILE_PATH
nextProfiler->mPath = constructProfilePath(nextProfiler);
#endif
}
}
root->mTotalInvokeCount++;
nextProfiler->mInvokeCount++;
startHighResolutionTimer(nextProfiler->mStartTime);
mCurrentProfilerData->mLastSeenProfiler = nextProfiler;
mCurrentProfilerData = nextProfiler;
}
void Profiler::enable(bool enabled)
{
mNextEnable = enabled;
}
void Profiler::dumpToConsole()
{
mDumpToConsole = true;
mDumpToFile = false;
mDumpFileName[0] = '\0';
}
void Profiler::dumpToFile(const char* fileName)
{
AssertFatal(dStrlen(fileName) < DumpFileNameLength, "Error, dump filename too long");
mDumpToFile = true;
mDumpToConsole = false;
dStrcpy(mDumpFileName, fileName);
}
void Profiler::hashPop(ProfilerRootData *expected)
{
#ifdef TORQUE_MULTITHREAD
// Ignore non-main-thread profiler activity.
if( !ThreadManager::isMainThread() )
return;
#endif
mStackDepth--;
PROFILER_DEBUG_POP_NODE();
AssertFatal(mStackDepth >= 0, "Stack underflow in profiler. You may have mismatched PROFILE_START and PROFILE_ENDs");
if(mEnabled)
{
if(mCurrentProfilerData->mSubDepth)
{
mCurrentProfilerData->mSubDepth--;
return;
}
if(expected)
{
AssertISV(expected == mCurrentProfilerData->mRoot, "Profiler::hashPop - didn't get expected ProfilerRoot!");
}
F64 fElapsed = endHighResolutionTimer(mCurrentProfilerData->mStartTime);
mCurrentProfilerData->mTotalTime += fElapsed;
mCurrentProfilerData->mParent->mSubTime += fElapsed; // mark it in the parent as well...
mCurrentProfilerData->mRoot->mTotalTime += fElapsed;
if(mCurrentProfilerData->mParent->mRoot)
mCurrentProfilerData->mParent->mRoot->mSubTime += fElapsed; // mark it in the parent as well...
mCurrentProfilerData = mCurrentProfilerData->mParent;
}
if(mStackDepth == 0)
{
// apply the next enable...
if(mDumpToConsole || mDumpToFile)
{
dump();
startHighResolutionTimer(mCurrentProfilerData->mStartTime);
}
if(!mEnabled && mNextEnable)
startHighResolutionTimer(mCurrentProfilerData->mStartTime);
#if defined(TORQUE_OS_WIN)
// The high performance counters under win32 are unreliable when running on multiple
// processors. When the profiler is enabled, we restrict Torque to a single processor.
if(mNextEnable != mEnabled)
{
if(mNextEnable)
{
Con::warnf("Warning: forcing the Torque profiler thread to run only on cpu 1.");
SetThreadAffinityMask(GetCurrentThread(), 1);
}
else
{
Con::warnf("Warning: the Torque profiler thread may now run on any cpu.");
DWORD_PTR procMask;
DWORD_PTR sysMask;
GetProcessAffinityMask( GetCurrentProcess(), &procMask, &sysMask);
SetThreadAffinityMask( GetCurrentThread(), procMask);
}
}
#endif
mEnabled = mNextEnable;
}
}
static S32 QSORT_CALLBACK rootDataCompare(const void *s1, const void *s2)
{
const ProfilerRootData *r1 = *((ProfilerRootData **) s1);
const ProfilerRootData *r2 = *((ProfilerRootData **) s2);
if((r2->mTotalTime - r2->mSubTime) > (r1->mTotalTime - r1->mSubTime))
return 1;
return -1;
}
static void profilerDataDumpRecurse(ProfilerData *data, char *buffer, U32 bufferLen, F64 totalTime)
{
// dump out this one:
Con::printf("%7.3f %7.3f %8d %s%s",
100 * data->mTotalTime / totalTime,
100 * (data->mTotalTime - data->mSubTime) / totalTime,
data->mInvokeCount,
buffer,
data->mRoot ? data->mRoot->mName : "ROOT" );
data->mTotalTime = 0;
data->mSubTime = 0;
data->mInvokeCount = 0;
buffer[bufferLen] = ' ';
buffer[bufferLen+1] = ' ';
buffer[bufferLen+2] = 0;
// sort data's children...
ProfilerData *list = NULL;
while(data->mFirstChild)
{
ProfilerData *ins = data->mFirstChild;
data->mFirstChild = ins->mNextSibling;
ProfilerData **walk = &list;
while(*walk && (*walk)->mTotalTime > ins->mTotalTime)
walk = &(*walk)->mNextSibling;
ins->mNextSibling = *walk;
*walk = ins;
}
data->mFirstChild = list;
while(list)
{
if(list->mInvokeCount)
profilerDataDumpRecurse(list, buffer, bufferLen + 2, totalTime);
list = list->mNextSibling;
}
buffer[bufferLen] = 0;
}
static void profilerDataDumpRecurseFile(ProfilerData *data, char *buffer, U32 bufferLen, F64 totalTime, FileStream& fws)
{
char pbuffer[256];
dSprintf(pbuffer, 255, "%7.3f %7.3f %8d %s%s\n",
100 * data->mTotalTime / totalTime,
100 * (data->mTotalTime - data->mSubTime) / totalTime,
data->mInvokeCount,
buffer,
data->mRoot ? data->mRoot->mName : "ROOT" );
fws.write(dStrlen(pbuffer), pbuffer);
data->mTotalTime = 0;
data->mSubTime = 0;
data->mInvokeCount = 0;
buffer[bufferLen] = ' ';
buffer[bufferLen+1] = ' ';
buffer[bufferLen+2] = 0;
// sort data's children...
ProfilerData *list = NULL;
while(data->mFirstChild)
{
ProfilerData *ins = data->mFirstChild;
data->mFirstChild = ins->mNextSibling;
ProfilerData **walk = &list;
while(*walk && (*walk)->mTotalTime > ins->mTotalTime)
walk = &(*walk)->mNextSibling;
ins->mNextSibling = *walk;
*walk = ins;
}
data->mFirstChild = list;
while(list)
{
if(list->mInvokeCount)
profilerDataDumpRecurseFile(list, buffer, bufferLen + 2, totalTime, fws);
list = list->mNextSibling;
}
buffer[bufferLen] = 0;
}
void Profiler::dump()
{
bool enableSave = mEnabled;
mEnabled = false;
mStackDepth++;
// may have some profiled calls... gotta turn em off.
Vector<ProfilerRootData *> rootVector;
F64 totalTime = 0;
for(ProfilerRootData *walk = ProfilerRootData::sRootList; walk; walk = walk->mNextRoot)
{
totalTime += walk->mTotalTime - walk->mSubTime;
rootVector.push_back(walk);
}
dQsort((void *) &rootVector[0], rootVector.size(), sizeof(ProfilerRootData *), rootDataCompare);
if (mDumpToConsole == true)
{
Con::printf("Profiler Data Dump:");
Con::printf("Ordered by non-sub total time -");
Con::printf("%%NSTime %% Time Invoke # Name");
for(U32 i = 0; i < rootVector.size(); i++)
{
Con::printf("%7.3f %7.3f %8d %s",
100 * (rootVector[i]->mTotalTime - rootVector[i]->mSubTime) / totalTime,
100 * rootVector[i]->mTotalTime / totalTime,
rootVector[i]->mTotalInvokeCount,
rootVector[i]->mName);
rootVector[i]->mTotalInvokeCount = 0;
rootVector[i]->mTotalTime = 0;
rootVector[i]->mSubTime = 0;
}
Con::printf("");
Con::printf("Ordered by stack trace total time -");
Con::printf("%% Time %% NSTime Invoke # Name");
mCurrentProfilerData->mTotalTime = endHighResolutionTimer(mCurrentProfilerData->mStartTime);
char depthBuffer[MaxStackDepth * 2 + 1];
depthBuffer[0] = 0;
profilerDataDumpRecurse(mCurrentProfilerData, depthBuffer, 0, totalTime);
mEnabled = enableSave;
mStackDepth--;
}
else if (mDumpToFile == true && mDumpFileName[0] != '\0')
{
FileStream fws;
bool success = fws.open(mDumpFileName, Torque::FS::File::Write);
AssertFatal(success, "Cannot write profile dump to specified file!");
char buffer[1024];
dStrcpy(buffer, "Profiler Data Dump:\n");
fws.write(dStrlen(buffer), buffer);
dStrcpy(buffer, "Ordered by non-sub total time -\n");
fws.write(dStrlen(buffer), buffer);
dStrcpy(buffer, "%%NSTime %% Time Invoke # Name\n");
fws.write(dStrlen(buffer), buffer);
for(U32 i = 0; i < rootVector.size(); i++)
{
dSprintf(buffer, 1023, "%7.3f %7.3f %8d %s\n",
100 * (rootVector[i]->mTotalTime - rootVector[i]->mSubTime) / totalTime,
100 * rootVector[i]->mTotalTime / totalTime,
rootVector[i]->mTotalInvokeCount,
rootVector[i]->mName);
fws.write(dStrlen(buffer), buffer);
rootVector[i]->mTotalInvokeCount = 0;
rootVector[i]->mTotalTime = 0;
rootVector[i]->mSubTime = 0;
}
dStrcpy(buffer, "\nOrdered by non-sub total time -\n");
fws.write(dStrlen(buffer), buffer);
dStrcpy(buffer, "%%NSTime %% Time Invoke # Name\n");
fws.write(dStrlen(buffer), buffer);
mCurrentProfilerData->mTotalTime = endHighResolutionTimer(mCurrentProfilerData->mStartTime);
char depthBuffer[MaxStackDepth * 2 + 1];
depthBuffer[0] = 0;
profilerDataDumpRecurseFile(mCurrentProfilerData, depthBuffer, 0, totalTime, fws);
mEnabled = enableSave;
mStackDepth--;
fws.close();
}
mDumpToConsole = false;
mDumpToFile = false;
mDumpFileName[0] = '\0';
}
void Profiler::enableMarker(const char *marker, bool enable)
{
reset();
U32 markerLen = dStrlen(marker);
if(markerLen == 0)
return;
bool sn = marker[markerLen - 1] == '*';
for(ProfilerRootData *data = ProfilerRootData::sRootList; data; data = data->mNextRoot)
{
if(sn)
{
if(!dStrncmp(marker, data->mName, markerLen - 1))
data->mEnabled = enable;
}
else
{
if(!dStrcmp(marker, data->mName))
data->mEnabled = enable;
}
}
}
//=============================================================================
// Console Functions.
//=============================================================================
// MARK: ---- Console Functions ----
//-----------------------------------------------------------------------------
DefineEngineFunction( profilerMarkerEnable, void, ( const char* markerName, bool enable ), ( true ),
"@brief Enable or disable a specific profile.\n\n"
"@param enable Optional paramater to enable or disable the profile.\n"
"@param markerName Name of a specific marker to enable or disable.\n"
"@note Calling this function will first call profilerReset(), clearing all data from profiler. "
"All profile markers are enabled by default.\n\n"
"@ingroup Debugging")
{
if( gProfiler )
gProfiler->enableMarker( markerName, enable );
}
//-----------------------------------------------------------------------------
DefineEngineFunction( profilerEnable, void, ( bool enable ),,
"@brief Enables or disables the profiler.\n\n"
"Data is only gathered while the profiler is enabled.\n\n"
"@note Profiler is not available in shipping builds.\n"
"T3D has predefined profiling areas surrounded by markers, "
"but you may need to define additional markers (in C++) around areas you wish to profile,"
" by using the PROFILE_START( markerName ); and PROFILE_END(); macros.\n\n"
"@ingroup Debugging\n" )
{
if(gProfiler)
gProfiler->enable(enable);
}
DefineEngineFunction(profilerDump, void, (),,
"@brief Dumps current profiling stats to the console window.\n\n"
"@note Markers disabled with profilerMarkerEnable() will be skipped over. "
"If the profiler is currently running, it will be disabled.\n"
"@ingroup Debugging")
{
if(gProfiler)
gProfiler->dumpToConsole();
}
DefineEngineFunction( profilerDumpToFile, void, ( const char* fileName ),,
"@brief Dumps current profiling stats to a file.\n\n"
"@note If the profiler is currently running, it will be disabled.\n"
"@param fileName Name and path of file to save profiling stats to. Must use forward slashes (/). "
"Will attempt to create the file if it does not already exist.\n"
"@tsexample\n"
"profilerDumpToFile( \"C:/Torque/log1.txt\" );\n"
"@endtsexample\n\n"
"@ingroup Debugging" )
{
if(gProfiler)
gProfiler->dumpToFile(fileName);
}
DefineEngineFunction( profilerReset, void, (),,
"@brief Resets the profiler, clearing it of all its data.\n\n"
"If the profiler is currently running, it will first be disabled. "
"All markers will retain their current enabled/disabled status.\n\n"
"@ingroup Debugging" )
{
if(gProfiler)
gProfiler->reset();
}
#endif