234 lines
7.3 KiB
C++
234 lines
7.3 KiB
C++
/*
|
|
** 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 : LevelEdit *
|
|
* *
|
|
* $Archive:: /Commando/Code/Tools/LevelEdit/test.cpp $*
|
|
* *
|
|
* Author:: Patrick Smith *
|
|
* *
|
|
* $Modtime:: 7/23/99 7:23p $*
|
|
* *
|
|
* $Revision:: 6 $*
|
|
* *
|
|
*---------------------------------------------------------------------------------------------*
|
|
* Functions: *
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
//#define _CATCH_MEM_LEAKS
|
|
#if (defined(_CATCH_MEM_LEAKS) && defined (_DEBUG))
|
|
|
|
#include "Windows.H"
|
|
#include "Winnt.H"
|
|
#include "ImageHlp.H"
|
|
|
|
extern void *_ptr_array[10000];
|
|
extern int g_allocs;
|
|
extern int g_frees;
|
|
|
|
|
|
typedef struct {
|
|
char function_name[80];
|
|
} MY_DEBUG_INFO;
|
|
|
|
const int DATA_LEVELS = 5;
|
|
const DATA_SIZE = sizeof (MY_DEBUG_INFO) * DATA_LEVELS;
|
|
|
|
|
|
void *_ptr_array[10000] = { 0 };
|
|
int g_allocs = 0;
|
|
int g_frees = 0;
|
|
bool _g_binit = false;
|
|
|
|
|
|
class ReportDebugClass
|
|
{
|
|
public:
|
|
ReportDebugClass (void) {}
|
|
~ReportDebugClass (void)
|
|
{
|
|
int counter = 0;
|
|
for (int index = 0; index < 10000; index ++) {
|
|
if (_ptr_array[index] != 0) {
|
|
MY_DEBUG_INFO debug_info[DATA_LEVELS] = { 0 };
|
|
::memcpy (debug_info, ((char*)(_ptr_array[index]))-DATA_SIZE, DATA_SIZE);
|
|
counter ++;
|
|
|
|
::OutputDebugString ("Possible memory leak for stack:\n");
|
|
for (int ilevel = 0; ilevel < DATA_LEVELS; ilevel ++) {
|
|
::OutputDebugString ("\t");
|
|
::OutputDebugString (debug_info[ilevel].function_name);
|
|
::OutputDebugString ("\n");
|
|
}
|
|
::OutputDebugString ("\n");
|
|
}
|
|
}
|
|
return ;
|
|
}
|
|
};
|
|
|
|
ReportDebugClass _debug_class;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ::operator new
|
|
//
|
|
void *
|
|
::operator new (size_t size)
|
|
{
|
|
|
|
if (_g_binit == false) {
|
|
_g_binit = true;
|
|
::SymInitialize (::GetCurrentProcess (), NULL, TRUE);
|
|
}
|
|
|
|
|
|
CONTEXT context = { 0 };
|
|
HANDLE hthread = ::GetCurrentThread ();
|
|
context.ContextFlags = CONTEXT_FULL;
|
|
::GetThreadContext (hthread, &context);
|
|
|
|
HANDLE hthread2 = NULL;
|
|
DuplicateHandle (::GetCurrentProcess (), hthread, ::GetCurrentProcess (), &hthread2, THREAD_ALL_ACCESS | THREAD_GET_CONTEXT, FALSE, 0);
|
|
context.ContextFlags = CONTEXT_FULL;
|
|
::GetThreadContext (hthread2, &context);
|
|
::CloseHandle (hthread2);
|
|
|
|
MY_DEBUG_INFO debug_info[DATA_LEVELS] = { 0 };
|
|
|
|
// Initialize the STACKFRAME structure for the first call. This is only
|
|
// necessary for Intel CPUs, and isn't mentioned in the documentation.
|
|
STACKFRAME sf = { 0 };
|
|
sf.AddrPC.Offset = context.Eip;
|
|
sf.AddrPC.Mode = AddrModeFlat;
|
|
sf.AddrStack.Offset = context.Esp;
|
|
sf.AddrStack.Mode = AddrModeFlat;
|
|
sf.AddrFrame.Offset = context.Ebp;
|
|
sf.AddrFrame.Mode = AddrModeFlat;
|
|
|
|
|
|
// Walk the stack up to DATA_LEVELS calls
|
|
for (int iframe = 0; iframe < DATA_LEVELS+1; iframe ++)
|
|
{
|
|
// Get a stack trace for this frame
|
|
if (!::StackWalk (IMAGE_FILE_MACHINE_I386,
|
|
GetCurrentProcess (),
|
|
GetCurrentThread (),
|
|
&sf,
|
|
&context,
|
|
0,
|
|
SymFunctionTableAccess,
|
|
SymGetModuleBase,
|
|
0)) {
|
|
break;
|
|
}
|
|
|
|
// Basic sanity check to make sure
|
|
if (sf.AddrFrame.Offset == 0) {
|
|
break;
|
|
}
|
|
|
|
// IMAGEHLP is wacky, and requires you to pass in a pointer to an
|
|
// IMAGEHLP_SYMBOL structure. The problem is that this structure is
|
|
// variable length. That is, you determine how big the structure is
|
|
// at runtime. This means that you can't use sizeof(struct).
|
|
// So...make a buffer that's big enough, and make a pointer
|
|
// to the buffer. We also need to initialize not one, but TWO
|
|
// members of the structure before it can be used.
|
|
|
|
BYTE symbol_buffer [sizeof(IMAGEHLP_SYMBOL) + 512];
|
|
PIMAGEHLP_SYMBOL psymbol = (PIMAGEHLP_SYMBOL)symbol_buffer;
|
|
::memset (symbol_buffer, 0, sizeof (symbol_buffer));
|
|
psymbol->SizeOfStruct = sizeof(symbol_buffer);
|
|
psymbol->MaxNameLength = 512;
|
|
|
|
DWORD sym_displacement = 0;
|
|
if (::SymGetSymFromAddr (::GetCurrentProcess (),
|
|
sf.AddrPC.Offset,
|
|
&sym_displacement,
|
|
psymbol))
|
|
{
|
|
// We don't care about the first stack frame (its inside the GetThreadContext call)
|
|
if (iframe >=1) {
|
|
::lstrcpyn (debug_info[iframe-1].function_name, psymbol->Name, sizeof (MY_DEBUG_INFO)-1);
|
|
debug_info[iframe-1].function_name[sizeof (debug_info[iframe-1].function_name)-1] = 0;
|
|
}
|
|
} else {
|
|
DWORD dwerror = ::GetLastError ();
|
|
int itest = 0;
|
|
}
|
|
|
|
}
|
|
|
|
// Allocate the buffer + our debug information
|
|
void *ptr = ::malloc (size + DATA_SIZE);
|
|
::memcpy (ptr, debug_info, DATA_SIZE);
|
|
|
|
// Stick the new buffer into our array (should be moved to linked list)
|
|
if (ptr) {
|
|
for (int index = 0; index < 10000; index ++) {
|
|
if (_ptr_array[index] == 0) {
|
|
_ptr_array[index] = (void *)(((char *)ptr) + DATA_SIZE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_allocs ++;
|
|
|
|
// Return the pointer
|
|
return (void*)(((char *)ptr) + DATA_SIZE);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// :::operator delete
|
|
//
|
|
void
|
|
::operator delete (void *ptr)
|
|
{
|
|
if (ptr) {
|
|
|
|
// Attempt to find the buffer in our array
|
|
bool bfound = false;
|
|
for (int index = 0; index < 10000; index ++) {
|
|
if (_ptr_array[index] == ptr) {
|
|
::free ((void*)(((char *)ptr) - DATA_SIZE));
|
|
_ptr_array[index] = 0;
|
|
bfound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we didn't find it in our array, just free it
|
|
if (!bfound) {
|
|
//::free (ptr);
|
|
}
|
|
}
|
|
|
|
g_frees ++;
|
|
return ;
|
|
}
|
|
|
|
#endif //defined(_CATCH_MEM_LEAKS) && defined (_DEBUG)
|