/* ** 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 . */ /*********************************************************************************************** *** 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)