CnC_Red_Alert/WIN32LIB/PROFILE/WPROFILE.CPP

273 lines
15 KiB
C++
Raw Permalink Normal View History

/*
** Command & Conquer Red Alert(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 <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** 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 : Library profiler *
* *
* File Name : WPROFILE.CPP *
* *
* Programmer : Steve Tall *
* *
* Start Date : 11/17/95 *
* *
* Last Update : November 20th 1995 [ST] *
* *
*---------------------------------------------------------------------------------------------*
* Overview: *
* *
* New System *
* ~~~~~~~~~~~ *
* *
* The new profiler system creates a seperate thread and then starts a timer off there. The *
* timer in the second thread uses GetThreadContext to sample the IP address of each user *
* thread. This system has the advantage of being able to sample what is happening in all the *
* threads we own not just the main thread. Another advantage is that it doesnt require a *
* major recompilation. *
* The disadvantage is that we dont really know what is going on when the IP is outside the *
* scope of our threads - We could be in direct draw, direct sound or even something like the *
* VMM and there is no way to tell. *
* *
* *
* *
* Old System *
* ~~~~~~~~~~~ *
* *
* The profiler works by using the function prologue and epilogue hooks available in Watcom *
* to register the current functions address in a global variable and then sampling the *
* contents of the variable using a windows timer which runs at up to 1000 samples per second.*
* *
* Compile the code to be sampled with the -ep and -ee flags to enable the prologue (__PRO) *
* and epilogue (__EPI) calls to be generated. *
* At the beginning of the section to be profiled (just before main loop normally) call the *
* Start_Profiler function to start sampling. At the end of the section, call Stop_Profiler *
* which will stop the timer and write the profile data to disk in the PROFILE.BIN file. *
* *
* Use PROFILE.EXE to view the results of the session. *
* *
* The addition of prologue and epilogue code will slow down the product and the profiler *
* allocates a huge buffer for data so it should not be linked in unless it is going to be *
* used. *
* *
* The advantage of the prologue/epilogue approach is that all samples represent valid *
* addresses within our code so we get valid results we can use even when the IP is in system *
* code. *
* *
*---------------------------------------------------------------------------------------------*
* *
* Functions: *
* Start_Profiler -- initialises the profiler data and starts gathering data *
* Stop_Profiler -- stops the timer and writes the profile data to disk *
* *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#define WIN32
#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check
#define _WIN32
#endif // _WIN32
#include <windows.h>
#include <windowsx.h>
#include <wwstd.h>
#include <rawfile.h>
#include <file.h>
#include "profile.h"
#include <vdmdbg.h>
#include <timer.h>
#define PROFILE
extern "C"{
#ifdef PROFILE
unsigned ProfileList [PROFILE_RATE*MAX_PROFILE_TIME];
#else
unsigned ProfileList [2];
#endif
unsigned ProfilePtr;
}
extern "C" void Old_Profiler_Callback ( UINT, UINT , DWORD, DWORD, DWORD );
extern "C" void New_Profiler_Callback (void);
extern "C" {
extern unsigned ProfileFunctionAddress;
}
unsigned long ProfilerEvent; //Handle for profiler callback
unsigned long ProfilerThread; //Handle for profiler thread
HANDLE CCThreadHandle;
CONTEXT ThreadContext;
#if (PROFILE_SYSTEM == NEW_PROFILE_SYSTEM)
/***********************************************************************************************
* Thread_Callback -- gets the IP address of our thread and registers it *
* *
* *
* *
* INPUT: Windows timer callback parms - not used *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 1/2/96 6:37AM ST : Created *
*=============================================================================================*/
void CALLBACK Thread_Callback (UINT,UINT,DWORD,DWORD,DWORD)
{
ThreadContext.ContextFlags = VDMCONTEXT_CONTROL;
if (!InTimerCallback){
GetThreadContext ( CCThreadHandle , &ThreadContext );
}else{
GetThreadContext (TimerThreadHandle , &ThreadContext);
}
ProfileFunctionAddress = ThreadContext.Eip;
New_Profiler_Callback();
}
/***********************************************************************************************
* Profile_Thread -- this is the thread our profiler runs in. It just starts off a timer and *
* then buggers off into an infinite message loop. We shouldnt get messages *
* here as this isnt our primary thread *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 1/2/96 6:39AM ST : Created *
*=============================================================================================*/
void Profile_Thread (void)
{
MSG msg;
ProfilerEvent = timeSetEvent (1000/PROFILE_RATE , 1 , Thread_Callback , 0 , TIME_PERIODIC);
//ProfilerEvent = timeSetEvent (100 , 1 , Thread_Callback , 0 , TIME_ONESHOT);
do {
GetMessage(&msg,NULL,0,0);
} while(1);
}
#endif //(PROFILE_SYSTEM == OLD_PROFILE_SYSTEM)
/***********************************************************************************************
* Start_Profiler -- initialises the profiler system and starts sampling *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: There may be a pause when sampling starts as Win95 does some VMM stuff *
* *
* HISTORY: *
* 11/20/95 5:12PM ST : Created *
*=============================================================================================*/
void __cdecl Start_Profiler (void)
{
#ifdef PROFILE
if (!ProfilerEvent){
memset (&ProfileList[0],-1,PROFILE_RATE*MAX_PROFILE_TIME*4);
}
Profile_Init();
if (!ProfilerEvent){
#if (PROFILE_SYSTEM == OLD_PROFILE_SYSTEM)
/*
** Old profile system - just set up a timer to monitor the global variable based on
** the last place __PRO was called from
*/
ProfilerEvent = timeSetEvent (1000/PROFILE_RATE , 1 , (void CALLBACK (UINT,UINT,DWORD,DWORD,DWORD))Old_Profiler_Callback , 0 , TIME_PERIODIC);
#else
/*
** New profile system - create a second thread that will do all the profiling
** using GetThreadContext
*/
if ( DuplicateHandle( GetCurrentProcess(), GetCurrentThread() , GetCurrentProcess() ,&CCThreadHandle , 0 , TRUE , DUPLICATE_SAME_ACCESS) ){
ProfilerEvent= (unsigned)CreateThread(NULL,2048,(LPTHREAD_START_ROUTINE)&Profile_Thread,NULL,0,&ProfilerThread);
}
#endif
}
#else
ProfilerEvent = 0;
#endif
}
/***********************************************************************************************
* Stop_Profiler -- stops the sampling timer and writes the colledted data to disk *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: Writes to file PROFILE.BIN *
* *
* HISTORY: *
* 11/20/95 5:13PM ST : Created *
*=============================================================================================*/
void __cdecl Stop_Profiler (void)
{
if (ProfilerEvent){
#if (PROFILE_SYSTEM == OLD_PROFILE_SYSTEM)
//
// Old system - just remove the timer event
//
timeKillEvent(ProfilerEvent);
#else
//
// New system - kill the profiling thread
//
TerminateThread((HANDLE)ProfilerThread,0);
#endif
ProfilerEvent=NULL;
Profile_End();
int handle = Open_File ( "profile.bin" , WRITE );
if (handle != WW_ERROR){
Write_File (handle , &ProfileList[0] , ProfilePtr*4);
Close_File (handle);
}
}
}