/* ** Command & Conquer Generals(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : WWDebug * * * * $Archive:: /Commando/Code/wwdebug/wwprofile.cpp $* * * * $Author:: Tom_s $* * * * $Modtime:: 6/29/01 3:10p $* * * * $Revision:: 14 $* * * *---------------------------------------------------------------------------------------------* * WWProfile_Get_Ticks -- Retrieves the cpu performance counter * * WWProfile_Get_Tick_Rate -- returns the clock frequency of the cpu * * WWProfileHierachyNodeClass::WWProfileHierachyNodeClass -- Constructor * * WWProfileHierachyNodeClass::~WWProfileHierachyNodeClass -- Destructor * * WWProfileHierachyNodeClass::Get_Sub_Node -- Searches for a child node by name (pointer) * * WWProfileHierachyNodeClass::Reset -- Reset all profiling data in the tree * * WWProfileHierachyNodeClass::Call -- Start timing * * WWProfileHierachyNodeClass::Return -- Stop timing, record results * * WWProfileManager::Start_Profile -- Begin a named profile * * WWProfileManager::Stop_Profile -- Stop timing and record the results. * * WWProfileManager::Reset -- Reset the contents of the profiling system * * WWProfileManager::Increment_Frame_Counter -- Increment the frame counter * * WWProfileManager::Get_Time_Since_Reset -- returns the elapsed time since last reset * * WWProfileManager::Get_Iterator -- Creates an iterator for the profile tree * * WWProfileManager::Release_Iterator -- Return an iterator for the profile tree * * WWProfileManager::Get_In_Order_Iterator -- Creates an "in-order" iterator for the profile * * WWProfileManager::Release_In_Order_Iterator -- Return an "in-order" iterator * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "always.h" #include "wwprofile.h" #include "wwdebug.h" #include /*********************************************************************************************** * WWProfile_Get_Ticks -- Retrieves the cpu performance counter * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ inline void WWProfile_Get_Ticks(_int64 * ticks) { #ifdef _UNIX *ticks = 0; #else __asm { push edx; push ecx; mov ecx,ticks; _emit 0Fh _emit 31h mov [ecx],eax; mov [ecx+4],edx; pop ecx; pop edx; } #endif } /*********************************************************************************************** * WWProfile_Get_Tick_Rate -- returns the clock frequency of the cpu * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ inline float WWProfile_Get_Tick_Rate(void) { #ifdef _UNIX return(0); #else static float _CPUFrequency = -1.0f; if (_CPUFrequency == -1.0f) { __int64 curr_rate = 0; ::QueryPerformanceFrequency ((LARGE_INTEGER *)&curr_rate); _CPUFrequency = (float)curr_rate; } return _CPUFrequency; #endif } /*********************************************************************************************** * WWProfileHierachyNodeClass::WWProfileHierachyNodeClass -- Constructor * * * * * * INPUT: * * name - pointer to a static string which is the name of this profile node * * parent - parent pointer * * * * OUTPUT: * * * * WARNINGS: * * The name is assumed to be a static pointer, only the pointer is stored and compared for * * efficiency reasons. * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ WWProfileHierachyNodeClass::WWProfileHierachyNodeClass( const char * name, WWProfileHierachyNodeClass * parent ) : Name( name ), TotalCalls( 0 ), TotalTime( 0 ), StartTime( 0 ), RecursionCounter( 0 ), Parent( parent ), Child( NULL ), Sibling( NULL ) { Reset(); } /*********************************************************************************************** * WWProfileHierachyNodeClass::~WWProfileHierachyNodeClass -- Destructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ WWProfileHierachyNodeClass::~WWProfileHierachyNodeClass( void ) { delete Child; delete Sibling; } /*********************************************************************************************** * WWProfileHierachyNodeClass::Get_Sub_Node -- Searches for a child node by name (pointer) * * * * INPUT: * * name - static string pointer to the name of the node we are searching for * * * * OUTPUT: * * * * WARNINGS: * * All profile names are assumed to be static strings so this function uses pointer compares * * to find the named node. * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ WWProfileHierachyNodeClass * WWProfileHierachyNodeClass::Get_Sub_Node( const char * name ) { // Try to find this sub node WWProfileHierachyNodeClass * child = Child; while ( child ) { if ( child->Name == name ) { return child; } child = child->Sibling; } // We didn't find it, so add it WWProfileHierachyNodeClass * node = W3DNEW WWProfileHierachyNodeClass( name, this ); node->Sibling = Child; Child = node; return node; } /*********************************************************************************************** * WWProfileHierachyNodeClass::Reset -- Reset all profiling data in the tree * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ void WWProfileHierachyNodeClass::Reset( void ) { TotalCalls = 0; TotalTime = 0.0f; if ( Child ) { Child->Reset(); } if ( Sibling ) { Sibling->Reset(); } } /*********************************************************************************************** * WWProfileHierachyNodeClass::Call -- Start timing * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ void WWProfileHierachyNodeClass::Call( void ) { TotalCalls++; if (RecursionCounter++ == 0) { WWProfile_Get_Ticks(&StartTime); } } /*********************************************************************************************** * WWProfileHierachyNodeClass::Return -- Stop timing, record results * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ bool WWProfileHierachyNodeClass::Return( void ) { if (--RecursionCounter == 0) { if ( TotalCalls != 0 ) { __int64 time; WWProfile_Get_Ticks(&time); time-=StartTime; float sec = (float)time / WWProfile_Get_Tick_Rate(); // if ( sec > 1 ) { // WWDEBUG_SAY(( "WWProfile of %s took %f seconds\n", Name, sec )); // } TotalTime += sec; } } return RecursionCounter == 0; } /*************************************************************************************************** ** ** WWProfileManager Implementation ** ***************************************************************************************************/ WWProfileHierachyNodeClass WWProfileManager::Root( "Root", NULL ); WWProfileHierachyNodeClass * WWProfileManager::CurrentNode = &WWProfileManager::Root; int WWProfileManager::FrameCounter = 0; __int64 WWProfileManager::ResetTime = 0; static unsigned int ThreadID = static_cast(-1); /*********************************************************************************************** * WWProfileManager::Start_Profile -- Begin a named profile * * * * Steps one level deeper into the tree, if a child already exists with the specified name * * then it accumulates the profiling; otherwise a new child node is added to the profile tree. * * * * INPUT: * * name - name of this profiling record * * * * OUTPUT: * * * * WARNINGS: * * The string used is assumed to be a static string; pointer compares are used throughout * * the profiling code for efficiency. * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ void WWProfileManager::Start_Profile( const char * name ) { if (::GetCurrentThreadId() != ThreadID) { return; } // int current_thread = ::GetCurrentThreadId(); if (name != CurrentNode->Get_Name()) { CurrentNode = CurrentNode->Get_Sub_Node( name ); } CurrentNode->Call(); } /*********************************************************************************************** * WWProfileManager::Stop_Profile -- Stop timing and record the results. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ void WWProfileManager::Stop_Profile( void ) { if (::GetCurrentThreadId() != ThreadID) { return; } // Return will indicate whether we should back up to our parent (we may // be profiling a recursive function) if (CurrentNode->Return()) { CurrentNode = CurrentNode->Get_Parent(); } } /*********************************************************************************************** * WWProfileManager::Reset -- Reset the contents of the profiling system * * * * This resets everything except for the tree structure. All of the timing data is reset. * * * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ void WWProfileManager::Reset( void ) { ThreadID = ::GetCurrentThreadId(); Root.Reset(); FrameCounter = 0; WWProfile_Get_Ticks(&ResetTime); } /*********************************************************************************************** * WWProfileManager::Increment_Frame_Counter -- Increment the frame counter * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ void WWProfileManager::Increment_Frame_Counter( void ) { FrameCounter++; } /*********************************************************************************************** * WWProfileManager::Get_Time_Since_Reset -- returns the elapsed time since last reset * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ float WWProfileManager::Get_Time_Since_Reset( void ) { __int64 time; WWProfile_Get_Ticks(&time); time -= ResetTime; return (float)time / WWProfile_Get_Tick_Rate(); } /*********************************************************************************************** * WWProfileManager::Get_Iterator -- Creates an iterator for the profile tree * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ WWProfileIterator * WWProfileManager::Get_Iterator( void ) { return W3DNEW WWProfileIterator( &Root ); } /*********************************************************************************************** * WWProfileManager::Release_Iterator -- Return an iterator for the profile tree * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ void WWProfileManager::Release_Iterator( WWProfileIterator * iterator ) { delete iterator; } /*********************************************************************************************** * WWProfileManager::Get_In_Order_Iterator -- Creates an "in-order" iterator for the profile t * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ WWProfileInOrderIterator * WWProfileManager::Get_In_Order_Iterator( void ) { return W3DNEW WWProfileInOrderIterator; } /*********************************************************************************************** * WWProfileManager::Release_In_Order_Iterator -- Return an "in-order" iterator * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/24/2000 gth : Created. * *=============================================================================================*/ void WWProfileManager::Release_In_Order_Iterator( WWProfileInOrderIterator * iterator ) { delete iterator; } /*************************************************************************************************** ** ** WWProfileIterator Implementation ** ***************************************************************************************************/ WWProfileIterator::WWProfileIterator( WWProfileHierachyNodeClass * start ) { CurrentParent = start; CurrentChild = CurrentParent->Get_Child(); } void WWProfileIterator::First(void) { CurrentChild = CurrentParent->Get_Child(); } void WWProfileIterator::Next(void) { CurrentChild = CurrentChild->Get_Sibling(); } bool WWProfileIterator::Is_Done(void) { return CurrentChild == NULL; } void WWProfileIterator::Enter_Child( void ) { CurrentParent = CurrentChild; CurrentChild = CurrentParent->Get_Child(); } void WWProfileIterator::Enter_Child( int index ) { CurrentChild = CurrentParent->Get_Child(); while ( (CurrentChild != NULL) && (index != 0) ) { index--; CurrentChild = CurrentChild->Get_Sibling(); } if ( CurrentChild != NULL ) { CurrentParent = CurrentChild; CurrentChild = CurrentParent->Get_Child(); } } void WWProfileIterator::Enter_Parent( void ) { if ( CurrentParent->Get_Parent() != NULL ) { CurrentParent = CurrentParent->Get_Parent(); } CurrentChild = CurrentParent->Get_Child(); } /*************************************************************************************************** ** ** WWProfileInOrderIterator Implementation ** ***************************************************************************************************/ WWProfileInOrderIterator::WWProfileInOrderIterator( void ) { CurrentNode = &WWProfileManager::Root; } void WWProfileInOrderIterator::First(void) { CurrentNode = &WWProfileManager::Root; } void WWProfileInOrderIterator::Next(void) { if ( CurrentNode->Get_Child() ) { // If I have a child, go to child CurrentNode = CurrentNode->Get_Child(); } else if ( CurrentNode->Get_Sibling() ) { // If I have a sibling, go to sibling CurrentNode = CurrentNode->Get_Sibling(); } else { // if not, go to my parent's sibling, or his....... // Find a parent with a sibling.... bool done = false; while ( CurrentNode != NULL && !done ) { // go to my parent CurrentNode = CurrentNode->Get_Parent(); // If I have a sibling, go there if ( CurrentNode != NULL && CurrentNode->Get_Sibling() != NULL ) { CurrentNode = CurrentNode->Get_Sibling(); done = true; } } } } bool WWProfileInOrderIterator::Is_Done(void) { return CurrentNode == NULL; } /* ** */ WWTimeItClass::WWTimeItClass( const char * name ) { Name = name; WWProfile_Get_Ticks( &Time ); } WWTimeItClass::~WWTimeItClass( void ) { __int64 End; WWProfile_Get_Ticks( &End ); End -= Time; #ifdef WWDEBUG float time = End / WWProfile_Get_Tick_Rate(); WWDEBUG_SAY(( "*** WWTIMEIT *** %s took %1.9f\n", Name, time )); #endif } /* ** */ WWMeasureItClass::WWMeasureItClass( float * p_result ) { WWASSERT(p_result != NULL); PResult = p_result; WWProfile_Get_Ticks( &Time ); } WWMeasureItClass::~WWMeasureItClass( void ) { __int64 End; WWProfile_Get_Ticks( &End ); End -= Time; WWASSERT(PResult != NULL); *PResult = End / WWProfile_Get_Tick_Rate(); }