Initial commit of Command & Conquer Renegade source code.

This commit is contained in:
LFeenanEA
2025-02-27 16:39:46 +00:00
parent 74ab8fa5e0
commit 58ed459113
4918 changed files with 1366710 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
Code/wwdebug/dspmake.cfg Normal file
View File

@@ -0,0 +1 @@
IGNORE = wwmemlog.cpp

517
Code/wwdebug/wwdebug.cpp Normal file
View File

@@ -0,0 +1,517 @@
/*
** Command & Conquer Renegade(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 : WWDebug *
* *
* $Archive:: /Commando/Code/wwdebug/wwdebug.cpp $*
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 1/13/02 1:46p $*
* *
* $Revision:: 16 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* WWDebug_Install_Message_Handler -- install function for handling the debug messages *
* WWDebug_Install_Assert_Handler -- Install a function for handling the assert messages *
* WWDebug_Install_Trigger_Handler -- install a trigger handler function *
* WWDebug_Printf -- Internal function for passing messages to installed handler *
* WWDebug_Assert_Fail -- Internal function for passing assert messages to installed handler *
* WWDebug_Assert_Fail_Print -- Internal function, passes assert message to handler *
* WWDebug_Check_Trigger -- calls the user-installed debug trigger handler *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "wwdebug.h"
#include <windows.h>
//#include "win.h" can use this if allowed to see wwlib
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <signal.h>
#include "except.h"
static PrintFunc _CurMessageHandler = NULL;
static AssertPrintFunc _CurAssertHandler = NULL;
static TriggerFunc _CurTriggerHandler = NULL;
static ProfileFunc _CurProfileStartHandler = NULL;
static ProfileFunc _CurProfileStopHandler = NULL;
// Convert the latest system error into a string and return a pointer to
// a static buffer containing the error string.
void Convert_System_Error_To_String(int id, char* buffer, int buf_len)
{
#ifndef _UNIX
FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
id,
0,
buffer,
buf_len,
NULL);
#endif
}
int Get_Last_System_Error()
{
return GetLastError();
}
/***********************************************************************************************
* WWDebug_Install_Message_Handler -- install function for handling the debug messages *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/19/98 GTH : Created. *
*=============================================================================================*/
PrintFunc WWDebug_Install_Message_Handler(PrintFunc func)
{
PrintFunc tmp = _CurMessageHandler;
_CurMessageHandler = func;
return tmp;
}
/***********************************************************************************************
* WWDebug_Install_Assert_Handler -- Install a function for handling the assert messages *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/19/98 GTH : Created. *
*=============================================================================================*/
AssertPrintFunc WWDebug_Install_Assert_Handler(AssertPrintFunc func)
{
AssertPrintFunc tmp = _CurAssertHandler;
_CurAssertHandler = func;
return tmp;
}
/***********************************************************************************************
* WWDebug_Install_Trigger_Handler -- install a trigger handler function *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
TriggerFunc WWDebug_Install_Trigger_Handler(TriggerFunc func)
{
TriggerFunc tmp = _CurTriggerHandler;
_CurTriggerHandler = func;
return tmp;
}
/***********************************************************************************************
* WWDebug_Install_Profile_Start_Handler -- install a profile handler function *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
ProfileFunc WWDebug_Install_Profile_Start_Handler(ProfileFunc func)
{
ProfileFunc tmp = _CurProfileStartHandler;
_CurProfileStartHandler = func;
return tmp;
}
/***********************************************************************************************
* WWDebug_Install_Profile_Stop_Handler -- install a profile handler function *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
ProfileFunc WWDebug_Install_Profile_Stop_Handler(ProfileFunc func)
{
ProfileFunc tmp = _CurProfileStopHandler;
_CurProfileStopHandler = func;
return tmp;
}
/***********************************************************************************************
* WWDebug_Printf -- Internal function for passing messages to installed handler *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/19/98 GTH : Created. *
*=============================================================================================*/
void WWDebug_Printf(const char * format,...)
{
if (_CurMessageHandler != NULL) {
va_list va;
char buffer[4096];
va_start(va, format);
vsprintf(buffer, format, va);
WWASSERT((strlen(buffer) < sizeof(buffer)));
_CurMessageHandler(WWDEBUG_TYPE_INFORMATION, buffer);
va_end(va);
}
}
/***********************************************************************************************
* WWDebug_Printf_Warning -- Internal function for passing messages to installed handler *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/19/98 GTH : Created. *
*=============================================================================================*/
void WWDebug_Printf_Warning(const char * format,...)
{
if (_CurMessageHandler != NULL) {
va_list va;
char buffer[4096];
va_start(va, format);
vsprintf(buffer, format, va);
WWASSERT((strlen(buffer) < sizeof(buffer)));
_CurMessageHandler(WWDEBUG_TYPE_WARNING, buffer);
va_end(va);
}
}
/***********************************************************************************************
* WWDebug_Printf_Error -- Internal function for passing messages to installed handler *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/19/98 GTH : Created. *
*=============================================================================================*/
void WWDebug_Printf_Error(const char * format,...)
{
if (_CurMessageHandler != NULL) {
va_list va;
char buffer[4096];
va_start(va, format);
vsprintf(buffer, format, va);
WWASSERT((strlen(buffer) < sizeof(buffer)));
_CurMessageHandler(WWDEBUG_TYPE_ERROR, buffer);
va_end(va);
}
}
/***********************************************************************************************
* WWDebug_Assert_Fail -- Internal function for passing assert messages to installed handler *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/19/98 GTH : Created. *
*=============================================================================================*/
#ifdef WWDEBUG
void WWDebug_Assert_Fail(const char * expr,const char * file, int line)
{
if (_CurAssertHandler != NULL) {
char buffer[4096];
sprintf(buffer,"%s (%d) Assert: %s\n",file,line,expr);
_CurAssertHandler(buffer);
} else {
/*
// If the exception handler is try to quit the game then don't show an assert.
*/
if (Is_Trying_To_Exit()) {
ExitProcess(0);
}
char assertbuf[4096];
sprintf(assertbuf, "Assert failed\n\n. File %s Line %d", file, line);
int code = MessageBoxA(NULL, assertbuf, "WWDebug_Assert_Fail", MB_ABORTRETRYIGNORE|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL);
if (code == IDABORT) {
raise(SIGABRT);
_exit(3);
}
if (code == IDRETRY) {
_asm int 3;
return;
}
}
}
#endif
/***********************************************************************************************
* _assert -- Catch all asserts by overriding lib function *
* *
* *
* *
* INPUT: Assert stuff *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 12/11/2001 3:56PM ST : Created *
*=============================================================================================*/
#ifdef WWDEBUG
void __cdecl _assert(void *expr, void *filename, unsigned lineno)
{
WWDebug_Assert_Fail((const char*)expr, (const char*)filename, lineno);
}
#endif //WWDEBUG
/***********************************************************************************************
* WWDebug_Assert_Fail_Print -- Internal function, passes assert message to handler *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/19/98 GTH : Created. *
*=============================================================================================*/
#ifdef WWDEBUG
void WWDebug_Assert_Fail_Print(const char * expr,const char * file, int line,const char * string)
{
if (_CurAssertHandler != NULL) {
char buffer[4096];
sprintf(buffer,"%s (%d) Assert: %s %s\n",file,line,expr, string);
_CurAssertHandler(buffer);
} else {
assert(0);
}
}
#endif
/***********************************************************************************************
* WWDebug_Check_Trigger -- calls the user-installed debug trigger handler *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
bool WWDebug_Check_Trigger(int trigger_num)
{
if (_CurTriggerHandler != NULL) {
return _CurTriggerHandler(trigger_num);
} else {
return false;
}
}
/***********************************************************************************************
* WWDebug_Profile_Start -- calls the user-installed profile start handler *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
void WWDebug_Profile_Start( const char * title)
{
if (_CurProfileStartHandler != NULL) {
_CurProfileStartHandler( title );
}
}
/***********************************************************************************************
* WWDebug_Profile_Stop -- calls the user-installed profile start handler *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
void WWDebug_Profile_Stop( const char * title)
{
if (_CurProfileStopHandler != NULL) {
_CurProfileStopHandler( title );
}
}
#ifdef WWDEBUG
/***********************************************************************************************
* WWDebug_DBWin32_Message_Handler -- *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/30/98 BMG : Created. *
*=============================================================================================*/
void WWDebug_DBWin32_Message_Handler( const char * str )
{
HANDLE heventDBWIN; /* DBWIN32 synchronization object */
HANDLE heventData; /* data passing synch object */
HANDLE hSharedFile; /* memory mapped file shared data */
LPSTR lpszSharedMem;
/* make sure DBWIN is open and waiting */
heventDBWIN = OpenEvent(EVENT_MODIFY_STATE, FALSE, "DBWIN_BUFFER_READY");
if ( !heventDBWIN )
{
//MessageBox(NULL, "DBWIN_BUFFER_READY nonexistent", NULL, MB_OK);
return;
}
/* get a handle to the data synch object */
heventData = OpenEvent(EVENT_MODIFY_STATE, FALSE, "DBWIN_DATA_READY");
if ( !heventData )
{
// MessageBox(NULL, "DBWIN_DATA_READY nonexistent", NULL, MB_OK);
CloseHandle(heventDBWIN);
return;
}
hSharedFile = CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, 0, 4096, "DBWIN_BUFFER");
if (!hSharedFile)
{
//MessageBox(NULL, "DebugTrace: Unable to create file mapping object DBWIN_BUFFER", "Error", MB_OK);
CloseHandle(heventDBWIN);
CloseHandle(heventData);
return;
}
lpszSharedMem = (LPSTR)MapViewOfFile(hSharedFile, FILE_MAP_WRITE, 0, 0, 512);
if (!lpszSharedMem)
{
//MessageBox(NULL, "DebugTrace: Unable to map shared memory", "Error", MB_OK);
CloseHandle(heventDBWIN);
CloseHandle(heventData);
return;
}
/* wait for buffer event */
WaitForSingleObject(heventDBWIN, INFINITE);
/* write it to the shared memory */
*((LPDWORD)lpszSharedMem) = 0;
wsprintf(lpszSharedMem + sizeof(DWORD), "%s", str);
/* signal data ready event */
SetEvent(heventData);
/* clean up handles */
CloseHandle(hSharedFile);
CloseHandle(heventData);
CloseHandle(heventDBWIN);
return;
}
#endif // WWDEBUG

