Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
wwmemlog.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/***********************************************************************************************
20 *** 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 ***
21 ***********************************************************************************************
22 * *
23 * Project Name : WWDebug *
24 * *
25 * $Archive:: /Commando/Code/wwdebug/wwmemlog.cpp $*
26 * *
27 * Original Author:: Greg Hjelstrom *
28 * *
29 * $Author:: Jani_p $*
30 * *
31 * $Modtime:: 11/21/01 2:03p $*
32 * *
33 * $Revision:: 27 $*
34 * *
35 *---------------------------------------------------------------------------------------------*
36 * Functions: *
37 * WWMemoryLogClass::Allocate_Memory -- allocates memory *
38 * WWMemoryLogClass::Release_Memory -- frees memory *
39 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
40
41#include "always.h"
42#include "wwmemlog.h"
43#include "wwdebug.h"
44#include "vector.h"
45#include "fastallocator.h"
46#include <windows.h>
47
48#define USE_FAST_ALLOCATOR
49
50#ifdef STEVES_NEW_CATCHER
51 #define DISABLE_MEMLOG 1
52#else //STEVES_NEW_CATCHER
53#ifdef PARAM_EDITING_ON
54 #define DISABLE_MEMLOG 1
55#else //PARAM_EDITING_ON
56 #define DISABLE_MEMLOG 1
57#endif //PARAM_EDITING_ON
58#endif //STEVES_NEW_CATCHER*/
59
60#ifdef USE_FAST_ALLOCATOR
61 #define ALLOC_MEMORY(n) FastAllocatorGeneral::Get_Allocator()->Alloc(n)
62 #define FREE_MEMORY(p) FastAllocatorGeneral::Get_Allocator()->Free(p)
63#else
64 #define ALLOC_MEMORY(n) ::malloc(n)
65 #define FREE_MEMORY(p) ::free(p)
66#endif
67
68
69/*
70** Enable one of the following #defines to specify which thread-sychronization
71** method to use.
72*/
73#ifndef _UNIX
74#define MEMLOG_USE_MUTEX 0
75#define MEMLOG_USE_CRITICALSECTION 1
76#define MEMLOG_USE_FASTCRITICALSECTION 0
77#else
78#undef DISABLE_MEMLOG
79#define DISABLE_MEMLOG 1
80#define MEMLOG_USE_MUTEX 0
81#define MEMLOG_USE_CRITICALSECTION 0
82#define MEMLOG_USE_FASTCRITICALSECTION 0
83#endif
84
85#if (DISABLE_MEMLOG == 0)
87#else
89#endif
90
91static unsigned AllocateCount;
92static unsigned FreeCount;
93
94/*
95** Name for each memory category. I'm padding the array with some "undefined" strings in case
96** someone forgets to set the name when adding a new category.
97*/
98static char * _MemoryCategoryNames[] =
99{
100 "UNKNOWN",
101 "Geometry",
102 "Animation",
103 "Texture",
104 "Pathfind",
105 "Vis",
106 "Sound",
107 "CullingData",
108 "Strings",
109 "GameData",
110 "PhysicsData",
111 "W3dData",
112 "StaticAllocations",
113 "GameInit",
114 "Renderer",
115 "Network",
116 "BINK",
117 "<undefined>",
118 "<undefined>",
119 "<undefined>",
120 "<undefined>",
121};
122
123
144
145
146
155{
156public:
162
163 // If object was created but not Init'd, ThreadID will be -1 and Count == 0
164 // If object was created and Init'd, ThreadID will not be -1. We expect Count to return to 1 after all Pop's
165 ~ActiveCategoryStackClass(void) { WWASSERT((ThreadID == -1 && Count == 0) || (ThreadID != -1 && Count == 1)); }
166
168
169 bool operator == (const ActiveCategoryStackClass &) { return false; }
170 bool operator != (const ActiveCategoryStackClass &) { return true; }
171
172 void Init(int thread_id) { ThreadID = thread_id; Count = 0; Push(MEM_UNKNOWN); }
173 void Set_Thread_ID(int id) { WWASSERT(ThreadID != -1); ThreadID = id; }
174 int Get_Thread_ID(void) { return ThreadID; }
175
176 void Push(int active_category) { WWASSERT(ThreadID != -1); (*this)[Count] = active_category; Count++; }
177 void Pop(void) { WWASSERT(ThreadID != -1) ; Count--; }
178 int Current(void) { WWASSERT(ThreadID != -1); return (*this)[Count-1]; }
179
180protected:
181
183 int Count;
184};
185
186
187
194const int MAX_CATEGORY_STACKS = 256; // maximum number of threads we expect to encounter...
195
196class ActiveCategoryClass : public VectorClass<ActiveCategoryStackClass>
197{
198public:
199
201
202 void Push(int active_category) { Get_Active_Stack().Push(active_category); }
203 void Pop(void) { Get_Active_Stack().Pop(); }
204 int Current(void) { return Get_Active_Stack().Current(); }
205
206protected:
207
209
210 int Count;
211};
212
213
220{
221public:
222
223 int Get_Current_Allocated_Memory(int category);
224 int Get_Peak_Allocated_Memory(int category);
225
226 /*
227 ** Interface for recording allocations and de-allocations
228 */
229 int Register_Memory_Allocated(int size);
230 void Register_Memory_Released(int category,int size);
231
232 void Push_Active_Category(int category);
233 void Pop_Active_Category(void);
234
235 void Init();
236
237private:
238
239 MemoryCounterClass _MemoryCounters[MEM_COUNT];
240 ActiveCategoryClass _ActiveCategoryTracker;
241
242};
243
244
251static MemLogClass * _TheMemLog = NULL;
252static bool _MemLogAllocated = false;
253
254#if MEMLOG_USE_MUTEX
255static void * _MemLogMutex = NULL;
256static int _MemLogLockCounter = 0;
257#endif
258
259#if MEMLOG_USE_CRITICALSECTION
260static bool _MemLogCriticalSectionAllocated = false;
261static char _MemLogCriticalSectionHandle[sizeof(CRITICAL_SECTION)];
262#endif
263
264#if MEMLOG_USE_FASTCRITICALSECTION
265volatile unsigned _MemLogSemaphore = 0;
266#endif
267
268/*
269** Use this code to get access to the mutex...
270*/
272{
273#if MEMLOG_USE_MUTEX
274
275 if (_MemLogMutex == NULL) {
276 _MemLogMutex=CreateMutex(NULL,false,NULL);
277 WWASSERT(_MemLogMutex);
278 }
279 return _MemLogMutex;
280
281#endif
282
283#if MEMLOG_USE_CRITICALSECTION
284
285 if (_MemLogCriticalSectionAllocated == false) {
286 InitializeCriticalSection((CRITICAL_SECTION*)_MemLogCriticalSectionHandle);
287 _MemLogCriticalSectionAllocated = true;
288 }
289 return _MemLogCriticalSectionHandle;
290
291#endif
292}
293
295{
296#if MEMLOG_USE_MUTEX
297
298 void * mutex = Get_Mem_Log_Mutex();
299#ifdef DEBUG_CRASHING
300 int res =
301#endif
302 WaitForSingleObject(mutex,INFINITE);
303 WWASSERT(res==WAIT_OBJECT_0);
304 _MemLogLockCounter++;
305#endif
306
307#if MEMLOG_USE_CRITICALSECTION
308
310 EnterCriticalSection((CRITICAL_SECTION*)_MemLogCriticalSectionHandle);
311
312#endif
313
314#if MEMLOG_USE_FASTCRITICALSECTION
315
316 volatile unsigned& nFlag=_MemLogSemaphore;
317
318 #define ts_lock _emit 0xF0
319 assert(((unsigned)&nFlag % 4) == 0);
320
321 __asm mov ebx, [nFlag]
322 __asm ts_lock
323 __asm bts dword ptr [ebx], 0
324 __asm jc The_Bit_Was_Previously_Set_So_Try_Again
325 return;
326
327 The_Bit_Was_Previously_Set_So_Try_Again:
329 __asm mov ebx, [nFlag]
330 __asm ts_lock
331 __asm bts dword ptr [ebx], 0
332 __asm jc The_Bit_Was_Previously_Set_So_Try_Again
333
334#endif
335}
336
338{
339#if MEMLOG_USE_MUTEX
340
341 void * mutex = Get_Mem_Log_Mutex();
342 _MemLogLockCounter--;
343#ifdef DEBUG_CRASHING
344 int res=
345#endif
346 ReleaseMutex(mutex);
347 WWASSERT(res);
348
349#endif
350#if MEMLOG_USE_CRITICALSECTION
351
353 LeaveCriticalSection((CRITICAL_SECTION*)_MemLogCriticalSectionHandle);
354
355#endif
356
357#if MEMLOG_USE_FASTCRITICALSECTION
358 _MemLogSemaphore = 0;
359#endif
360}
361
368
369
370
371/***************************************************************************************************
372**
373** ActiveCategoryStackClass Implementation
374**
375***************************************************************************************************/
378{
379 if (this != &that) {
381 ThreadID = that.ThreadID;
382 Count = that.Count;
383 }
384 return *this;
385}
386
387
388/***************************************************************************************************
389**
390** ActiveCategoryClass Implementation
391**
392***************************************************************************************************/
394{
395 int current_thread = ::GetCurrentThreadId();
396
397 /*
398 ** If we already have an allocated category stack for the current thread,
399 ** just return its active category.
400 */
401 for (int i=0; i<Count; i++) {
402 ActiveCategoryStackClass & cat_stack = (*this)[i];
403 if (cat_stack.Get_Thread_ID() == current_thread) {
404 return cat_stack;
405 }
406 }
407
408 /*
409 ** If we fall through to here, we need to allocate a new category stack
410 ** for this thread.
411 */
412 (*this)[Count].Init(current_thread);
413 Count++;
414 return (*this)[Count-1];
415}
416
417
418/***************************************************************************************************
419**
420** MemLogClass Implementation
421**
422***************************************************************************************************/
424{
426 return _MemoryCounters[category].Get_Current_Allocated_Memory();
427}
428
430{
432 return _MemoryCounters[category].Get_Peak_Allocated_Memory();
433}
434
436{
437 {
439 WWASSERT(_ActiveCategoryTracker.Current()==MEM_STATICALLOCATION);
440 }
441 Pop_Active_Category(); // Remove staticallocation state forever
442}
443
445{
447
448 int active_category = _ActiveCategoryTracker.Current();
449 WWASSERT((active_category >= 0) && (active_category < MEM_COUNT));
450 _MemoryCounters[active_category].Memory_Allocated(size);
451
452 return active_category;
453}
454
455void MemLogClass::Register_Memory_Released(int category,int size)
456{
458 _MemoryCounters[category].Memory_Released(size);
459}
460
462{
464 WWASSERT((category >= 0) && (category < MEM_COUNT));
465 _ActiveCategoryTracker.Push(category);
466}
467
469{
471 _ActiveCategoryTracker.Pop();
472}
473
474
475
476/***************************************************************************************************
477**
478** WWMemoryLogClass Implementation
479**
480***************************************************************************************************/
481
483{
484 return MEM_COUNT;
485}
486
487const char * WWMemoryLogClass::Get_Category_Name(int category)
488{
489 return _MemoryCategoryNames[category];
490}
491
496
498{
499 return Get_Log()->Get_Peak_Allocated_Memory(category);
500}
501
503{
504#if (DISABLE_MEMLOG == 0)
505 Get_Log()->Push_Active_Category(category);
506#endif //(DISABLE_MEMLOG == 0)
507}
508
510{
511#if (DISABLE_MEMLOG == 0)
513#endif //(DISABLE_MEMLOG == 0)
514}
515
520
522{
523 Get_Log()->Register_Memory_Released(category,size);
524}
525
526
527static void _MemLogCleanup(void)
528{
529 delete _TheMemLog;
530}
531
532
534{
536
537 if (_TheMemLog == NULL) {
538 //assert(!_MemLogAllocated);
539 _TheMemLog = W3DNEW MemLogClass;
540
541#ifdef STEVES_NEW_CATCHER
542 /*
543 ** This was me trying to be clever and fix the memory leak in the memlog. Unfortunately, the Get_Log member can be called
544 ** during the process of exiting the process (IYSWIM) and you get it trying to re-allocate the MemLogClass I just freed.
545 ** Solution is just to disable memlog when I'm trying to find memory leaks. ST - 6/18/2001 9:51PM
546 */
547 if (!_MemLogAllocated) {
548 atexit(&Release_Log);
549 }
550 _MemLogAllocated = true;
551#endif //STEVES_NEW_CATCHER
552 }
553 return _TheMemLog;
554}
555
556
557/***********************************************************************************************
558 * WWMemoryLogClass::Release_Log -- Free the memory used by WWMemoryLogClass so it doesn't leak*
559 * *
560 * *
561 * *
562 * INPUT: Nothing *
563 * *
564 * OUTPUT: Nothing *
565 * *
566 * WARNINGS: Called as part of _onexit processing *
567 * *
568 * It's messy, but I assume there's a reason it's not statically allocated... *
569 * OK, now I get it *
570 * *
571 * HISTORY: *
572 * 6/13/2001 8:55PM ST : Created *
573 *=============================================================================================*/
575{
577 if (_TheMemLog) {
578 delete _TheMemLog;
579 _TheMemLog = NULL;
580 }
581}
582
583
584/***************************************************************************************************
585**
586** Allocating and Freeing memory
587**
588** PLEASE NOTE: The user is expected to implement global new and delete functions in his own
589** code which call WWMemoryLogClass::Allocate_Memory and WWMemoryLogClass::Release_Memory.
590** This was the only solution I could come up given that some APPS have their own new and delete
591** functions or enable the CRT ones. It was also not an option to move this entire system into
592** the APP because I wanted all of our LIBs to participate in the memory usage logging...
593**
594***************************************************************************************************/
595
596const int WWMEMLOG_KEY0 = (unsigned('G')<<24) | (unsigned('g')<<16) | (unsigned('0')<<8) | unsigned('l');
597const int WWMEMLOG_KEY1 = (unsigned('~')<<24) | (unsigned('_')<<16) | (unsigned('d')<<8) | unsigned('3');
598
599
608{
609 MemoryLogStruct(int category,int size) :
612 Category(category),
613 Size(size)
614 {}
615
616 bool Is_Valid_Memory_Log(void) { return ((Key0 == WWMEMLOG_KEY0) && (Key1 == WWMEMLOG_KEY1)); }
617
618 int Key0; // if this is not equal to WWMEMLOG_KEY0 then we don't have a valid log
619 int Key1; // should be equal to WWMEMLOG_KEY1
620 int Category; // category this memory belongs to
621 int Size; // size of the allocation
622};
623
624
625
626/***********************************************************************************************
627 * WWMemoryLogClass::Allocate_Memory -- allocates memory *
628 * *
629 * This function adds a header to the memory allocated so that when the memory is freed *
630 * the proper memory category size can be decremented. The application using this logging *
631 * system should call this function from inside its overloaded 'new' operator. *
632 * *
633 * INPUT: *
634 * *
635 * OUTPUT: *
636 * *
637 * WARNINGS: *
638 * *
639 * HISTORY: *
640 * 5/29/2001 gth : Created. *
641 *=============================================================================================*/
643{
644 AllocateCount++;
645#if DISABLE_MEMLOG
646 return ALLOC_MEMORY(size);
647#else
648
649 __declspec( thread ) static bool reentrancy_test = false;
651
652 if (reentrancy_test) {
653 return ALLOC_MEMORY(size);
654 } else {
655 reentrancy_test = true;
656
657 /*
658 ** Allocate space for the requested buffer + our logging structure
659 */
660 void * ptr = ALLOC_MEMORY(size + sizeof(MemoryLogStruct));
661
662 if (ptr != NULL) {
663 /*
664 ** Record this allocation
665 */
666 int active_category = WWMemoryLogClass::Register_Memory_Allocated(size);
667
668 /*
669 ** Write our logging structure into the beginning of the buffer. I'm using
670 ** placement new syntax to initialize the log structure right in the memory buffer
671 */
672 new(ptr) MemoryLogStruct(active_category,size);
673
674 /*
675 ** Return the allocated memory to the user, skipping past our log structure.
676 */
677 reentrancy_test = false;
678 return (void*)(((char *)ptr) + sizeof(MemoryLogStruct));
679
680 } else {
681 reentrancy_test = false;
682 return ptr;
683 }
684
685 }
686#endif //DISABLE_MEMLOG
687}
688
689
690/***********************************************************************************************
691 * WWMemoryLogClass::Release_Memory -- frees memory *
692 * *
693 * This function checks for a wwmemlog header and decrements the relevant memory category. *
694 * It should be called in the application's custom delete operator. *
695 * *
696 * INPUT: *
697 * *
698 * OUTPUT: *
699 * *
700 * WARNINGS: *
701 * *
702 * HISTORY: *
703 * 5/29/2001 gth : Created. *
704 *=============================================================================================*/
706{
707 FreeCount++;
708#if DISABLE_MEMLOG
709 FREE_MEMORY(ptr);
710#else
711
713 if (ptr) {
714
715 /*
716 ** Check if this memory is preceeded by a valid MemoryLogStruct
717 */
718 MemoryLogStruct * memlog = (MemoryLogStruct*)((char*)ptr - sizeof(MemoryLogStruct));
719 if (memlog->Is_Valid_Memory_Log()) {
720
721 /*
722 ** Valid MemoryLogStruct found, track the de-allocation and pass on
723 ** to the built-in free function.
724 */
726 FREE_MEMORY((void*)memlog);
727
728 } else {
729
730 /*
731 ** No valid MemoryLogStruct found, just call free on the memory.
732 */
733 FREE_MEMORY(ptr);
734 }
735 }
736
737#endif //DISABLE_MEMLOG
738}
739
740// Reset allocate and free counters
741
743{
744 AllocateCount=0;
745 FreeCount=0;
746}
747
748// Return allocate count since last reset
750{
751 return AllocateCount;
752}
753
754// Return allocate count since last reset
756{
757 return FreeCount;
758}
759
761{
762 Get_Log()->Init();
763}
#define NULL
Definition BaseType.h:92
#define max(x, y)
Definition BaseType.h:105
void __declspec(dllexport) CreateDebugDialog(void)
#define WWASSERT
#define W3DNEW
Definition always.h:109
#define WWINLINE
Definition always.h:172
ActiveCategoryStackClass & Get_Active_Stack(void)
Definition wwmemlog.cpp:393
void Push(int active_category)
Definition wwmemlog.cpp:202
bool operator!=(const ActiveCategoryStackClass &)
Definition wwmemlog.cpp:170
ActiveCategoryStackClass & operator=(const ActiveCategoryStackClass &that)
Definition wwmemlog.cpp:377
void Set_Thread_ID(int id)
Definition wwmemlog.cpp:173
void Init(int thread_id)
Definition wwmemlog.cpp:172
void Push(int active_category)
Definition wwmemlog.cpp:176
bool operator==(const ActiveCategoryStackClass &)
Definition wwmemlog.cpp:169
void Register_Memory_Released(int category, int size)
Definition wwmemlog.cpp:455
void Init()
Definition wwmemlog.cpp:435
void Push_Active_Category(int category)
Definition wwmemlog.cpp:461
int Get_Current_Allocated_Memory(int category)
Definition wwmemlog.cpp:423
void Pop_Active_Category(void)
Definition wwmemlog.cpp:468
int Register_Memory_Allocated(int size)
Definition wwmemlog.cpp:444
int Get_Peak_Allocated_Memory(int category)
Definition wwmemlog.cpp:429
void Memory_Allocated(int size)
Definition wwmemlog.cpp:134
int Get_Peak_Allocated_Memory(void)
Definition wwmemlog.cpp:138
int Get_Current_Allocated_Memory(void)
Definition wwmemlog.cpp:137
void Memory_Released(int size)
Definition wwmemlog.cpp:135
static void Switch_Thread()
Definition thread.cpp:130
virtual bool operator==(VectorClass< T > const &) const
Definition Vector.H:284
WWINLINE VectorClass(NoInitClass const &)
Definition Vector.H:91
static int Register_Memory_Allocated(int size)
Definition wwmemlog.cpp:516
static bool IsMemoryLogEnabled
Definition wwmemlog.h:142
static int Get_Category_Count(void)
Definition wwmemlog.cpp:482
static int Get_Free_Count()
Definition wwmemlog.cpp:755
static void Release_Memory(void *mem)
Definition wwmemlog.cpp:705
static const char * Get_Category_Name(int category)
Definition wwmemlog.cpp:487
static void Release_Log(void)
Definition wwmemlog.cpp:574
static int Get_Allocate_Count()
Definition wwmemlog.cpp:749
static void Init()
Definition wwmemlog.cpp:760
static void Reset_Counters()
Definition wwmemlog.cpp:742
static int Get_Current_Allocated_Memory(int category)
Definition wwmemlog.cpp:492
static void Pop_Active_Category(void)
Definition wwmemlog.cpp:509
static void Push_Active_Category(int category)
Definition wwmemlog.cpp:502
static void Register_Memory_Released(int category, int size)
Definition wwmemlog.cpp:521
static int Get_Peak_Allocated_Memory(int category)
Definition wwmemlog.cpp:497
static void * Allocate_Memory(size_t size)
Definition wwmemlog.cpp:642
static MemLogClass * Get_Log(void)
Definition wwmemlog.cpp:533
#define ts_lock
bool Is_Valid_Memory_Log(void)
Definition wwmemlog.cpp:616
MemoryLogStruct(int category, int size)
Definition wwmemlog.cpp:609
WWINLINE void Unlock_Mem_Log_Mutex(void)
Definition wwmemlog.cpp:337
WWINLINE void Lock_Mem_Log_Mutex(void)
Definition wwmemlog.cpp:294
const int WWMEMLOG_KEY0
Definition wwmemlog.cpp:596
#define ALLOC_MEMORY(n)
Definition wwmemlog.cpp:61
const int MAX_CATEGORY_STACK_DEPTH
Definition wwmemlog.cpp:153
const int WWMEMLOG_KEY1
Definition wwmemlog.cpp:597
#define FREE_MEMORY(p)
Definition wwmemlog.cpp:62
const int MAX_CATEGORY_STACKS
Definition wwmemlog.cpp:194
WWINLINE void * Get_Mem_Log_Mutex(void)
Definition wwmemlog.cpp:271
@ MEM_COUNT
Definition wwmemlog.h:76
@ MEM_STATICALLOCATION
Definition wwmemlog.h:70
@ MEM_UNKNOWN
Definition wwmemlog.h:58