mirror of
https://github.com/1dot13/source.git
synced 2026-08-19 14:20:30 +02:00
Added profiler support
Fixed memory leak and CTD with editor summary info Added extra VS2K5 property pages for debug support git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@1528 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
+60
-18
@@ -95,7 +95,7 @@ INT32 giCurrentViewLevel = ALL_LEVELS_MASK;
|
|||||||
BOOLEAN gbSectorLevels[16][16];
|
BOOLEAN gbSectorLevels[16][16];
|
||||||
BOOLEAN gfGlobalSummaryLoaded = FALSE;
|
BOOLEAN gfGlobalSummaryLoaded = FALSE;
|
||||||
|
|
||||||
SUMMARYFILE *gpSectorSummary[16][16][8];
|
SUMMARYFILE *gpSectorSummary[16][16][8] = {};
|
||||||
SUMMARYFILE *gpCurrentSectorSummary;
|
SUMMARYFILE *gpCurrentSectorSummary;
|
||||||
|
|
||||||
MOUSE_REGION MapRegion;
|
MOUSE_REGION MapRegion;
|
||||||
@@ -2419,6 +2419,8 @@ void LoadGlobalSummary()
|
|||||||
if( gfMustForceUpdateAllMaps )
|
if( gfMustForceUpdateAllMaps )
|
||||||
{
|
{
|
||||||
OutputDebugString( (LPCSTR)String( "A MAJOR MAP UPDATE EVENT HAS BEEN DETECTED FOR %d MAPS!!!!.\n", gusNumberOfMapsToBeForceUpdated ) );
|
OutputDebugString( (LPCSTR)String( "A MAJOR MAP UPDATE EVENT HAS BEEN DETECTED FOR %d MAPS!!!!.\n", gusNumberOfMapsToBeForceUpdated ) );
|
||||||
|
//ADB update it!
|
||||||
|
ApologizeOverrideAndForceUpdateEverything();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2503,6 +2505,11 @@ void WriteSectorSummaryUpdate( STR8 puiFilename, UINT8 ubLevel, SUMMARYFILE *pSu
|
|||||||
else
|
else
|
||||||
x = (puiFilename[ 1 ] - '0') * 10 + puiFilename[ 2 ] - '0' - 1;
|
x = (puiFilename[ 1 ] - '0') * 10 + puiFilename[ 2 ] - '0' - 1;
|
||||||
|
|
||||||
|
if( gpSectorSummary[x][y][ubLevel] )
|
||||||
|
{
|
||||||
|
MemFree( gpSectorSummary[x][y][ubLevel] );
|
||||||
|
gpSectorSummary[x][y][ubLevel] = NULL;
|
||||||
|
}
|
||||||
gpSectorSummary[x][y][ubLevel] = pSummaryFileInfo;
|
gpSectorSummary[x][y][ubLevel] = pSummaryFileInfo;
|
||||||
|
|
||||||
// Snap: Restore the data directory once we are finished.
|
// Snap: Restore the data directory once we are finished.
|
||||||
@@ -2568,6 +2575,9 @@ void LoadSummary( STR8 pSector, UINT8 ubLevel, FLOAT dMajorMapVersion )
|
|||||||
if( !fp )
|
if( !fp )
|
||||||
{
|
{
|
||||||
gusNumEntriesWithOutdatedOrNoSummaryInfo++;
|
gusNumEntriesWithOutdatedOrNoSummaryInfo++;
|
||||||
|
//ADB don't forget that these might need to be updated!!!
|
||||||
|
gusNumberOfMapsToBeForceUpdated++;
|
||||||
|
gfMustForceUpdateAllMaps = TRUE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fread( &temp, 1, sizeof( SUMMARYFILE ), fp );
|
fread( &temp, 1, sizeof( SUMMARYFILE ), fp );
|
||||||
@@ -2731,16 +2741,23 @@ void SummaryUpdateCallback( GUI_BUTTON *btn, INT32 reason )
|
|||||||
SetProgressBarTitle( 0, L"Generating map summary", BLOCKFONT2, FONT_RED, FONT_NEARBLACK );
|
SetProgressBarTitle( 0, L"Generating map summary", BLOCKFONT2, FONT_RED, FONT_NEARBLACK );
|
||||||
SetProgressBarMsgAttributes( 0, SMALLCOMPFONT, FONT_BLACK, FONT_BLACK );
|
SetProgressBarMsgAttributes( 0, SMALLCOMPFONT, FONT_BLACK, FONT_BLACK );
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// 0verhaul: This should NOT be freed. An array index can be freed when it is recalculated,
|
||||||
|
// as this function is about to do. And then it SHOULD be freed first, which it wasn't doing.
|
||||||
|
// Either way, using this pointer is not a safe way to free the current sector's summary data.
|
||||||
if( gpCurrentSectorSummary )
|
if( gpCurrentSectorSummary )
|
||||||
{
|
{
|
||||||
MemFree( gpCurrentSectorSummary );
|
MemFree( gpCurrentSectorSummary );
|
||||||
gpCurrentSectorSummary = NULL;
|
gpCurrentSectorSummary = NULL;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
sprintf( str, "%c%d", gsSelSectorY + 'A' - 1, gsSelSectorX );
|
sprintf( str, "%c%d", gsSelSectorY + 'A' - 1, gsSelSectorX );
|
||||||
EvaluateWorld( str, (UINT8)giCurrLevel );
|
EvaluateWorld( str, (UINT8)giCurrLevel );
|
||||||
|
|
||||||
gpSectorSummary[ gsSelSectorX ][ gsSelSectorY ][ giCurrLevel ] = gpCurrentSectorSummary;
|
// ADB: The current sector summary should be set to the array value, not the other way around.
|
||||||
|
// Also the sector X and Y values are 1-based and the array is 0-based. So subtract 1 from X and Y
|
||||||
|
gpCurrentSectorSummary = gpSectorSummary[ gsSelSectorX - 1][ gsSelSectorY - 1][ giCurrLevel ];
|
||||||
|
|
||||||
gfRenderSummary = TRUE;
|
gfRenderSummary = TRUE;
|
||||||
|
|
||||||
@@ -2765,7 +2782,7 @@ void ExtractTempFilename()
|
|||||||
void ApologizeOverrideAndForceUpdateEverything()
|
void ApologizeOverrideAndForceUpdateEverything()
|
||||||
{
|
{
|
||||||
INT32 x, y;
|
INT32 x, y;
|
||||||
CHAR16 str[ 50 ];
|
CHAR16 str[ 128 ];
|
||||||
CHAR8 name[50];
|
CHAR8 name[50];
|
||||||
SUMMARYFILE *pSF;
|
SUMMARYFILE *pSF;
|
||||||
//Create one huge assed button
|
//Create one huge assed button
|
||||||
@@ -2883,7 +2900,8 @@ void ApologizeOverrideAndForceUpdateEverything()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EvaluateWorld( "p3_m.dat", 0 );
|
// ADB: The call to EvaluateWorld does not use the .dat extension
|
||||||
|
EvaluateWorld( "p3_m", 0 );
|
||||||
|
|
||||||
RemoveProgressBar( 2 );
|
RemoveProgressBar( 2 );
|
||||||
gfUpdatingNow = FALSE;
|
gfUpdatingNow = FALSE;
|
||||||
@@ -2933,7 +2951,6 @@ void SetupItemDetailsMode( BOOLEAN fAllowRecursion )
|
|||||||
if( gpCurrentSectorSummary->ubSummaryVersion == GLOBAL_SUMMARY_VERSION )
|
if( gpCurrentSectorSummary->ubSummaryVersion == GLOBAL_SUMMARY_VERSION )
|
||||||
gusNumEntriesWithOutdatedOrNoSummaryInfo++;
|
gusNumEntriesWithOutdatedOrNoSummaryInfo++;
|
||||||
SummaryUpdateCallback( ButtonList[ iSummaryButton[ SUMMARY_UPDATE ] ], MSYS_CALLBACK_REASON_LBUTTON_UP );
|
SummaryUpdateCallback( ButtonList[ iSummaryButton[ SUMMARY_UPDATE ] ], MSYS_CALLBACK_REASON_LBUTTON_UP );
|
||||||
gpCurrentSectorSummary = gpSectorSummary[ gsSelSectorX - 1 ][ gsSelSectorY - 1 ][ giCurrLevel ];
|
|
||||||
}
|
}
|
||||||
//Open the original map for the sector
|
//Open the original map for the sector
|
||||||
sprintf( szFilename, "MAPS\\%S", gszFilename );
|
sprintf( szFilename, "MAPS\\%S", gszFilename );
|
||||||
@@ -2942,21 +2959,28 @@ void SetupItemDetailsMode( BOOLEAN fAllowRecursion )
|
|||||||
{ //The file couldn't be found!
|
{ //The file couldn't be found!
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//Now fileseek directly to the file position where the number of world items are stored
|
// ADB: The uiNumItemsPosition may be 0 here due to the recursion further down.
|
||||||
if( !FileSeek( hfile, gpCurrentSectorSummary->uiNumItemsPosition, FILE_SEEK_FROM_START ) )
|
// If so, skip the read
|
||||||
{ //Position couldn't be found!
|
uiNumItems = 0;
|
||||||
FileClose( hfile );
|
if (gpCurrentSectorSummary->uiNumItemsPosition)
|
||||||
return;
|
{
|
||||||
}
|
//Now fileseek directly to the file position where the number of world items are stored
|
||||||
//Now load the number of world items from the map.
|
if( !FileSeek( hfile, gpCurrentSectorSummary->uiNumItemsPosition, FILE_SEEK_FROM_START ) )
|
||||||
FileRead( hfile, &uiNumItems, 4, &uiNumBytesRead );
|
{ //Position couldn't be found!
|
||||||
if( uiNumBytesRead != 4 )
|
FileClose( hfile );
|
||||||
{ //Invalid situation.
|
return;
|
||||||
FileClose( hfile );
|
}
|
||||||
return;
|
//Now load the number of world items from the map.
|
||||||
|
FileRead( hfile, &uiNumItems, 4, &uiNumBytesRead );
|
||||||
|
if( uiNumBytesRead != 4 )
|
||||||
|
{ //Invalid situation.
|
||||||
|
FileClose( hfile );
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Now compare this number with the number the summary thinks we should have. If they are different,
|
//Now compare this number with the number the summary thinks we should have. If they are different,
|
||||||
//the the summary doesn't match the map. What we will do is force regenerate the map so that they do
|
//then the summary doesn't match the map. What we will do is force regenerate the map so that they do
|
||||||
//match
|
//match
|
||||||
if( uiNumItems != gpCurrentSectorSummary->usNumItems && fAllowRecursion )
|
if( uiNumItems != gpCurrentSectorSummary->usNumItems && fAllowRecursion )
|
||||||
{
|
{
|
||||||
@@ -3108,4 +3132,22 @@ UINT8 GetCurrentSummaryVersion()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClearSummaryInfo()
|
||||||
|
{
|
||||||
|
for (int x = 0; x < 16; x++)
|
||||||
|
{
|
||||||
|
for (int y=0; y < 16; y++)
|
||||||
|
{
|
||||||
|
for (int z=0; z < 8; z++)
|
||||||
|
{
|
||||||
|
if (gpSectorSummary[x][y][z])
|
||||||
|
{
|
||||||
|
MemFree( gpSectorSummary[x][y][z]);
|
||||||
|
gpSectorSummary[x][y][z] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -104,6 +104,7 @@ extern void WriteSectorSummaryUpdate( STR8 puiFilename, UINT8 ubLevel, SUMMARYFI
|
|||||||
extern BOOLEAN gfMustForceUpdateAllMaps;
|
extern BOOLEAN gfMustForceUpdateAllMaps;
|
||||||
extern BOOLEAN gfMajorUpdate;
|
extern BOOLEAN gfMajorUpdate;
|
||||||
void ApologizeOverrideAndForceUpdateEverything();
|
void ApologizeOverrideAndForceUpdateEverything();
|
||||||
|
void ClearSummaryInfo();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+10
-3
@@ -73,6 +73,7 @@
|
|||||||
#include "Music Control.h"
|
#include "Music Control.h"
|
||||||
#include "Soldier Profile.h"
|
#include "Soldier Profile.h"
|
||||||
#include "GameSettings.h"
|
#include "GameSettings.h"
|
||||||
|
#include "Summary Info.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -305,6 +306,9 @@ BOOLEAN EditModeInit( void )
|
|||||||
fFirstTimeInEditModeInit = FALSE;
|
fFirstTimeInEditModeInit = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear the summary info array
|
||||||
|
ClearSummaryInfo();
|
||||||
|
|
||||||
//Initialize editor specific stuff for each Taskbar.
|
//Initialize editor specific stuff for each Taskbar.
|
||||||
EntryInitEditorTerrainInfo();
|
EntryInitEditorTerrainInfo();
|
||||||
EntryInitEditorItemsInfo();
|
EntryInitEditorItemsInfo();
|
||||||
@@ -557,6 +561,9 @@ BOOLEAN EditModeShutdown( void )
|
|||||||
|
|
||||||
HideExitGrids();
|
HideExitGrids();
|
||||||
|
|
||||||
|
// Clear the summary info array
|
||||||
|
ClearSummaryInfo();
|
||||||
|
|
||||||
UnPauseGame();
|
UnPauseGame();
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@@ -581,7 +588,7 @@ void SetBackgroundTexture( )
|
|||||||
usIndex = (UINT16)(rand( ) % 10 );
|
usIndex = (UINT16)(rand( ) % 10 );
|
||||||
|
|
||||||
// Adjust for type
|
// Adjust for type
|
||||||
usIndex += gTileTypeStartIndex[ gCurrentBackground ];
|
usIndex = usIndex + gTileTypeStartIndex[ gCurrentBackground ];
|
||||||
|
|
||||||
// Set land index
|
// Set land index
|
||||||
if( TypeRangeExistsInLandLayer( cnt, FIRSTFLOOR, LASTFLOOR, &Dummy ) )
|
if( TypeRangeExistsInLandLayer( cnt, FIRSTFLOOR, LASTFLOOR, &Dummy ) )
|
||||||
@@ -1557,7 +1564,7 @@ void HandleKeyboardShortcuts( )
|
|||||||
RemoveAllRoofsOfTypeRange( i, FIRSTTEXTURE, LASTITEM );
|
RemoveAllRoofsOfTypeRange( i, FIRSTTEXTURE, LASTITEM );
|
||||||
RemoveAllOnRoofsOfTypeRange( i, FIRSTTEXTURE, LASTITEM );
|
RemoveAllOnRoofsOfTypeRange( i, FIRSTTEXTURE, LASTITEM );
|
||||||
RemoveAllShadowsOfTypeRange( i, FIRSTROOF, LASTSLANTROOF );
|
RemoveAllShadowsOfTypeRange( i, FIRSTROOF, LASTSLANTROOF );
|
||||||
usRoofIndex = 9 + ( rand() % 3 );
|
usRoofIndex = (UINT16) (9 + ( rand() % 3 ));
|
||||||
GetTileIndexFromTypeSubIndex( usRoofType, usRoofIndex, &usTileIndex );
|
GetTileIndexFromTypeSubIndex( usRoofType, usRoofIndex, &usTileIndex );
|
||||||
AddRoofToHead( i, usTileIndex );
|
AddRoofToHead( i, usTileIndex );
|
||||||
}
|
}
|
||||||
@@ -3141,7 +3148,7 @@ void EnsureStatusOfEditorButtons()
|
|||||||
|
|
||||||
void HandleMouseClicksInGameScreen()
|
void HandleMouseClicksInGameScreen()
|
||||||
{
|
{
|
||||||
EXITGRID dummy={0,0,0,0};
|
//EXITGRID dummy={0,0,0,0};
|
||||||
INT16 sX, sY;
|
INT16 sX, sY;
|
||||||
BOOLEAN fPrevState;
|
BOOLEAN fPrevState;
|
||||||
if( !GetMouseXY( &sGridX, &sGridY ) )
|
if( !GetMouseXY( &sGridX, &sGridY ) )
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="Windows-1252"?>
|
||||||
|
<VisualStudioPropertySheet
|
||||||
|
ProjectType="Visual C++"
|
||||||
|
Version="8.00"
|
||||||
|
Name="Path Debug"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
PreprocessorDefinitions="PATHAI_VISIBLE_DEBUG"
|
||||||
|
/>
|
||||||
|
</VisualStudioPropertySheet>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<VisualStudioPropertySheet
|
||||||
|
ProjectType="Visual C++"
|
||||||
|
Version="8.00"
|
||||||
|
Name="Roof Debug"
|
||||||
|
>
|
||||||
|
</VisualStudioPropertySheet>
|
||||||
@@ -49,4 +49,6 @@
|
|||||||
#define _CRT_SECURE_NO_WARNINGS
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
#define _CRT_NON_CONFORMING_SWPRINTFS
|
#define _CRT_NON_CONFORMING_SWPRINTFS
|
||||||
|
|
||||||
|
#include "Profiler.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+442
@@ -0,0 +1,442 @@
|
|||||||
|
#ifdef PROFILER_ENABLED
|
||||||
|
|
||||||
|
#include "builddefines.h"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#ifdef PERIODIC_PROFILING
|
||||||
|
bool gRecordingProfile = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PerfManager* PerfManager::_instance(NULL);
|
||||||
|
|
||||||
|
PerfDatum::PerfDatum( const char* const fileName,
|
||||||
|
const char* const functionName,
|
||||||
|
const int lineNumber,
|
||||||
|
__int64 cycles,
|
||||||
|
__int64 calls)
|
||||||
|
:
|
||||||
|
_fileName(fileName),
|
||||||
|
_functionName(functionName),
|
||||||
|
_lineNumber(lineNumber),
|
||||||
|
_cycles(cycles),
|
||||||
|
_calls(calls)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfManager::PerfManager(void)
|
||||||
|
:
|
||||||
|
_callTime(0),
|
||||||
|
_totalTime(0)
|
||||||
|
{
|
||||||
|
//Set up a root function so that the stack is never empty:
|
||||||
|
//(this will be the only entry with _lineNumber equal to 0)
|
||||||
|
_perfLog.insert(PerfDatum(__FILE__, "Root",0));
|
||||||
|
_perfStack.push_back(&*(_perfLog.begin()));
|
||||||
|
_lastTime = getCPUCount();
|
||||||
|
calibrate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfManager::calibrate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfManager::~PerfManager(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfManager* PerfManager::instance()
|
||||||
|
{
|
||||||
|
if(!_instance) //Double-check locking (first check)
|
||||||
|
{ _instance = new PerfManager();
|
||||||
|
}
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfManager::enterFunction(const char* const fileName,
|
||||||
|
const char* const functionName,
|
||||||
|
const int lineNumber)
|
||||||
|
{
|
||||||
|
//Mark the time when we end processing the previous function
|
||||||
|
__int64 endTime = getCPUCount();
|
||||||
|
//assuming we haven't corrupted the stack and reduced it to zero elements
|
||||||
|
if(_perfStack.size())
|
||||||
|
{ PERF_STACK_T::reverse_iterator lastMarker = _perfStack.rbegin();
|
||||||
|
//Update the total time spent processing the previous function
|
||||||
|
__int64 runTime = (endTime - _lastTime) - _callTime;
|
||||||
|
(**lastMarker)._cycles += runTime;
|
||||||
|
_totalTime += runTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we fail to insert the performance marker into our log, it means it is
|
||||||
|
//a duplicate. We can ignore this "error" as using the existing copy is
|
||||||
|
//the correct behavior.
|
||||||
|
std::pair<PERF_LOG_T::iterator, bool> result =
|
||||||
|
_perfLog.insert(PerfDatum(fileName, functionName, lineNumber));
|
||||||
|
//Update the call count on the new function:
|
||||||
|
PerfDatum* marker = &*(result.first);
|
||||||
|
marker->_calls++;
|
||||||
|
//Add performance marker to the stack as well as the log:
|
||||||
|
_perfStack.push_back(marker);
|
||||||
|
//Now that we are clear of any complex operations, we can set the call
|
||||||
|
//time of the new marker more accurately:
|
||||||
|
_lastTime = getCPUCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfManager::exitFunction()
|
||||||
|
{
|
||||||
|
//Mark the time when we exited the function
|
||||||
|
__int64 endTime = getCPUCount();
|
||||||
|
//Update the function in our log with its new runtime
|
||||||
|
PERF_STACK_T::reverse_iterator lastMarker = _perfStack.rbegin();
|
||||||
|
__int64 runTime = endTime - _lastTime;
|
||||||
|
(**lastMarker)._cycles += runTime;
|
||||||
|
_totalTime += runTime;
|
||||||
|
_perfStack.pop_back();
|
||||||
|
//Lastly, update the function we've fallen back to with the new current
|
||||||
|
//run time.
|
||||||
|
_lastTime = getCPUCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
__int64 PerfManager::getCPUCount () const
|
||||||
|
{
|
||||||
|
_asm rdtsc
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PerfSort::operator ()(const PerfDatum& lhs, const PerfDatum&rhs) const
|
||||||
|
{
|
||||||
|
if(rhs._lineNumber == lhs._lineNumber)
|
||||||
|
{ return 0 < strcmp(rhs._fileName, lhs._fileName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ return rhs._lineNumber > lhs._lineNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PerfSort2::operator ()(const PerfDatum& lhs, const PerfDatum&rhs) const
|
||||||
|
{
|
||||||
|
return lhs._cycles > rhs._cycles;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PerfManager::getPercision(const double value) const
|
||||||
|
{
|
||||||
|
int retVal = 1;
|
||||||
|
if(10.0 <= value)
|
||||||
|
{ retVal = 3;
|
||||||
|
}
|
||||||
|
else if (1.0 <= value)
|
||||||
|
{ retVal = 2;
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfManager::log(std::ostream &os)
|
||||||
|
{
|
||||||
|
//Create a copy of the log that sorts by cycles used
|
||||||
|
std::set<PerfDatum, PerfSort2> logCopy;
|
||||||
|
for(PERF_LOG_T::iterator i = _perfLog.begin(); i != _perfLog.end(); i++)
|
||||||
|
{
|
||||||
|
logCopy.insert(*i);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cyclesWidth = 1;
|
||||||
|
//I want the formatting to be nice!
|
||||||
|
for(std::set<PerfDatum, PerfSort2>::iterator i = logCopy.begin();
|
||||||
|
i != logCopy.end();
|
||||||
|
i++)
|
||||||
|
{
|
||||||
|
if(i->_lineNumber) {
|
||||||
|
std::stringstream cyclesString;
|
||||||
|
cyclesString << i->_cycles;
|
||||||
|
std::string tempString;
|
||||||
|
cyclesString >> tempString;
|
||||||
|
cyclesWidth = tempString.size();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Stream the sorted log to the supplied ostream
|
||||||
|
for(std::set<PerfDatum, PerfSort2>::iterator i = logCopy.begin();
|
||||||
|
i != logCopy.end();
|
||||||
|
++i)
|
||||||
|
{
|
||||||
|
if(i->_lineNumber)
|
||||||
|
{ //Truncate path from file name if the compiler included it.
|
||||||
|
const char* j = i->_fileName + strlen(i->_fileName);
|
||||||
|
while('\\' != *j && '/' != *j && j >= i->_fileName){j--;}
|
||||||
|
if('\\' == *j || '/' == *j){j++;}
|
||||||
|
std::stringstream cyclesString;
|
||||||
|
std::string tempString;
|
||||||
|
cyclesString << i->_cycles;
|
||||||
|
cyclesString >> tempString;
|
||||||
|
os.width(cyclesWidth);
|
||||||
|
os << tempString.c_str() << ", ";
|
||||||
|
__int64 cyclesPerCall = i->_cycles / i->_calls;
|
||||||
|
cyclesString.clear();
|
||||||
|
tempString.clear();
|
||||||
|
cyclesString << cyclesPerCall;
|
||||||
|
cyclesString >> tempString;
|
||||||
|
os.width(cyclesWidth);
|
||||||
|
os << tempString.c_str() << "] cycles (";
|
||||||
|
double percent = double(i->_cycles) / double(_totalTime) * 100;
|
||||||
|
os.precision(getPercision(percent));
|
||||||
|
os.width(4);
|
||||||
|
os << percent << "%) used in [";
|
||||||
|
os.width(8);
|
||||||
|
os << i->_calls << "] calls by " << j << "::" << i->_functionName\
|
||||||
|
<< " at line " << i->_lineNumber << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfMarker::PerfMarker( const char* const fileName,
|
||||||
|
const char* const functionName,
|
||||||
|
const int lineNumber)
|
||||||
|
:
|
||||||
|
#ifdef PERIODIC_PROFILING
|
||||||
|
_onStack(gRecordingProfile)
|
||||||
|
#else
|
||||||
|
_onStack(true)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#ifdef PERIODIC_PROFILING
|
||||||
|
if (gRecordingProfile == true) {
|
||||||
|
#endif
|
||||||
|
PerfManager::instance()->enterFunction(fileName,functionName,lineNumber);
|
||||||
|
#ifdef PERIODIC_PROFILING
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfMarker::~PerfMarker()
|
||||||
|
{
|
||||||
|
endMark();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfMarker::endMark()
|
||||||
|
{
|
||||||
|
if(_onStack)
|
||||||
|
{ PerfManager::instance()->exitFunction();
|
||||||
|
_onStack = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#include "profiler.h"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#ifdef PERIODIC_PROFILING
|
||||||
|
bool gRecordingProfile = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PerfManager* PerfManager::_instance(NULL);
|
||||||
|
|
||||||
|
PerfDatum::PerfDatum( const char* const fileName,
|
||||||
|
const char* const functionName,
|
||||||
|
const int lineNumber,
|
||||||
|
__int64 cycles,
|
||||||
|
__int64 calls)
|
||||||
|
:
|
||||||
|
_fileName(fileName),
|
||||||
|
_functionName(functionName),
|
||||||
|
_lineNumber(lineNumber),
|
||||||
|
_cycles(cycles),
|
||||||
|
_calls(calls)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfManager::PerfManager(void)
|
||||||
|
:
|
||||||
|
_callTime(0),
|
||||||
|
_totalTime(0)
|
||||||
|
{
|
||||||
|
//Set up a root function so that the stack is never empty:
|
||||||
|
//(this will be the only entry with _lineNumber equal to 0)
|
||||||
|
_perfLog.insert(PerfDatum(__FILE__, "Root",0));
|
||||||
|
_perfStack.push_back(&*(_perfLog.begin()));
|
||||||
|
_lastTime = getCPUCount();
|
||||||
|
calibrate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfManager::calibrate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfManager::~PerfManager(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfManager* PerfManager::instance()
|
||||||
|
{
|
||||||
|
if(!_instance) //Double-check locking (first check)
|
||||||
|
{ _instance = new PerfManager();
|
||||||
|
}
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfManager::enterFunction(const char* const fileName,
|
||||||
|
const char* const functionName,
|
||||||
|
const int lineNumber)
|
||||||
|
{
|
||||||
|
//Mark the time when we end processing the previous function
|
||||||
|
__int64 endTime = getCPUCount();
|
||||||
|
//assuming we haven't corrupted the stack and reduced it to zero elements
|
||||||
|
if(_perfStack.size())
|
||||||
|
{ PERF_STACK_T::reverse_iterator lastMarker = _perfStack.rbegin();
|
||||||
|
//Update the total time spent processing the previous function
|
||||||
|
__int64 runTime = (endTime - _lastTime) - _callTime;
|
||||||
|
(**lastMarker)._cycles += runTime;
|
||||||
|
_totalTime += runTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we fail to insert the performance marker into our log, it means it is
|
||||||
|
//a duplicate. We can ignore this "error" as using the existing copy is
|
||||||
|
//the correct behavior.
|
||||||
|
std::pair<PERF_LOG_T::iterator, bool> result =
|
||||||
|
_perfLog.insert(PerfDatum(fileName, functionName, lineNumber));
|
||||||
|
//Update the call count on the new function:
|
||||||
|
PerfDatum* marker = &*(result.first);
|
||||||
|
marker->_calls++;
|
||||||
|
//Add performance marker to the stack as well as the log:
|
||||||
|
_perfStack.push_back(marker);
|
||||||
|
//Now that we are clear of any complex operations, we can set the call
|
||||||
|
//time of the new marker more accurately:
|
||||||
|
_lastTime = getCPUCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfManager::exitFunction()
|
||||||
|
{
|
||||||
|
//Mark the time when we exited the function
|
||||||
|
__int64 endTime = getCPUCount();
|
||||||
|
//Update the function in our log with its new runtime
|
||||||
|
PERF_STACK_T::reverse_iterator lastMarker = _perfStack.rbegin();
|
||||||
|
__int64 runTime = endTime - _lastTime;
|
||||||
|
(**lastMarker)._cycles += runTime;
|
||||||
|
_totalTime += runTime;
|
||||||
|
_perfStack.pop_back();
|
||||||
|
//Lastly, update the function we've fallen back to with the new current
|
||||||
|
//run time.
|
||||||
|
_lastTime = getCPUCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
__int64 PerfManager::getCPUCount () const
|
||||||
|
{
|
||||||
|
_asm rdtsc
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PerfSort::operator ()(const PerfDatum& lhs, const PerfDatum&rhs) const
|
||||||
|
{
|
||||||
|
if(rhs._lineNumber == lhs._lineNumber)
|
||||||
|
{ return 0 < strcmp(rhs._fileName, lhs._fileName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ return rhs._lineNumber > lhs._lineNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PerfSort2::operator ()(const PerfDatum& lhs, const PerfDatum&rhs) const
|
||||||
|
{
|
||||||
|
return lhs._cycles > rhs._cycles;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PerfManager::getPercision(const double value) const
|
||||||
|
{
|
||||||
|
int retVal = 1;
|
||||||
|
if(10.0 <= value)
|
||||||
|
{ retVal = 3;
|
||||||
|
}
|
||||||
|
else if (1.0 <= value)
|
||||||
|
{ retVal = 2;
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfManager::log(std::ostream &os)
|
||||||
|
{
|
||||||
|
//Create a copy of the log that sorts by cycles used
|
||||||
|
std::set<PerfDatum, PerfSort2> logCopy;
|
||||||
|
for(PERF_LOG_T::iterator i = _perfLog.begin(); i != _perfLog.end(); i++)
|
||||||
|
{
|
||||||
|
logCopy.insert(*i);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cyclesWidth = 1;
|
||||||
|
//I want the formatting to be nice!
|
||||||
|
for(std::set<PerfDatum, PerfSort2>::iterator i = logCopy.begin();
|
||||||
|
i != logCopy.end();
|
||||||
|
i++)
|
||||||
|
{
|
||||||
|
if(i->_lineNumber) {
|
||||||
|
std::stringstream cyclesString;
|
||||||
|
cyclesString << i->_cycles;
|
||||||
|
std::string tempString;
|
||||||
|
cyclesString >> tempString;
|
||||||
|
cyclesWidth = tempString.size();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Stream the sorted log to the supplied ostream
|
||||||
|
for(std::set<PerfDatum, PerfSort2>::iterator i = logCopy.begin();
|
||||||
|
i != logCopy.end();
|
||||||
|
++i)
|
||||||
|
{
|
||||||
|
if(i->_lineNumber)
|
||||||
|
{ //Truncate path from file name if the compiler included it.
|
||||||
|
const char* j = i->_fileName + strlen(i->_fileName);
|
||||||
|
while('\\' != *j && '/' != *j && j >= i->_fileName){j--;}
|
||||||
|
if('\\' == *j || '/' == *j){j++;}
|
||||||
|
std::stringstream cyclesString;
|
||||||
|
std::string tempString;
|
||||||
|
cyclesString << i->_cycles;
|
||||||
|
cyclesString >> tempString;
|
||||||
|
os.width(cyclesWidth);
|
||||||
|
os << tempString.c_str() << ", ";
|
||||||
|
__int64 cyclesPerCall = i->_cycles / i->_calls;
|
||||||
|
cyclesString.clear();
|
||||||
|
tempString.clear();
|
||||||
|
cyclesString << cyclesPerCall;
|
||||||
|
cyclesString >> tempString;
|
||||||
|
os.width(cyclesWidth);
|
||||||
|
os << tempString.c_str() << "] cycles (";
|
||||||
|
double percent = double(i->_cycles) / double(_totalTime) * 100;
|
||||||
|
os.precision(getPercision(percent));
|
||||||
|
os.width(4);
|
||||||
|
os << percent << "%) used in [";
|
||||||
|
os.width(8);
|
||||||
|
os << i->_calls << "] calls by " << j << "::" << i->_functionName\
|
||||||
|
<< " at line " << i->_lineNumber << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfMarker::PerfMarker( const char* const fileName,
|
||||||
|
const char* const functionName,
|
||||||
|
const int lineNumber)
|
||||||
|
:
|
||||||
|
#ifdef PERIODIC_PROFILING
|
||||||
|
_onStack(gRecordingProfile)
|
||||||
|
#else
|
||||||
|
_onStack(true)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#ifdef PERIODIC_PROFILING
|
||||||
|
if (gRecordingProfile == true) {
|
||||||
|
#endif
|
||||||
|
PerfManager::instance()->enterFunction(fileName,functionName,lineNumber);
|
||||||
|
#ifdef PERIODIC_PROFILING
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfMarker::~PerfMarker()
|
||||||
|
{
|
||||||
|
endMark();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfMarker::endMark()
|
||||||
|
{
|
||||||
|
if(_onStack)
|
||||||
|
{ PerfManager::instance()->exitFunction();
|
||||||
|
_onStack = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
//#ifndef PROFILER_ENABLED
|
||||||
|
//#define PROFILER_ENABLED
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#ifdef PROFILER_ENABLED
|
||||||
|
#define STRINGIZE(x) #x
|
||||||
|
#define NAMED_ PERFORMANCE_MARKER(x) PerfMarker x(__FILE__, STRINGIZE(x), __LINE__);
|
||||||
|
#define END_NAMED_ PERFORMANCE_MARKER(x) x.endMark();
|
||||||
|
#define PERFORMANCE_MARKER PerfMarker MARK(__FILE__, __FUNCTION__, __LINE__);
|
||||||
|
#define PERIODIC_PROFILING
|
||||||
|
#else
|
||||||
|
#define PERFORMANCE_MARKER
|
||||||
|
#endif
|
||||||
|
#pragma warning (disable : 4512)//disables assignment operator could not be generated
|
||||||
|
|
||||||
|
struct PerfDatum
|
||||||
|
{
|
||||||
|
PerfDatum( const char* const fileName,
|
||||||
|
const char* const functionName,
|
||||||
|
const int lineNumber,
|
||||||
|
__int64 cycles = 0,
|
||||||
|
__int64 calls = 0);
|
||||||
|
const char* const _fileName;
|
||||||
|
const char* const _functionName;
|
||||||
|
const int _lineNumber;
|
||||||
|
__int64 _cycles; //Number of CPU cycles used
|
||||||
|
__int64 _calls; //Number of calls to this function
|
||||||
|
};
|
||||||
|
#pragma warning (default : 4512)//disables assignment operator could not be generated
|
||||||
|
|
||||||
|
class PerfSort
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool operator()(const PerfDatum& lhs, const PerfDatum& rhs) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PerfSort2
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool operator()(const PerfDatum& lhs, const PerfDatum& rhs) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::set<PerfDatum, PerfSort> PERF_LOG_T;
|
||||||
|
typedef std::vector<PerfDatum* > PERF_STACK_T;
|
||||||
|
|
||||||
|
class PerfManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~PerfManager(void);
|
||||||
|
static PerfManager* instance();
|
||||||
|
void enterFunction( const char* const fileName,
|
||||||
|
const char* const functionName,
|
||||||
|
const int lineNumber);
|
||||||
|
void exitFunction();
|
||||||
|
void log(std::ostream &os);
|
||||||
|
private:
|
||||||
|
PerfManager(void);
|
||||||
|
__int64 getCPUCount(void) const;
|
||||||
|
void calibrate(void);
|
||||||
|
int getPercision(const double value) const; //used for formatting percentages
|
||||||
|
static PerfManager* _instance;
|
||||||
|
PERF_LOG_T _perfLog;
|
||||||
|
PERF_STACK_T _perfStack;
|
||||||
|
__int64 _callTime; //Time it takes to call the getCPUCount function
|
||||||
|
__int64 _totalTime;
|
||||||
|
__int64 _lastTime; //The last time we got hte CPU count.
|
||||||
|
};
|
||||||
|
|
||||||
|
class PerfMarker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PerfMarker( const char* const fileName,
|
||||||
|
const char* const functionName,
|
||||||
|
const int lineNumber);
|
||||||
|
~PerfMarker();
|
||||||
|
void endMark();
|
||||||
|
private:
|
||||||
|
bool _onStack;
|
||||||
|
};
|
||||||
|
|
||||||
Reference in New Issue
Block a user