197
Code/wwdebug/wwdebug.dsp Normal file
View File

@@ -0,0 +1,197 @@
# Microsoft Developer Studio Project File - Name="wwdebug" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Static Library" 0x0104
CFG=wwdebug - Win32 DebugE
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "wwdebug.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "wwdebug.mak" CFG="wwdebug - Win32 DebugE"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "wwdebug - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "wwdebug - Win32 Debug" (based on "Win32 (x86) Static Library")
!MESSAGE "wwdebug - Win32 Profile" (based on "Win32 (x86) Static Library")
!MESSAGE "wwdebug - Win32 DebugE" (based on "Win32 (x86) Static Library")
!MESSAGE "wwdebug - Win32 ProfileE" (based on "Win32 (x86) Static Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""$/Commando/Code/wwdebug", OPNAAAAA"
# PROP Scc_LocalPath "."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "wwdebug - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /Zi /O2 /Ob2 /I "..\wwlib" /D "NDEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /FD /c
# SUBTRACT CPP /Fr /YX
# ADD BASE RSC /l 0x409
# ADD RSC /l 0x409
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo /out:"..\Libs\release\wwdebug.lib"
!ELSEIF "$(CFG)" == "wwdebug - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MTd /W3 /Gm /Gi /ZI /Od /I "..\wwlib" /D "_DEBUG" /D "WWDEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /Fr /YX /FD /c
# ADD BASE RSC /l 0x409
# ADD RSC /l 0x409
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo /out:"..\Libs\debug\wwdebug.lib"
!ELSEIF "$(CFG)" == "wwdebug - Win32 Profile"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "wwdebug_"
# PROP BASE Intermediate_Dir "wwdebug_"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Profile"
# PROP Intermediate_Dir "Profile"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /Zi /O2 /Op /Ob2 /I "..\wwlib" /D "NDEBUG" /D "WWDEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409
# ADD RSC /l 0x409
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo /out:"..\Libs\release\wwdebug.lib"
# ADD LIB32 /nologo /out:"..\Libs\profile\wwdebug.lib"
!ELSEIF "$(CFG)" == "wwdebug - Win32 DebugE"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "DebugE"
# PROP BASE Intermediate_Dir "DebugE"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "DebugE"
# PROP Intermediate_Dir "DebugE"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "..\wwlib" /D "_DEBUG" /D "WWDEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /Fr /YX /FD /c
# ADD CPP /nologo /MTd /W3 /Gm /Gi /ZI /Od /I "..\wwlib" /D "_DEBUG" /D "WWDEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /D "PARAM_EDITING_ON" /Fr /YX /FD /c
# ADD BASE RSC /l 0x409
# ADD RSC /l 0x409
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo /out:"..\Libs\debug\wwdebug.lib"
# ADD LIB32 /nologo /out:"..\Libs\debug\wwdebuge.lib"
!ELSEIF "$(CFG)" == "wwdebug - Win32 ProfileE"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "ProfileE"
# PROP BASE Intermediate_Dir "ProfileE"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "ProfileE"
# PROP Intermediate_Dir "ProfileE"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MD /W3 /GX /Zi /O2 /Op /Ob2 /I "..\wwlib" /D "NDEBUG" /D "WWDEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /YX /FD /c
# ADD CPP /nologo /MT /W3 /Zi /O2 /Op /Ob2 /I "..\wwlib" /D "NDEBUG" /D "WWDEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /D "PARAM_EDITING_ON" /YX /FD /c
# ADD BASE RSC /l 0x409
# ADD RSC /l 0x409
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo /out:"..\Libs\profile\wwdebug.lib"
# ADD LIB32 /nologo /out:"..\Libs\profile\wwdebuge.lib"
!ENDIF
# Begin Target
# Name "wwdebug - Win32 Release"
# Name "wwdebug - Win32 Debug"
# Name "wwdebug - Win32 Profile"
# Name "wwdebug - Win32 DebugE"
# Name "wwdebug - Win32 ProfileE"
# Begin Group "Source"
# PROP Default_Filter "cpp;c"
# Begin Source File
SOURCE=.\wwdebug.cpp
# End Source File
# Begin Source File
SOURCE=.\wwmemlog.cpp
# End Source File
# Begin Source File
SOURCE=.\wwprofile.cpp
# End Source File
# End Group
# Begin Group "Headers"
# PROP Default_Filter "h"
# Begin Source File
SOURCE=.\wwdebug.h
# End Source File
# Begin Source File
SOURCE=.\wwhack.h
# End Source File
# Begin Source File
SOURCE=.\wwmemlog.h
# End Source File
# Begin Source File
SOURCE=.\wwprofile.h
# End Source File
# End Group
# End Target
# End Project

173
Code/wwdebug/wwdebug.h Normal file
View File

@@ -0,0 +1,173 @@
/*
** Command & Conquer Renegade(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 : WWDebug *
* *
* $Archive:: /Commando/Code/wwdebug/wwdebug.h $*
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 9/03/01 1:59p $*
* *
* $Revision:: 19 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
#ifndef WWDEBUG_H
#define WWDEBUG_H
// The macro MESSAGE allows user to put:
// #pragma MESSAGE("Hello world")
// anywhere in a source file. The message:
// sourcefname.cpp (123) : Hello world
// would be printed if put in sourcefname.cpp on line 123 in compile window like an error.
// You can then use next/prev error hot keys to see where comment is. It is not an error and
// will be printed everytime it is compiled. Very useful to put comments in code that cannot
// be forgoten.
#define STRING_IT(a) #a
#define TOKEN_IT(a) STRING_IT(,##a)
#define MESSAGE(a) message (__FILE__ "(" TOKEN_IT(__LINE__) ") : " a)
void Convert_System_Error_To_String(int error_id, char* buffer, int buf_len);
int Get_Last_System_Error();
/*
** If 'WWDEBUG' is turned off, all WWDEBUG_xxx macros will
** be discarded.
*/
typedef enum {
WWDEBUG_TYPE_INFORMATION,
WWDEBUG_TYPE_WARNING,
WWDEBUG_TYPE_ERROR,
WWDEBUG_TYPE_USER
} DebugType;
typedef void (*PrintFunc)(DebugType type, const char * message);
typedef void (*AssertPrintFunc)(const char * message);
typedef bool (*TriggerFunc)(int trigger_num);
typedef void (*ProfileFunc)(const char * title);
PrintFunc WWDebug_Install_Message_Handler(PrintFunc func);
AssertPrintFunc WWDebug_Install_Assert_Handler(AssertPrintFunc func);
TriggerFunc WWDebug_Install_Trigger_Handler(TriggerFunc func);
ProfileFunc WWDebug_Install_Profile_Start_Handler(ProfileFunc func);
ProfileFunc WWDebug_Install_Profile_Stop_Handler(ProfileFunc func);
/*
** Users should not call the following three functions directly! Use the macros below instead...
*/
void WWDebug_Printf(const char * format,...);
void WWDebug_Printf_Warning(const char * format,...);
void WWDebug_Printf_Error(const char * format,...);
#ifdef WWDEBUG
void WWDebug_Assert_Fail(const char * expr,const char * file, int line);
void WWDebug_Assert_Fail_Print(const char * expr,const char * file, int line,const char * string);
bool WWDebug_Check_Trigger(int trigger_num);
void WWDebug_Profile_Start( const char * title);
void WWDebug_Profile_Stop( const char * title);
/*
** A message handler to display to DBWIN32
*/
void WWDebug_DBWin32_Message_Handler( const char * message);
#endif
/*
** Use the following #define so that all of the debugging messages
** and strings go away when the release version is built.
** WWDEBUG_SAY(("dir = %f\n",dir));
*/
#ifdef WWDEBUG
#define WWDEBUG_SAY(x) WWDebug_Printf x
#define WWDEBUG_WARNING(x) WWDebug_Printf_Warning x
#define WWDEBUG_ERROR(x) WWDebug_Printf_Error x
#else
#define WWDEBUG_SAY(x)
#define WWDEBUG_WARNING(x)
#define WWDEBUG_ERROR(x)
#endif
#define WWRELEASE_SAY(x) WWDebug_Printf x
#define WWRELEASE_WARNING(x) WWDebug_Printf_Warning x
#define WWRELEASE_ERROR(x) WWDebug_Printf_Error x
/*
** The WWASSERT and WWASSERT_PRINT macros will send messages to your
** assert handler.
*/
#ifdef WWDEBUG
#define WWASSERT(expr) ((expr)?(void)0:WWDebug_Assert_Fail(#expr,__FILE__,__LINE__))
#define WWASSERT_PRINT( expr, string ) ((expr)?(void)0:WWDebug_Assert_Fail_Print(#expr,__FILE__,__LINE__,string))
#define DIE (WWDebug_Assert_Fail("DIE",__FILE__,__LINE__))
#else
#define WWASSERT( expr )
#define WWASSERT_PRINT( expr, string )
#define DIE
#endif
/*
** The WWDEBUG_BREAK macro will cause the application to break into
** the debugger...
*/
#ifdef WWDEBUG
#define WWDEBUG_BREAK _asm int 0x03
#else
#define WWDEBUG_BREAK _asm int 0x03
#endif
/*
** The WWDEBUG_TRIGGER macro can be used to ask the application if
** a debug trigger is set. We define a couple of generic triggers
** for casual use.
*/
#define WWDEBUG_TRIGGER_GENERIC0 0
#define WWDEBUG_TRIGGER_GENERIC1 1
#ifdef WWDEBUG
#define WWDEBUG_TRIGGER(x) WWDebug_Check_Trigger(x)
#else
#define WWDEBUG_TRIGGER(x) (0)
#endif
/*
** The WWDEBUG_PROFILE macros can be used to time blocks of code
*/
#ifdef WWDEBUG
#define WWDEBUG_PROFILE_START(x) WWDebug_Profile_Start(x)
#define WWDEBUG_PROFILE_STOP(x) WWDebug_Profile_Stop(x)
#else
#define WWDEBUG_PROFILE_START(x)
#define WWDEBUG_PROFILE_STOP(x)
#endif
#endif

50
Code/wwdebug/wwhack.h Normal file
View File

@@ -0,0 +1,50 @@
/*
** Command & Conquer Renegade(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 : WWDebug *
* *
* $Archive:: /Commando/Code/wwdebug/wwhack.h $*
* *
* $Author:: Byon_g $*
* *
* $Modtime:: 10/06/99 1:09p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
#ifndef WWHACK_H
#define WWHACK_H
/*
** FORCE_LINK is a hack to force a module in a lib to be linked into the EXE.
*/
#define FORCE_LINK( module ) void _Force_Link_ ## module( void ); _Force_Link_ ## module()
#define DECLARE_FORCE_LINK( module ) void _Force_Link_ ## module( void ) {}
#endif

748
Code/wwdebug/wwmemlog.cpp Normal file
View File

@@ -0,0 +1,748 @@
/*
** Command & Conquer Renegade(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 : WWDebug *
* *
* $Archive:: /Commando/Code/wwdebug/wwmemlog.cpp $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 11/21/01 2:03p $*
* *
* $Revision:: 27 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* WWMemoryLogClass::Allocate_Memory -- allocates memory *
* WWMemoryLogClass::Release_Memory -- frees memory *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "always.h"
#include "wwmemlog.h"
#include "wwdebug.h"
#include "vector.h"
#include "fastallocator.h"
#include <windows.h>
#define USE_FAST_ALLOCATOR
#ifdef STEVES_NEW_CATCHER
#define DISABLE_MEMLOG 1
#else //STEVES_NEW_CATCHER
#ifdef PARAM_EDITING_ON
#define DISABLE_MEMLOG 1
#else //PARAM_EDITING_ON
#define DISABLE_MEMLOG 0
#endif //PARAM_EDITING_ON
#endif //STEVES_NEW_CATCHER*/
#ifdef USE_FAST_ALLOCATOR
#define ALLOC_MEMORY(n) FastAllocatorGeneral::Get_Allocator()->Alloc(n)
#define FREE_MEMORY(p) FastAllocatorGeneral::Get_Allocator()->Free(p)
#else
#define ALLOC_MEMORY(n) ::malloc(n)
#define FREE_MEMORY(p) ::free(p)
#endif
/*
** Enable one of the following #defines to specify which thread-sychronization
** method to use.
*/
#define MEMLOG_USE_MUTEX 0
#define MEMLOG_USE_CRITICALSECTION 1
#define MEMLOG_USE_FASTCRITICALSECTION 0
static unsigned AllocateCount;
static unsigned FreeCount;
/*
** Name for each memory category. I'm padding the array with some "undefined" strings in case
** someone forgets to set the name when adding a new category.
*/
static char * _MemoryCategoryNames[] =
{
"UNKNOWN",
"Geometry",
"Animation",
"Texture",
"Pathfind",
"Vis",
"Sound",
"CullingData",
"Strings",
"GameData",
"PhysicsData",
"W3dData",
"StaticAllocations",
"GameInit",
"Renderer",
"Network",
"BINK",
"<undefined>",
"<undefined>",
"<undefined>",
"<undefined>",
};
/**
** MemoryCounterClass
** This object will store statistics for each memory category. It can provide things like
** the current amount of allocated memory and the peak amount of allocated memory.
*/
class MemoryCounterClass
{
public:
MemoryCounterClass(void) : CurrentAllocation(0), PeakAllocation(0) { }
void Memory_Allocated(int size) { CurrentAllocation+=size; PeakAllocation = max(PeakAllocation,CurrentAllocation); }
void Memory_Released(int size) { CurrentAllocation-=size; }
int Get_Current_Allocated_Memory(void) { return CurrentAllocation; }
int Get_Peak_Allocated_Memory(void) { return PeakAllocation; }
protected:
int CurrentAllocation;
int PeakAllocation;
};
/**
** ActiveCategoryStackClass
** This object is used to keep track of the "active memory category". Whenever memory is allocated
** it will be charged to the current active memory category. To be thread-safe, there will be
** one ActiveCategoryStack per thread that is encountered in the program.
*/
const int MAX_CATEGORY_STACK_DEPTH = 1024;
class ActiveCategoryStackClass : public VectorClass<int>
{
public:
ActiveCategoryStackClass(void) :
VectorClass<int>(MAX_CATEGORY_STACK_DEPTH),
ThreadID(-1),
Count(0)
{ }
~ActiveCategoryStackClass(void) { WWASSERT(Count == 1); }
ActiveCategoryStackClass & operator = (const ActiveCategoryStackClass & that);
bool operator == (const ActiveCategoryStackClass &) { return false; }
bool operator != (const ActiveCategoryStackClass &) { return true; }
void Init(int thread_id) { ThreadID = thread_id; Count = 0; Push(MEM_UNKNOWN); }
void Set_Thread_ID(int id) { ThreadID = id; }
int Get_Thread_ID(void) { return ThreadID; }
void Push(int active_category) { (*this)[Count] = active_category; Count++; }
void Pop(void) { Count--; }
int Current(void) { return (*this)[Count-1]; }
protected:
int ThreadID;
int Count;
};
/**
** ActiveCategoryClass
** This is a dynamic vector of ActiveCategoryStackClasses which adds a new stack each time
** a new thread is encountered. It also is able to return to you the active category for
** the currently active thread automatically.
*/
const int MAX_CATEGORY_STACKS = 256; // maximum number of threads we expect to encounter...
class ActiveCategoryClass : public VectorClass<ActiveCategoryStackClass>
{
public:
ActiveCategoryClass(void) : VectorClass<ActiveCategoryStackClass>(MAX_CATEGORY_STACKS), Count(0) { Get_Active_Stack().Push(MEM_STATICALLOCATION); }
void Push(int active_category) { Get_Active_Stack().Push(active_category); }
void Pop(void) { Get_Active_Stack().Pop(); }
int Current(void) { return Get_Active_Stack().Current(); }
protected:
ActiveCategoryStackClass & Get_Active_Stack(void);
int Count;
};
/**
** MemLogClass
** This class ties all of the logging datastructures together into a single object
** which can be created on demand when the first 'new' call is encountered.
*/
class MemLogClass
{
public:
int Get_Current_Allocated_Memory(int category);
int Get_Peak_Allocated_Memory(int category);
/*
** Interface for recording allocations and de-allocations
*/
int Register_Memory_Allocated(int size);
void Register_Memory_Released(int category,int size);
void Push_Active_Category(int category);
void Pop_Active_Category(void);
void Init();
private:
MemoryCounterClass _MemoryCounters[MEM_COUNT];
ActiveCategoryClass _ActiveCategoryTracker;
};
/**
** Static Variables
** _TheMemLog - object which encapsulates all logging. will be allocated on first use
** _MemLogMutex - handle to the mutex used to arbtirate access to the logging data structures
** _MemLogLockCounter - count of the active mutex locks.
*/
static MemLogClass * _TheMemLog = NULL;
static bool _MemLogAllocated = false;
#if MEMLOG_USE_MUTEX
static void * _MemLogMutex = NULL;
static int _MemLogLockCounter = 0;
#endif
#if MEMLOG_USE_CRITICALSECTION
static bool _MemLogCriticalSectionAllocated = false;
static char _MemLogCriticalSectionHandle[sizeof(CRITICAL_SECTION)];
#endif
#if MEMLOG_USE_FASTCRITICALSECTION
volatile unsigned _MemLogSemaphore = 0;
#endif
/*
** Use this code to get access to the mutex...
*/
WWINLINE void * Get_Mem_Log_Mutex(void)
{
#if MEMLOG_USE_MUTEX
if (_MemLogMutex == NULL) {
_MemLogMutex=CreateMutex(NULL,false,NULL);
WWASSERT(_MemLogMutex);
}
return _MemLogMutex;
#endif
#if MEMLOG_USE_CRITICALSECTION
if (_MemLogCriticalSectionAllocated == false) {
InitializeCriticalSection((CRITICAL_SECTION*)_MemLogCriticalSectionHandle);
_MemLogCriticalSectionAllocated = true;
}
return _MemLogCriticalSectionHandle;
#endif
}
WWINLINE void Lock_Mem_Log_Mutex(void)
{
#if MEMLOG_USE_MUTEX
void * mutex = Get_Mem_Log_Mutex();
#ifdef WWDEBUG
int res =
#endif
WaitForSingleObject(mutex,INFINITE);
WWASSERT(res==WAIT_OBJECT_0);
_MemLogLockCounter++;
#endif
#if MEMLOG_USE_CRITICALSECTION
Get_Mem_Log_Mutex();
EnterCriticalSection((CRITICAL_SECTION*)_MemLogCriticalSectionHandle);
#endif
#if MEMLOG_USE_FASTCRITICALSECTION
volatile unsigned& nFlag=_MemLogSemaphore;
#define ts_lock _emit 0xF0
assert(((unsigned)&nFlag % 4) == 0);
__asm mov ebx, [nFlag]
__asm ts_lock
__asm bts dword ptr [ebx], 0
__asm jc The_Bit_Was_Previously_Set_So_Try_Again
return;
The_Bit_Was_Previously_Set_So_Try_Again:
ThreadClass::Switch_Thread();
__asm mov ebx, [nFlag]
__asm ts_lock
__asm bts dword ptr [ebx], 0
__asm jc The_Bit_Was_Previously_Set_So_Try_Again
#endif
}
WWINLINE void Unlock_Mem_Log_Mutex(void)
{
#if MEMLOG_USE_MUTEX
void * mutex = Get_Mem_Log_Mutex();
_MemLogLockCounter--;
#ifdef WWDEBUG
int res=
#endif
ReleaseMutex(mutex);
WWASSERT(res);
#endif
#if MEMLOG_USE_CRITICALSECTION
Get_Mem_Log_Mutex();
LeaveCriticalSection((CRITICAL_SECTION*)_MemLogCriticalSectionHandle);
#endif
#if MEMLOG_USE_FASTCRITICALSECTION
_MemLogSemaphore = 0;
#endif
}
class MemLogMutexLockClass
{
public:
MemLogMutexLockClass(void) { Lock_Mem_Log_Mutex(); }
~MemLogMutexLockClass(void) { Unlock_Mem_Log_Mutex(); }
};
/***************************************************************************************************
**
** ActiveCategoryStackClass Implementation
**
***************************************************************************************************/
ActiveCategoryStackClass &
ActiveCategoryStackClass::operator = (const ActiveCategoryStackClass & that)
{
if (this != &that) {
VectorClass<int>::operator == (that);
ThreadID = that.ThreadID;
Count = that.Count;
}
return *this;
}
/***************************************************************************************************
**
** ActiveCategoryClass Implementation
**
***************************************************************************************************/
ActiveCategoryStackClass & ActiveCategoryClass::Get_Active_Stack(void)
{
int current_thread = ::GetCurrentThreadId();
/*
** If we already have an allocated category stack for the current thread,
** just return its active category.
*/
for (int i=0; i<Count; i++) {
ActiveCategoryStackClass & cat_stack = (*this)[i];
if (cat_stack.Get_Thread_ID() == current_thread) {
return cat_stack;
}
}
/*
** If we fall through to here, we need to allocate a new category stack
** for this thread.
*/
(*this)[Count].Init(current_thread);
Count++;
return (*this)[Count-1];
}
/***************************************************************************************************
**
** MemLogClass Implementation
**
***************************************************************************************************/
int MemLogClass::Get_Current_Allocated_Memory(int category)
{
MemLogMutexLockClass lock;
return _MemoryCounters[category].Get_Current_Allocated_Memory();
}
int MemLogClass::Get_Peak_Allocated_Memory(int category)
{
MemLogMutexLockClass lock;
return _MemoryCounters[category].Get_Peak_Allocated_Memory();
}
void MemLogClass::Init()
{
{
MemLogMutexLockClass lock;
WWASSERT(_ActiveCategoryTracker.Current()==MEM_STATICALLOCATION);
}
Pop_Active_Category(); // Remove staticallocation state forever
}
int MemLogClass::Register_Memory_Allocated(int size)
{
MemLogMutexLockClass lock;
int active_category = _ActiveCategoryTracker.Current();
WWASSERT((active_category >= 0) && (active_category < MEM_COUNT));
_MemoryCounters[active_category].Memory_Allocated(size);
return active_category;
}
void MemLogClass::Register_Memory_Released(int category,int size)
{
MemLogMutexLockClass lock;
_MemoryCounters[category].Memory_Released(size);
}
void MemLogClass::Push_Active_Category(int category)
{
MemLogMutexLockClass lock;
WWASSERT((category >= 0) && (category < MEM_COUNT));
_ActiveCategoryTracker.Push(category);
}
void MemLogClass::Pop_Active_Category(void)
{
MemLogMutexLockClass lock;
_ActiveCategoryTracker.Pop();
}
/***************************************************************************************************
**
** WWMemoryLogClass Implementation
**
***************************************************************************************************/
int WWMemoryLogClass::Get_Category_Count(void)
{
return MEM_COUNT;
}
const char * WWMemoryLogClass::Get_Category_Name(int category)
{
return _MemoryCategoryNames[category];
}
int WWMemoryLogClass::Get_Current_Allocated_Memory(int category)
{
return Get_Log()->Get_Current_Allocated_Memory(category);
}
int WWMemoryLogClass::Get_Peak_Allocated_Memory(int category)
{
return Get_Log()->Get_Peak_Allocated_Memory(category);
}
void WWMemoryLogClass::Push_Active_Category(int category)
{
#if (DISABLE_MEMLOG == 0)
Get_Log()->Push_Active_Category(category);
#endif //(DISABLE_MEMLOG == 0)
}
void WWMemoryLogClass::Pop_Active_Category(void)
{
#if (DISABLE_MEMLOG == 0)
Get_Log()->Pop_Active_Category();
#endif //(DISABLE_MEMLOG == 0)
}
int WWMemoryLogClass::Register_Memory_Allocated(int size)
{
return Get_Log()->Register_Memory_Allocated(size);
}
void WWMemoryLogClass::Register_Memory_Released(int category,int size)
{
Get_Log()->Register_Memory_Released(category,size);
}
static void __cdecl _MemLogCleanup(void)
{
delete _TheMemLog;
}
MemLogClass * WWMemoryLogClass::Get_Log(void)
{
MemLogMutexLockClass lock;
if (_TheMemLog == NULL) {
//assert(!_MemLogAllocated);
_TheMemLog = new MemLogClass;
#ifdef STEVES_NEW_CATCHER
/*
** This was me trying to be clever and fix the memory leak in the memlog. Unfortunately, the Get_Log member can be called
** during the process of exiting the process (IYSWIM) and you get it trying to re-allocate the MemLogClass I just freed.
** Solution is just to disable memlog when I'm trying to find memory leaks. ST - 6/18/2001 9:51PM
*/
if (!_MemLogAllocated) {
atexit(&Release_Log);
}
_MemLogAllocated = true;
#endif //STEVES_NEW_CATCHER
}
return _TheMemLog;
}
/***********************************************************************************************
* WWMemoryLogClass::Release_Log -- Free the memory used by WWMemoryLogClass so it doesn't leak*
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: Called as part of _onexit processing *
* *
* It's messy, but I assume there's a reason it's not statically allocated... *
* OK, now I get it *
* *
* HISTORY: *
* 6/13/2001 8:55PM ST : Created *
*=============================================================================================*/
void __cdecl WWMemoryLogClass::Release_Log(void)
{
MemLogMutexLockClass lock;
if (_TheMemLog) {
delete _TheMemLog;
_TheMemLog = NULL;
}
}
/***************************************************************************************************
**
** Allocating and Freeing memory
**
** PLEASE NOTE: The user is expected to implement global new and delete functions in his own
** code which call WWMemoryLogClass::Allocate_Memory and WWMemoryLogClass::Release_Memory.
** This was the only solution I could come up given that some APPS have their own new and delete
** functions or enable the CRT ones. It was also not an option to move this entire system into
** the APP because I wanted all of our LIBs to participate in the memory usage logging...
**
***************************************************************************************************/
const int WWMEMLOG_KEY0 = (unsigned('G')<<24) | (unsigned('g')<<16) | (unsigned('0')<<8) | unsigned('l');
const int WWMEMLOG_KEY1 = (unsigned('~')<<24) | (unsigned('_')<<16) | (unsigned('d')<<8) | unsigned('3');
/**
** MemoryLogStruct
** This structure is added to the beginning of each memory allocation to facilitate
** tracking which category the memory belongs to when it is freed. The size of
** this struct is also 16 bytes so that we wont be seriously affecting the alignment
** of allocated memory...
*/
struct MemoryLogStruct
{
MemoryLogStruct(int category,int size) :
Key0(WWMEMLOG_KEY0),
Key1(WWMEMLOG_KEY1),
Category(category),
Size(size)
{}
bool Is_Valid_Memory_Log(void) { return ((Key0 == WWMEMLOG_KEY0) && (Key1 == WWMEMLOG_KEY1)); }
int Key0; // if this is not equal to WWMEMLOG_KEY0 then we don't have a valid log
int Key1; // should be equal to WWMEMLOG_KEY1
int Category; // category this memory belongs to
int Size; // size of the allocation
};
/***********************************************************************************************
* WWMemoryLogClass::Allocate_Memory -- allocates memory *
* *
* This function adds a header to the memory allocated so that when the memory is freed *
* the proper memory category size can be decremented. The application using this logging *
* system should call this function from inside its overloaded 'new' operator. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/29/2001 gth : Created. *
*=============================================================================================*/
void * WWMemoryLogClass::Allocate_Memory(size_t size)
{
#if DISABLE_MEMLOG
AllocateCount++;
return ALLOC_MEMORY(size);
#else
__declspec( thread ) static bool reentrancy_test = false;
MemLogMutexLockClass lock;
if (reentrancy_test) {
return ALLOC_MEMORY(size);
} else {
reentrancy_test = true;
/*
** Allocate space for the requested buffer + our logging structure
*/
void * ptr = ALLOC_MEMORY(size + sizeof(MemoryLogStruct));
if (ptr != NULL) {
/*
** Record this allocation
*/
int active_category = WWMemoryLogClass::Register_Memory_Allocated(size);
/*
** Write our logging structure into the beginning of the buffer. I'm using
** placement new syntax to initialize the log structure right in the memory buffer
*/
new(ptr) MemoryLogStruct(active_category,size);
/*
** Return the allocated memory to the user, skipping past our log structure.
*/
reentrancy_test = false;
return (void*)(((char *)ptr) + sizeof(MemoryLogStruct));
} else {
reentrancy_test = false;
return ptr;
}
}
#endif //DISABLE_MEMLOG
}
/***********************************************************************************************
* WWMemoryLogClass::Release_Memory -- frees memory *
* *
* This function checks for a wwmemlog header and decrements the relevant memory category. *
* It should be called in the application's custom delete operator. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/29/2001 gth : Created. *
*=============================================================================================*/
void WWMemoryLogClass::Release_Memory(void *ptr)
{
#if DISABLE_MEMLOG
FREE_MEMORY(ptr);
FreeCount++;
#else
MemLogMutexLockClass lock;
if (ptr) {
/*
** Check if this memory is preceeded by a valid MemoryLogStruct
*/
MemoryLogStruct * memlog = (MemoryLogStruct*)((char*)ptr - sizeof(MemoryLogStruct));
if (memlog->Is_Valid_Memory_Log()) {
/*
** Valid MemoryLogStruct found, track the de-allocation and pass on
** to the built-in free function.
*/
WWMemoryLogClass::Register_Memory_Released(memlog->Category,memlog->Size);
FREE_MEMORY((void*)memlog);
} else {
/*
** No valid MemoryLogStruct found, just call free on the memory.
*/
FREE_MEMORY(ptr);
}
}
#endif //DISABLE_MEMLOG
}
// Reset allocate and free counters
void WWMemoryLogClass::Reset_Counters()
{
AllocateCount=0;
FreeCount=0;
}
// Return allocate count since last reset
int WWMemoryLogClass::Get_Allocate_Count()
{
return AllocateCount;
}
// Return allocate count since last reset
int WWMemoryLogClass::Get_Free_Count()
{
return FreeCount;
}
void WWMemoryLogClass::Init()
{
Get_Log()->Init();
}

173
Code/wwdebug/wwmemlog.h Normal file
View File

@@ -0,0 +1,173 @@
/*
** Command & Conquer Renegade(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 : WWDebug *
* *
* $Archive:: /Commando/Code/wwdebug/wwmemlog.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 11/09/01 6:51p $*
* *
* $Revision:: 8 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if _MSC_VER >= 1000
#pragma once
#endif
#ifndef WWMEMLOG_H
#define WWMEMLOG_H
class MemLogClass;
/**
** Memory Log Categories
** You can cause memory allocations to be "counted" against any of the following categories.
** NOTE: if you add a new category here, be sure to add its name to the array in the .cpp file...
*/
enum
{
MEM_UNKNOWN = 0,
MEM_GEOMETRY, // memory used by geometry data
MEM_ANIMATION, // memory used by animation data
MEM_TEXTURE, // memory used by textures
MEM_PATHFIND, // memory used by the pathfind system
MEM_VIS, // memory used by the vis system
MEM_SOUND, // memory used by the sound system
MEM_CULLINGDATA, // culling systems
MEM_STRINGS, // string data
MEM_GAMEDATA, // game engine datastructures
MEM_PHYSICSDATA, // physics engine datastructures
MEM_W3DDATA, // w3d datastructures (not including ones more applicable to above categories)
MEM_STATICALLOCATION,// all the allocations that happen before the memlog Init() function call are from statically allocated objects
MEM_GAMEINIT, // game init time allocations
MEM_RENDERER, // dx8 renderer
MEM_NETWORK,
MEM_BINK,
MEM_COUNT
};
/**
** WWMemoryLogClass
** This interface can provide information on how much memory has been allocated to each
** memory category. In order to enable this logging, you will need to implement global
** new and delete functions which call the Allocate_Memory and Release_Memory functions
** in this class. For example:
**
** void * ::operator new (size_t size)
** {
** return WWMemoryLogClass::Allocate_Memory(size);
** }
**
** void ::operator delete (void *ptr)
** {
** WWMemoryLogClass::Release_Memory(ptr);
** }
*/
class WWMemoryLogClass
{
public:
/*
** Accessors to the current memory map
*/
static int Get_Category_Count(void);
static const char * Get_Category_Name(int category);
static int Get_Current_Allocated_Memory(int category);
static int Get_Peak_Allocated_Memory(int category);
/*
** Interface for the debug version of new and delete
*/
static int Register_Memory_Allocated(int size);
static void Register_Memory_Released(int category,int size);
/*
** New and Delete functions. If you want to use this logging system,
** implement global new and delete functions which call into these
** functions.
*/
static void * Allocate_Memory(size_t size);
static void Release_Memory(void * mem);
static void Reset_Counters(); // Reset allocate and free counters
static int Get_Allocate_Count(); // Return allocate count since last reset
static int Get_Free_Count(); // Return allocate count since last reset
static void Init();
protected:
/*
** Interface for WWMemorySampleClass to set the active category
*/
static void Push_Active_Category(int category);
static void Pop_Active_Category(void);
static MemLogClass * Get_Log(void);
static void Release_Log(void);
friend class WWMemorySampleClass;
};
/**
** WWMemorySampleClass
** This class is meant to be created and destroyed on the stack to automatically push
** and pop the desired memory category. NOTE: this class should not be used directly,
** instead, use the WWMEMLOG macros!
*/
class WWMemorySampleClass
{
public:
WWMemorySampleClass(int category) { WWMemoryLogClass::Push_Active_Category(category); }
~WWMemorySampleClass(void) { WWMemoryLogClass::Pop_Active_Category(); }
};
/*
** Use the WWMEMLOG macro to track all memory allocations within the current scope.
** If WWDEBUG is not enabled, memory usage logging will be disabled.
*/
#ifdef WWDEBUG
#define WWMEMLOG( category ) WWMemorySampleClass _memsample( category )
#else
#define WWMEMLOG( category )
#endif
#endif //WWMEMLOG_H

801
Code/wwdebug/wwprofile.cpp Normal file
View File

@@ -0,0 +1,801 @@
/*
** Command & Conquer Renegade(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 : WWDebug *
* *
* $Archive:: /Commando/Code/wwdebug/wwprofile.cpp $*
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 4/01/02 10:30a $*
* *
* $Revision:: 20 $*
* *
*---------------------------------------------------------------------------------------------*
* WWProfile_Get_Ticks -- Retrieves the cpu performance counter *
* 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 "wwprofile.h"
#include "fastallocator.h"
#include "wwdebug.h"
#include <windows.h>
//#include "systimer.h"
#include "systimer.h"
#include "rawfile.h"
#include "ffactory.h"
#include "simplevec.h"
#include "cpudetect.h"
static SimpleDynVecClass<WWProfileHierachyNodeClass*> ProfileCollectVector;
static double TotalFrameTimes;
static bool ProfileCollecting;
unsigned WWProfile_Get_System_Time()
{
return TIMEGETTIME();
}
/***********************************************************************************************
* 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 = TIMEGETTIME();
#else
__asm
{
push edx;
push ecx;
push eax;
mov ecx,ticks;
_emit 0Fh
_emit 31h
mov [ecx],eax;
mov [ecx+4],edx;
pop eax;
pop ecx;
pop edx;
}
#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* WWProfileHierachyNodeClass::Clone_Hierarchy(WWProfileHierachyNodeClass* parent)
{
WWProfileHierachyNodeClass* node=new WWProfileHierachyNodeClass(Name,parent);
node->TotalCalls=TotalCalls;
node->TotalTime=TotalTime;
node->StartTime=StartTime;
node->RecursionCounter=RecursionCounter;
if (Child) {
node->Child=Child->Clone_Hierarchy(this);
}
if (Sibling) {
node->Sibling=Sibling->Clone_Hierarchy(parent);
}
return node;
}
void WWProfileHierachyNodeClass::Write_To_File(FileClass* file,int recursion)
{
if (TotalTime!=0.0f) {
int i;
StringClass string;
StringClass work;
for (i=0;i<recursion;++i) { string+="\t"; }
work.Format("%s\t%d\t%f\r\n",Name,TotalCalls,TotalTime*1000.0f);
string+=work;
file->Write(string.Peek_Buffer(),string.Get_Length());
}
if (Child) {
Child->Write_To_File(file,recursion+1);
}
if (Sibling) {
Sibling->Write_To_File(file,recursion);
}
}
/***********************************************************************************************
* 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 = new 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;
TotalTime += float(double(time)*CPUDetectClass::Get_Inv_Processor_Ticks_Per_Second());
}
}
return RecursionCounter == 0;
}
/***************************************************************************************************
**
** WWProfileManager Implementation
**
***************************************************************************************************/
WWProfileHierachyNodeClass WWProfileManager::Root( "Root", NULL );
WWProfileHierachyNodeClass * WWProfileManager::CurrentNode = &WWProfileManager::Root;
WWProfileHierachyNodeClass * WWProfileManager::CurrentRootNode = &WWProfileManager::Root;
int WWProfileManager::FrameCounter = 0;
__int64 WWProfileManager::ResetTime = 0;
static unsigned int ThreadID = static_cast<unsigned int>(-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();
}
void WWProfileManager::Start_Root_Profile( const char * name )
{
if (::GetCurrentThreadId() != ThreadID) {
return;
}
if (name != CurrentRootNode->Get_Name()) {
CurrentRootNode = CurrentRootNode->Get_Sub_Node( name );
}
CurrentRootNode->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();
}
}
void WWProfileManager::Stop_Root_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 (CurrentRootNode->Return()) {
CurrentRootNode = CurrentRootNode->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 )
{
if (ProfileCollecting) {
float time=Get_Time_Since_Reset();
TotalFrameTimes+=time;
WWProfileHierachyNodeClass* new_root=Root.Clone_Hierarchy(NULL);
new_root->Set_Total_Time(time);
new_root->Set_Total_Calls(1);
ProfileCollectVector.Add(new_root);
Reset();
}
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(double(time) * CPUDetectClass::Get_Inv_Processor_Ticks_Per_Second());
}
/***********************************************************************************************
* 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 new 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;
}
void WWProfileManager::Begin_Collecting()
{
Reset();
ProfileCollecting=true;
TotalFrameTimes=0.0;
}
void WWProfileManager::End_Collecting(const char* filename)
{
int i;
if (filename && ProfileCollectVector.Count()!=0) {
FileClass * file= _TheWritingFileFactory->Get_File(filename);
if (file != NULL) {
//
// Open or create the file
//
file->Open (FileClass::WRITE);
StringClass str;
float avg_frame_time=TotalFrameTimes/float(ProfileCollectVector.Count());
str.Format(
"Total frames: %d, average frame time: %fms\r\n"
"All frames taking more than twice the average frame time are marked with keyword SPIKE.\r\n\r\n",
ProfileCollectVector.Count(),avg_frame_time*1000.0f);
file->Write(str.Peek_Buffer(),str.Get_Length());
for (i=0;i<ProfileCollectVector.Count();++i) {
float frame_time=ProfileCollectVector[i]->Get_Total_Time();
str.Format("FRAME: %d %fms %s ---------------\r\n",i,frame_time*1000.0f,frame_time>avg_frame_time*2.0f ? "SPIKE" : "");
file->Write(str.Peek_Buffer(),str.Get_Length());
ProfileCollectVector[i]->Write_To_File(file,0);
}
//
// Close the file
//
file->Close ();
_TheWritingFileFactory->Return_File (file);
}
}
for (i=0;i<ProfileCollectVector.Count();++i) {
delete ProfileCollectVector[i];
ProfileCollectVector[i]=0;
}
ProfileCollectVector.Delete_All();
ProfileCollecting=false;
}
/***********************************************************************************************
* 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 new 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 * CPUDetectClass::Get_Inv_Processor_Ticks_Per_Second();
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 * CPUDetectClass::Get_Inv_Processor_Ticks_Per_Second();
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
unsigned WWMemoryAndTimeLog::TabCount;
WWMemoryAndTimeLog::WWMemoryAndTimeLog(const char* name)
:
Name(name),
TimeStart(WWProfile_Get_System_Time()),
AllocCountStart(FastAllocatorGeneral::Get_Allocator()->Get_Total_Allocation_Count()),
AllocSizeStart(FastAllocatorGeneral::Get_Allocator()->Get_Total_Allocated_Size())
{
IntermediateTimeStart=TimeStart;
IntermediateAllocCountStart=AllocCountStart;
IntermediateAllocSizeStart=AllocSizeStart;
StringClass tmp(0,true);
for (unsigned i=0;i<TabCount;++i) tmp+="\t";
WWRELEASE_SAY(("%s%s {\n",tmp,name));
TabCount++;
}
WWMemoryAndTimeLog::~WWMemoryAndTimeLog()
{
if (TabCount>0) TabCount--;
StringClass tmp(0,true);
for (unsigned i=0;i<TabCount;++i) tmp+="\t";
WWRELEASE_SAY(("%s} ",tmp));
unsigned current_time=WWProfile_Get_System_Time();
int current_alloc_count=FastAllocatorGeneral::Get_Allocator()->Get_Total_Allocation_Count();
int current_alloc_size=FastAllocatorGeneral::Get_Allocator()->Get_Total_Allocated_Size();
WWRELEASE_SAY(("IN TOTAL %s took %d.%3.3d s, did %d memory allocations of %d bytes\n",
Name,
(current_time - TimeStart)/1000, (current_time - TimeStart)%1000,
current_alloc_count - AllocCountStart,
current_alloc_size - AllocSizeStart));
WWRELEASE_SAY(("\n"));
}
void WWMemoryAndTimeLog::Log_Intermediate(const char* text)
{
unsigned current_time=WWProfile_Get_System_Time();
int current_alloc_count=FastAllocatorGeneral::Get_Allocator()->Get_Total_Allocation_Count();
int current_alloc_size=FastAllocatorGeneral::Get_Allocator()->Get_Total_Allocated_Size();
StringClass tmp(0,true);
for (unsigned i=0;i<TabCount;++i) tmp+="\t";
WWRELEASE_SAY(("%s%s took %d.%3.3d s, did %d memory allocations of %d bytes\n",
tmp,
text,
(current_time - IntermediateTimeStart)/1000, (current_time - IntermediateTimeStart)%1000,
current_alloc_count - IntermediateAllocCountStart,
current_alloc_size - IntermediateAllocSizeStart));
IntermediateTimeStart=current_time;
IntermediateAllocCountStart=current_alloc_count;
IntermediateAllocSizeStart=current_alloc_size;
}

296
Code/wwdebug/wwprofile.h Normal file
View File

@@ -0,0 +1,296 @@
/*
** Command & Conquer Renegade(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 : WWDebug *
* *
* $Archive:: /Commando/Code/wwdebug/wwprofile.h $*
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 3/25/02 2:05p $*
* *
* $Revision:: 14 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
//#define ENABLE_TIME_AND_MEMORY_LOG
#ifndef WWPROFILE_H
#define WWPROFILE_H
#include "wwstring.h"
#ifdef _UNIX
typedef signed long long __int64;
typedef signed long long _int64;
#endif
// enable profiling by default in debug mode.
#ifdef WWDEBUG
#define ENABLE_WWPROFILE
#endif
extern unsigned WWProfile_Get_System_Time(); // timeGetTime() wrapper
class FileClass;
/*
** A node in the WWProfile Hierarchy Tree
*/
class WWProfileHierachyNodeClass {
public:
WWProfileHierachyNodeClass( const char * name, WWProfileHierachyNodeClass * parent );
~WWProfileHierachyNodeClass( void );
WWProfileHierachyNodeClass * Get_Sub_Node( const char * name );
WWProfileHierachyNodeClass * Get_Parent( void ) { return Parent; }
WWProfileHierachyNodeClass * Get_Sibling( void ) { return Sibling; }
WWProfileHierachyNodeClass * Get_Child( void ) { return Child; }
void Reset( void );
void Call( void );
bool Return( void );
const char * Get_Name( void ) { return Name; }
int Get_Total_Calls( void ) { return TotalCalls; }
float Get_Total_Time( void ) { return TotalTime; }
WWProfileHierachyNodeClass* Clone_Hierarchy(WWProfileHierachyNodeClass* parent);
void Write_To_File(FileClass* file,int recursion);
int Get_Total_Calls() const { return TotalCalls; }
float Get_Total_Time() const { return TotalTime; }
void Set_Total_Calls(int calls) { TotalCalls=calls; }
void Set_Total_Time(float time) { TotalTime=time; }
protected:
const char * Name;
int TotalCalls;
float TotalTime;
__int64 StartTime;
int RecursionCounter;
WWProfileHierachyNodeClass * Parent;
WWProfileHierachyNodeClass * Child;
WWProfileHierachyNodeClass * Sibling;
};
/*
** An iterator to navigate through the tree
*/
class WWProfileIterator
{
public:
// Access all the children of the current parent
void First(void);
void Next(void);
bool Is_Done(void);
void Enter_Child( void ); // Make the current child the new parent
void Enter_Child( int index ); // Make the given child the new parent
void Enter_Parent( void ); // Make the current parent's parent the new parent
// Access the current child
const char * Get_Current_Name( void ) { return CurrentChild->Get_Name(); }
int Get_Current_Total_Calls( void ) { return CurrentChild->Get_Total_Calls(); }
float Get_Current_Total_Time( void ) { return CurrentChild->Get_Total_Time(); }
// Access the current parent
const char * Get_Current_Parent_Name( void ) { return CurrentParent->Get_Name(); }
int Get_Current_Parent_Total_Calls( void ) { return CurrentParent->Get_Total_Calls(); }
float Get_Current_Parent_Total_Time( void ) { return CurrentParent->Get_Total_Time(); }
protected:
WWProfileHierachyNodeClass * CurrentParent;
WWProfileHierachyNodeClass * CurrentChild;
WWProfileIterator( WWProfileHierachyNodeClass * start );
friend class WWProfileManager;
};
/*
** An iterator to walk through the tree in depth first order
*/
class WWProfileInOrderIterator
{
public:
void First(void);
void Next(void);
bool Is_Done(void);
// Access the current node
const char * Get_Current_Name( void ) { return CurrentNode->Get_Name(); }
int Get_Current_Total_Calls( void ) { return CurrentNode->Get_Total_Calls(); }
float Get_Current_Total_Time( void ) { return CurrentNode->Get_Total_Time(); }
protected:
WWProfileHierachyNodeClass * CurrentNode;
WWProfileInOrderIterator( void );
friend class WWProfileManager;
};
/*
** The Manager for the WWProfile system
*/
class WWProfileManager {
public:
static void Start_Profile( const char * name );
static void Stop_Profile( void );
static void Start_Root_Profile( const char * name );
static void Stop_Root_Profile( void );
static void Reset( void );
static void Increment_Frame_Counter( void );
static int Get_Frame_Count_Since_Reset( void ) { return FrameCounter; }
static float Get_Time_Since_Reset( void );
static WWProfileIterator * Get_Iterator( void );
static void Release_Iterator( WWProfileIterator * iterator );
static WWProfileInOrderIterator * Get_In_Order_Iterator( void );
static void Release_In_Order_Iterator( WWProfileInOrderIterator * iterator );
static WWProfileHierachyNodeClass * Get_Root( void ) { return &Root; }
static void Begin_Collecting();
static void End_Collecting(const char* filename);
private:
static WWProfileHierachyNodeClass Root;
static WWProfileHierachyNodeClass * CurrentNode;
static WWProfileHierachyNodeClass * CurrentRootNode;
static int FrameCounter;
static __int64 ResetTime;
friend class WWProfileInOrderIterator;
};
/*
** WWProfileSampleClass is a simple way to profile a function's scope
** Use the WWPROFILE macro at the start of scope to time
*/
class WWProfileSampleClass {
bool IsRoot;
public:
WWProfileSampleClass( const char * name, bool is_root ) : IsRoot(is_root)
{
if (IsRoot) WWProfileManager::Start_Root_Profile( name );
else WWProfileManager::Start_Profile( name );
}
~WWProfileSampleClass( void )
{
if (IsRoot) WWProfileManager::Stop_Root_Profile();
else WWProfileManager::Stop_Profile();
}
};
#ifdef ENABLE_WWPROFILE
#define WWPROFILE( name ) WWProfileSampleClass _wwprofile( name, false )
#define WWROOTPROFILE( name ) WWProfileSampleClass _wwprofile( name, true )
#else
#define WWPROFILE( name )
#define WWROOTPROFILE( name )
#endif
/*
** WWTimeIt is like WWProfile, but it doesn't save anything, it just times one routine, regardless of thread
*/
class WWTimeItClass {
public:
WWTimeItClass( const char * name );
~WWTimeItClass( void );
private:
const char * Name;
__int64 Time;
};
#ifdef ENABLE_WWPROFILE
#define WWTIMEIT( name ) WWTimeItClass _wwtimeit( name )
#else
#define WWTIMEIT( name )
#endif
/*
** TSS 06/27/01
** WWMeasureItClass is like WWTimeItClass, but it pokes the result into the given float,
** and can be used in the release build.
*/
class WWMeasureItClass {
public:
WWMeasureItClass( float * p_result );
~WWMeasureItClass( void );
private:
__int64 Time;
float * PResult;
};
// ----------------------------------------------------------------------------
//
// Use the first macro to log time and memory usage within the stack segment.
// Use the second macro to log intermediate values. The intermediate values are
// calculated from the previous intermediate log, so you can log how much each
// item takes by placing the macro after each of the
//
// ----------------------------------------------------------------------------
#ifdef ENABLE_TIME_AND_MEMORY_LOG
#define WWLOG_PREPARE_TIME_AND_MEMORY(t) WWMemoryAndTimeLog memory_and_time_log(t)
#define WWLOG_INTERMEDIATE(t) memory_and_time_log.Log_Intermediate(t)
#else
#define WWLOG_PREPARE_TIME_AND_MEMORY(t)
#define WWLOG_INTERMEDIATE(t)
#endif
struct WWMemoryAndTimeLog
{
unsigned TimeStart;
unsigned IntermediateTimeStart;
int AllocCountStart;
int IntermediateAllocCountStart;
int AllocSizeStart;
int IntermediateAllocSizeStart;
StringClass Name;
static unsigned TabCount;
WWMemoryAndTimeLog(const char* name);
~WWMemoryAndTimeLog();
void Log_Intermediate(const char* text);
};
#endif // WWPROFILE_H