mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 19:55:23 +00:00
1229 lines
27 KiB
C++
1229 lines
27 KiB
C++
/*
|
|
* $Logfile: /DescentIII/Main/mem/mem.cpp $
|
|
* $Revision: 56 $
|
|
* $Date: 1/03/02 5:27p $
|
|
* $Author: Matt $
|
|
*
|
|
* Memory library
|
|
*
|
|
* $Log: /DescentIII/Main/mem/mem.cpp $
|
|
*
|
|
* 56 1/03/02 5:27p Matt
|
|
* Add a longlong cast to prevent math overflow.
|
|
*
|
|
* 55 4/19/00 5:33p Matt
|
|
* From Duane for 1.4
|
|
* Close renderer on malloc fail error exit
|
|
* Mac changes
|
|
*
|
|
* 54 10/21/99 2:25p Kevin
|
|
* Mac merge
|
|
*
|
|
* 53 8/10/99 5:12p Jeff
|
|
* added a debug function to dump the current state of the mem library to
|
|
* file
|
|
*
|
|
* 52 7/28/99 2:22p Kevin
|
|
* Macintosh Stuff
|
|
*
|
|
* 51 5/13/99 5:06p Ardussi
|
|
* changes for compiling on the Mac
|
|
*
|
|
* 50 5/02/99 12:24a Jason
|
|
* mem was incorrectly choosing lowmem mode on 64 meg machines
|
|
*
|
|
* 49 4/30/99 5:07p Kevin
|
|
* misc dedicated server, networking and low memory enhancements
|
|
*
|
|
* 48 4/16/99 10:42p Jeff
|
|
* moved global variables out of window scope code to all builds for linux
|
|
*
|
|
* 47 4/15/99 11:09p Jeff
|
|
* fixed mem_strdup for linux
|
|
*
|
|
* 46 4/15/99 9:22p Jeff
|
|
* Changes for Linux version
|
|
*
|
|
* 45 4/12/99 2:23p Kevin
|
|
* fixed missing symbol
|
|
*
|
|
* 44 4/12/99 1:13p Kevin
|
|
* Moved call to GetLastError and played with some debugging
|
|
*
|
|
* 43 3/17/99 11:19a 3dsmax
|
|
* Took out debugging
|
|
*
|
|
* 42 3/16/99 4:49p Kevin
|
|
* Fixed realloc
|
|
*
|
|
* 41 3/15/99 4:32p Jeff
|
|
* fixed code so mem library compiles correctly
|
|
*
|
|
* 40 3/15/99 11:24a Kevin
|
|
* Improved memory leak detection for mem_strdup and turned on debugging
|
|
*
|
|
* 39 3/14/99 2:31p Kevin
|
|
* Added mem_heapcheck() function for debugging
|
|
*
|
|
* 38 3/02/99 5:50p Kevin
|
|
* Ouch. Duplicate structures existed and were conflicting.
|
|
*
|
|
* 37 2/28/99 6:30p Kevin
|
|
* Added a call to Error() when unable to alloc memory
|
|
*
|
|
* 36 1/09/99 4:40p Jeff
|
|
* added some ifdefs and fixes to get files to compile under Linux
|
|
*
|
|
* 35 1/05/99 3:46p Matt
|
|
* For Kevin, added call to GetLastError()
|
|
*
|
|
* 34 11/05/98 2:00p Sean
|
|
*
|
|
* 33 11/05/98 11:23a Kevin
|
|
* fixed bug that caused it not to work under NT
|
|
*
|
|
* 32 10/30/98 11:23a Samir
|
|
*
|
|
* 31 10/30/98 11:10a Kevin
|
|
* doh! fixed ifdef
|
|
*
|
|
* 30 10/30/98 11:01a Kevin
|
|
* took out mem debug stuff for nt users (temp)
|
|
*
|
|
* 29 10/28/98 5:01p Kevin
|
|
* Improved debugging aids in mem lib
|
|
*
|
|
* 28 10/21/98 7:18p Matt
|
|
* Added quick-exit system to not free individual mem chunks on exit,
|
|
* since the whole heap gets freed at the end.
|
|
*
|
|
* 27 10/18/98 9:11p Matt
|
|
* Took the program name out of some error messages.
|
|
*
|
|
* 26 10/18/98 6:21p Matt
|
|
* Changed a Debug_ConsolePrintf() to mprintf() since the former doesn't
|
|
* exist in a Release build and the latter compiles out.
|
|
*
|
|
* 25 10/17/98 12:46p Kevin
|
|
* Beta 4 fixes
|
|
*
|
|
* 24 10/15/98 12:15p Kevin
|
|
* put in heap checking code
|
|
*
|
|
* 23 10/15/98 11:59a Kevin
|
|
* removed useage of timer_GetTime and FindArg
|
|
*
|
|
* 22 10/14/98 11:25p Jeff
|
|
* commented out call to HeapDestroy() to keep NT users from crashing on
|
|
* exit because it destroys the heap before Sound thread closes
|
|
*
|
|
* 21 10/14/98 7:20p Kevin
|
|
* More dsp changes...
|
|
*
|
|
* 20 10/14/98 4:39p Matt
|
|
* Changed OutrageMessageBox() calls to use either Error() or
|
|
* EditorMessageBox()
|
|
*
|
|
* 19 10/13/98 3:42p Kevin
|
|
* bug fixes
|
|
*
|
|
* 18 10/12/98 8:39p Kevin
|
|
* removed mprintf's and fixed some smallish bugs
|
|
*
|
|
* 17 10/12/98 11:32a Kevin
|
|
* doh! heap size was 160 megs
|
|
*
|
|
* 16 10/12/98 11:25a Kevin
|
|
* doh!
|
|
*
|
|
* 15 10/12/98 10:55a Kevin
|
|
* Don't use memory lib for editor currently
|
|
*
|
|
* 14 10/12/98 10:22a Samir
|
|
* made mem_strdup take a const char pointer.
|
|
*
|
|
* 13 10/09/98 7:40p Kevin
|
|
*
|
|
* 12 10/09/98 6:55p Kevin
|
|
* fized bug with zero byte mallocs and mem_size
|
|
*
|
|
* 11 10/09/98 3:32p Kevin
|
|
* New memory library
|
|
*
|
|
* 10 10/08/98 4:24p Kevin
|
|
* Changed code to comply with memory library usage. Always use mem_malloc
|
|
* , mem_free and mem_strdup
|
|
*
|
|
* 9 10/08/98 2:40p Kevin
|
|
*
|
|
* 8 10/08/98 12:00p Kevin
|
|
* Demo system work
|
|
*
|
|
* 7 9/22/98 3:55p Samir
|
|
* ifdef out stuff for non-debug version.
|
|
*
|
|
* 6 8/31/98 12:42p Samir
|
|
* added some debug code.
|
|
*
|
|
* 5 7/31/98 5:44p Samir
|
|
* improved memory debugging.
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
#ifndef LINUX
|
|
#include <new.h>
|
|
#endif
|
|
#if defined(MACOSX)
|
|
|
|
#include <malloc/malloc.h>
|
|
#else
|
|
#include <malloc.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#ifdef WIN32
|
|
#include <search.h>
|
|
//Non-Linux Includes
|
|
#include <windows.h>
|
|
#include <crtdbg.h>
|
|
#else
|
|
#endif
|
|
|
|
#include "init.h"
|
|
#include "mem.h"
|
|
#include "pserror.h"
|
|
#include "pstypes.h"
|
|
//#include "args.h"
|
|
//#include "ddio.h"
|
|
//
|
|
//#define MEM_DEBUG
|
|
|
|
#ifdef MEM_USE_RTL
|
|
#pragma message("mem.cpp: Compiling For Run-Time Library usage")
|
|
#endif
|
|
|
|
#ifdef MEM_DEBUG
|
|
#pragma message("mem.cpp: Compiling with Debug Settings")
|
|
#endif
|
|
|
|
#ifdef MEM_LOGFILE
|
|
#pragma message("mem.cpp: Compiling with logfile support")
|
|
#endif
|
|
|
|
int Total_mem_used=0;
|
|
int Mem_high_water_mark = 0;
|
|
|
|
#define MEM_NO_MEMORY_PTR 0xdeadbeef
|
|
#define MEM_MAX_MALLOCS 199999
|
|
#define MEM_GAURDIAN_SIG 0x2bad
|
|
#define MEM_MALLOC_TO_SORT 10000
|
|
|
|
typedef struct mem_alloc_info
|
|
{
|
|
int len;
|
|
void * ptr;
|
|
unsigned short line;
|
|
char file[17];
|
|
}mem_alloc_info;
|
|
|
|
static void *Mem_failsafe_block = NULL;;
|
|
bool Mem_low_memory_mode = false;
|
|
bool Mem_superlow_memory_mode = false;
|
|
//If this is set, the mem library ignores mem_free() calls. All the memory is then freed at once oon exit.
|
|
bool Mem_quick_exit = 0;
|
|
#if defined (__LINUX__)
|
|
//Linux memory management
|
|
int LnxTotalMemUsed;
|
|
void mem_shutdown(void)
|
|
{
|
|
}
|
|
void mem_Init(void)
|
|
{
|
|
LnxTotalMemUsed = 0;
|
|
}
|
|
int mem_GetTotalMemoryUsed(void)
|
|
{
|
|
return LnxTotalMemUsed;
|
|
}
|
|
void *mem_malloc_sub(int size,const char *file,int line)
|
|
{
|
|
void *new_mem = malloc(size);
|
|
if(!new_mem){
|
|
mprintf((0,"Out of memory allocating %d bytes: line %d in %s\n",size,line,file));
|
|
Int3();
|
|
return NULL;
|
|
}
|
|
LnxTotalMemUsed += size;
|
|
return new_mem;
|
|
}
|
|
void mem_free_sub(void *memblock)
|
|
{
|
|
if(memblock){
|
|
#if defined(MACOSX)
|
|
LnxTotalMemUsed -= malloc_size(memblock);
|
|
#else
|
|
LnxTotalMemUsed -= malloc_usable_size(memblock);
|
|
#endif
|
|
free(memblock);
|
|
}
|
|
}
|
|
void mem_error_msg(const char *file,int line,int size)
|
|
{
|
|
mprintf((0,"Memory error (size=%d) line %d in %s\n",size,line,file));
|
|
Int3();
|
|
}
|
|
char *mem_strdup_sub(const char *string,char *file,int line)
|
|
{
|
|
char *ret = strdup(string);
|
|
if(!ret)
|
|
{
|
|
mprintf((0,"Out of memory allocating %d bytes: line %d in %s\n",strlen(string)+1,line,file));
|
|
Int3();
|
|
return NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
void *mem_realloc_sub(void *mem,int size)
|
|
{
|
|
return realloc(mem,size);
|
|
}
|
|
int mem_size_sub(void *memblock)
|
|
{
|
|
#if defined(MACOSX)
|
|
return malloc_size(memblock);
|
|
#else
|
|
return malloc_usable_size(memblock);
|
|
#endif
|
|
}
|
|
bool mem_dumpmallocstofile(char *filename)
|
|
{
|
|
return false;
|
|
}
|
|
#pragma mark -
|
|
#elif defined (MACINTOSH)
|
|
#include "ddio.h"
|
|
#include "ddio_mac.h"
|
|
#include "descent.h"
|
|
#include "gamesave.h"
|
|
#include "renderer.h"
|
|
|
|
//Macintosh memory management
|
|
int MacTotalMemUsed;
|
|
int MacDynamicStartMem = 0;
|
|
|
|
#define FAILSAFE_SIZE 2048
|
|
|
|
#ifdef DAJ_DEBUG
|
|
//#define MEM_LOGFILE
|
|
#endif
|
|
#define USE_MALLOC
|
|
|
|
//int Mem_next_slot = 0;
|
|
//mem_alloc_info mem_info[MEM_MAX_MALLOCS];
|
|
FILE * mem_out = NULL;
|
|
|
|
void mem_shutdown(void)
|
|
{
|
|
if (Mem_failsafe_block) {
|
|
mem_free(Mem_failsafe_block);
|
|
Mem_failsafe_block = NULL;
|
|
}
|
|
MacTotalMemUsed = 0;
|
|
// Mem_next_slot = 0;
|
|
}
|
|
void mem_Init(void)
|
|
{
|
|
if(Mem_failsafe_block)
|
|
return;
|
|
|
|
Mem_failsafe_block = malloc(FAILSAFE_SIZE);
|
|
if (!Mem_failsafe_block) {
|
|
Error("No available heap memory.");
|
|
}
|
|
MacDynamicStartMem = (int)Mem_failsafe_block;
|
|
MacTotalMemUsed = 0;
|
|
#ifdef MEM_LOGFILE
|
|
{
|
|
mem_out = fopen("memory.out","wt");
|
|
ASSERT(mem_out);
|
|
}
|
|
#endif
|
|
atexit(mem_shutdown);
|
|
return;
|
|
}
|
|
int mem_GetTotalMemoryUsed(void)
|
|
{
|
|
return MacTotalMemUsed - MacDynamicStartMem;
|
|
}
|
|
|
|
void *mem_malloc_sub(int size,const char *file,int line)
|
|
{
|
|
void *new_mem;
|
|
|
|
if(size <= 0)
|
|
{
|
|
mprintf((0, "Warning: Zero byte malloc in %s line %d!\n", file, line));
|
|
// Int3();
|
|
return (void *)MEM_NO_MEMORY_PTR;
|
|
}
|
|
|
|
#ifdef USE_MALLOC
|
|
new_mem = malloc(size);
|
|
#else
|
|
new_mem = NewPtr(size);
|
|
#endif
|
|
if(!new_mem){
|
|
if(Mem_failsafe_block == NULL) //Been here already!!
|
|
return NULL;
|
|
|
|
mem_free (Mem_failsafe_block);
|
|
Mem_failsafe_block = NULL;
|
|
|
|
#ifdef _DEBUG
|
|
mprintf((0,"Out of memory allocating %d bytes: line %d in %s\n",size,line,file));
|
|
Debugger();
|
|
#else
|
|
rend_Close();
|
|
::ShowCursor();
|
|
|
|
if (GetFunctionMode() == GAME_MODE)
|
|
{
|
|
char pathname[PSPATHNAME_LEN];
|
|
ddio_MakePath(pathname, Base_directory, "savegame", "crash", NULL);
|
|
SaveGameState(pathname, "crash save");
|
|
|
|
::Alert(129, NULL);
|
|
// ::Alert(128, NULL);
|
|
}
|
|
else
|
|
{
|
|
::Alert(129, NULL);
|
|
}
|
|
#endif
|
|
|
|
// ShutdownD3();
|
|
|
|
exit(0);
|
|
// Int3();
|
|
// ExitToShell();
|
|
// return NULL;
|
|
}
|
|
|
|
#ifdef MEM_LOGFILE
|
|
|
|
int mem_addr = (int)new_mem;
|
|
if(mem_addr > MacTotalMemUsed) {
|
|
MacTotalMemUsed = mem_addr;
|
|
fprintf(mem_out, "0x%0X %d %d %s:%d\n", mem_addr,
|
|
MacTotalMemUsed - MacDynamicStartMem, size, file, line);
|
|
}
|
|
#endif
|
|
return new_mem;
|
|
}
|
|
void mem_free_sub(void *memblock)
|
|
{
|
|
if((void *)MEM_NO_MEMORY_PTR == memblock)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(memblock){
|
|
#ifdef USE_MALLOC
|
|
free(memblock);
|
|
#else
|
|
DisposePtr((char *)memblock);
|
|
#endif
|
|
}
|
|
#if 0
|
|
for(int i=0; i<Mem_next_slot; i++) {
|
|
if(memblock == mem_info[i].ptr) {
|
|
fprintf(mem_out, "free %d %s:%d\n", mem_info[i].len, mem_info[i].file, mem_info[i].line);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
void mem_error_msg(const char *file,int line,int size)
|
|
{
|
|
mprintf((0,"Memory error (size=%d) line %d in %s\n",size,line,file));
|
|
Int3();
|
|
}
|
|
char *mem_strdup_sub(const char *string,char *file,int line)
|
|
{
|
|
int len = strlen(string)+1;
|
|
char * ret = (char *)mem_malloc(len);
|
|
|
|
strcpy(ret,string);
|
|
return ret;
|
|
}
|
|
void *mem_realloc_sub(void *mem,int size)
|
|
{
|
|
void *new_mem;
|
|
|
|
if(size <= 0)
|
|
{
|
|
// Int3();
|
|
return (void *)MEM_NO_MEMORY_PTR;
|
|
}
|
|
#ifdef USE_MALLOC
|
|
new_mem = realloc(mem,size);
|
|
#else
|
|
SetPtrSize((char*)mem, size);
|
|
new_mem = mem;
|
|
#endif
|
|
if(!new_mem){
|
|
if(Mem_failsafe_block == NULL) //Been here already!!
|
|
return NULL;
|
|
|
|
mem_free (Mem_failsafe_block);
|
|
Mem_failsafe_block = NULL;
|
|
|
|
#ifdef _DEBUG
|
|
mprintf((0,"Out of memory allocating %d bytes\n",size));
|
|
Debugger();
|
|
#else
|
|
::ShowCursor();
|
|
|
|
if (GetFunctionMode() == GAME_MODE)
|
|
{
|
|
char pathname[PSPATHNAME_LEN];
|
|
ddio_MakePath(pathname, Base_directory, "savegame", "crash", NULL);
|
|
SaveGameState(pathname, "crash save");
|
|
|
|
::Alert(128, NULL);
|
|
}
|
|
else
|
|
{
|
|
::Alert(129, NULL);
|
|
}
|
|
#endif
|
|
// ShutdownD3();
|
|
|
|
exit(0);
|
|
// Int3();
|
|
// ExitToShell();
|
|
// return NULL;
|
|
}
|
|
return new_mem;
|
|
}
|
|
int mem_size_sub(void *memblock)
|
|
{
|
|
return 0;
|
|
}
|
|
bool mem_dumpmallocstofile(char *filename)
|
|
{
|
|
return false;
|
|
}
|
|
#pragma mark -
|
|
#else // defined (WIN32)
|
|
//Windows memory management
|
|
|
|
// Uncomment this to detect memory leaks and memory overwrites. Slows down mallocs and frees a little.
|
|
//#define MEM_DEBUG 1
|
|
//Uncomment this if you want everything written to "memory.out"
|
|
//#define MEM_LOGFILE 1
|
|
|
|
#ifdef MEM_USE_RTL
|
|
#undef MEM_DEBUG
|
|
#endif
|
|
|
|
class MemClass
|
|
{
|
|
public:
|
|
~MemClass();
|
|
};
|
|
class MemClass Mymem;
|
|
/* modify these lines to establish data type */
|
|
typedef mem_alloc_info * T; /* type of item to be stored */
|
|
typedef int hashTableIndex; /* index into hash table */
|
|
#define compEQ(a,b) ((a)->ptr == (b)->ptr)
|
|
typedef struct Node_ {
|
|
struct Node_ *next; /* next node */
|
|
T data; /* data stored in node */
|
|
} Node;
|
|
Node *findNode (T data);
|
|
void deleteNode(T data);
|
|
Node *insertNode(T data);
|
|
hashTableIndex hash(T data);
|
|
Node **hashTable;
|
|
int hashTableSize = MEM_MAX_MALLOCS;
|
|
#ifdef MEM_DEBUG
|
|
mem_alloc_info mem_info[MEM_MAX_MALLOCS];
|
|
int Mem_next_slot = 0;
|
|
int Mem_highest_used_slot = 0;
|
|
FILE * mem_out = NULL;
|
|
int Mem_mallocs_since_last_sort = 0;
|
|
/*
|
|
#undef new
|
|
void *operator new(unsigned int size, char *file, int line)
|
|
{
|
|
return mem_malloc_sub(size, file, line);
|
|
}
|
|
void *operator new [](unsigned int size, char *file, int line)
|
|
{
|
|
return mem_malloc_sub(size, file, line);
|
|
}
|
|
void *operator new(unsigned int size)
|
|
{
|
|
return mem_malloc_sub(size,"unknown",0);
|
|
}
|
|
void operator delete(void *ptr)
|
|
{
|
|
mem_free_sub(ptr);
|
|
}
|
|
*/
|
|
#endif
|
|
int handle_program_memory_depletion( size_t size );
|
|
HANDLE Heap;
|
|
void mem_Init()
|
|
{
|
|
#if defined(WIN32) || defined(__LINUX__)
|
|
// allocate failsafe block for memory used by any functions after we run out of memory.
|
|
// (printf for instance needs heap memory, as well as our error library.)
|
|
MEMORYSTATUS ms;
|
|
#ifdef MEM_DEBUG
|
|
hashTable = (Node **)malloc(hashTableSize * sizeof(Node *));
|
|
for(int a=0;a<hashTableSize;a++)
|
|
{
|
|
hashTable[a] = NULL;
|
|
}
|
|
#ifdef MEM_LOGFILE
|
|
{
|
|
mem_out = fopen("memory.out","wt");
|
|
ASSERT(mem_out);
|
|
}
|
|
#endif
|
|
for(int i=0;i<MEM_MAX_MALLOCS;i++)
|
|
{
|
|
mem_info[i].ptr = (void *)MEM_NO_MEMORY_PTR;
|
|
}
|
|
#endif
|
|
|
|
GlobalMemoryStatus(&ms);
|
|
Heap = HeapCreate(HEAP_NO_SERIALIZE,16000000,0);//GetProcessHeap();
|
|
if(!Heap)
|
|
{
|
|
mprintf((0,"Unable to create memory heap! error: %d\n",GetLastError()));
|
|
Error("Unable to create memory heap; your system may not have enough memory to run.");
|
|
}
|
|
ASSERT(Heap);
|
|
HeapCompact(Heap,0);
|
|
mprintf((0,"System Memory Status:\n"));
|
|
mprintf((0,"Percent of memory in use: %d\n",ms.dwMemoryLoad));
|
|
mprintf((0,"Bytes of physical memory : %d\n",ms.dwTotalPhys));
|
|
mprintf((0,"Free physical memory bytes : %d\n",ms.dwAvailPhys));
|
|
mprintf((0,"Available virtual memory : %d\n",ms.dwAvailPageFile));
|
|
|
|
//See if there is enough memory to run
|
|
if(((longlong) ms.dwAvailPageFile+ms.dwAvailPhys)<(50*1024*1024))
|
|
{
|
|
Error("Your system doesn't have enough available memory to continue.\r\n\r\nMemory Statistics:\r\nTotal Physical: %d\r\nAvaliable Physical: %d\r\nAvailable Virtual: %d\r\n\r\nYou may be able to continue by rebooting, or freeing up some disk space.",
|
|
ms.dwTotalPhys,ms.dwAvailPhys,ms.dwAvailPageFile);
|
|
return;
|
|
}
|
|
else if((ms.dwTotalPhys)<(62*1024*1024))
|
|
{
|
|
mprintf((0,"Using low memory mode!\n"));
|
|
Mem_low_memory_mode = true;
|
|
return;
|
|
}
|
|
else if((ms.dwTotalPhys)<(46*1024*1024))
|
|
{
|
|
mprintf((0,"Using super low memory mode!\n"));
|
|
Mem_low_memory_mode = true;
|
|
Mem_superlow_memory_mode = true;
|
|
return;
|
|
}
|
|
#else
|
|
#ifdef MEM_DEBUG
|
|
hashTable = (Node **)malloc(hashTableSize * sizeof(Node *));
|
|
for(int a=0;a<hashTableSize;a++)
|
|
{
|
|
hashTable[a] = NULL;
|
|
}
|
|
#ifdef MEM_LOGFILE
|
|
{
|
|
mem_out = fopen("memory.out","wt");
|
|
ASSERT(mem_out);
|
|
}
|
|
#endif
|
|
for(int i=0;i<MEM_MAX_MALLOCS;i++)
|
|
{
|
|
mem_info[i].ptr = (void *)MEM_NO_MEMORY_PTR;
|
|
}
|
|
#endif
|
|
Heap = NULL;
|
|
|
|
Mem_failsafe_block = mem_malloc(128);
|
|
if (!Mem_failsafe_block) {
|
|
Error("No available heap memory.");
|
|
}
|
|
return;
|
|
#endif
|
|
#if defined(WIN32) || defined(__LINUX__)
|
|
Mem_failsafe_block = mem_malloc(128);
|
|
if (!Mem_failsafe_block) {
|
|
Error("No available heap memory.");
|
|
}
|
|
_set_new_handler(handle_program_memory_depletion);
|
|
int flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
|
|
#ifdef _DEBUG
|
|
flags |= _CRTDBG_ALLOC_MEM_DF;
|
|
#endif
|
|
_CrtSetDbgFlag(flags);
|
|
//atexit(mem_Free);
|
|
#endif
|
|
}
|
|
// Returns the number of dynamically allocated bytes
|
|
int mem_GetTotalMemoryUsed ()
|
|
{
|
|
return Total_mem_used;
|
|
}
|
|
bool bail_now = false;
|
|
// Allocates a block of memory and returns a pointer to it
|
|
void *mem_malloc_sub (int size, const char *file, int line)
|
|
{
|
|
//float mstart = timer_GetTime();
|
|
void *retp;
|
|
#ifdef MEM_USE_RTL
|
|
retp = malloc(size);
|
|
return retp;
|
|
#else
|
|
#if defined(WIN32)
|
|
if(!Heap)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
if(size < 0)
|
|
{
|
|
mprintf((0,"Some bozo is trying to allocate a negative length of memory!\n"));
|
|
mprintf((0,"Offending file: %s line: %d\n",file, line));
|
|
Int3();
|
|
return NULL;
|
|
}
|
|
else if(size == 0)
|
|
{
|
|
mprintf((0, "Warning: Zero byte malloc in %s line %d!\n", file, line));
|
|
Int3();
|
|
return (void *)MEM_NO_MEMORY_PTR;
|
|
}
|
|
mem_alloc_info * mi = NULL;
|
|
mem_alloc_info no_track_mi;
|
|
bool track_node = true;
|
|
#ifdef MEM_DEBUG
|
|
if(!bail_now)
|
|
{
|
|
if(mem_info[Mem_next_slot].ptr==(void *)MEM_NO_MEMORY_PTR)
|
|
{
|
|
mi = &mem_info[Mem_next_slot];
|
|
Mem_next_slot++;
|
|
if(Mem_next_slot>Mem_highest_used_slot)
|
|
Mem_highest_used_slot = Mem_next_slot;
|
|
}
|
|
else
|
|
{
|
|
for(int i=0;i<MEM_MAX_MALLOCS;i++)
|
|
{
|
|
if(mem_info[i].ptr==(void *)MEM_NO_MEMORY_PTR)
|
|
{
|
|
mi = &mem_info[i];
|
|
Mem_next_slot = i+1;
|
|
if(i>Mem_highest_used_slot)
|
|
Mem_highest_used_slot = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
if(bail_now||(mi==NULL))
|
|
#else
|
|
|
|
#endif
|
|
{
|
|
|
|
mi = &no_track_mi;
|
|
if(bail_now==false)
|
|
{
|
|
track_node = false;
|
|
#ifdef MEM_DEBUG
|
|
mprintf((0,"Out of memory tracking slots!!!!\n"));
|
|
#endif
|
|
}
|
|
bail_now = true;
|
|
|
|
}
|
|
#ifndef MEM_DEBUG
|
|
mi->ptr = HeapAlloc(Heap,HEAP_NO_SERIALIZE,size);
|
|
#else
|
|
mi->ptr = HeapAlloc(Heap,HEAP_NO_SERIALIZE,size+2);
|
|
mi->len = size;
|
|
unsigned short mem_sig = MEM_GAURDIAN_SIG;
|
|
memcpy(((char *)mi->ptr)+size,(void *)&mem_sig,2);
|
|
int flen = strlen(file);
|
|
int lofs = 0;
|
|
//Strip down the filename to be < 16 bytes
|
|
if(flen > 16)
|
|
lofs = flen-16;
|
|
|
|
strcpy(mi->file,file+lofs);
|
|
mi->line = line;
|
|
#ifdef MEM_LOGFILE
|
|
fprintf(mem_out,"%s,%d,%d,%d\n",mi->file,mi->line,size,HeapSize(Heap,0,mi->ptr));
|
|
#endif
|
|
#endif
|
|
int errors;
|
|
if(mi->ptr==NULL)
|
|
{
|
|
#ifndef MACINTOSH
|
|
errors = GetLastError();
|
|
#endif
|
|
mprintf((0,"Unable to alloc memory in mem_malloc_sub()!\n"));
|
|
Int3();
|
|
ASSERT(mi->ptr);
|
|
Error("Out of memory, unable to continue.");
|
|
mem_error_msg(file, line, size);
|
|
return 0;
|
|
}
|
|
|
|
retp = mi->ptr;
|
|
|
|
Total_mem_used+=size;
|
|
if(Mem_high_water_mark<Total_mem_used)
|
|
{
|
|
Mem_high_water_mark = Total_mem_used;
|
|
}
|
|
#ifdef MEM_DEBUG
|
|
if(track_node)
|
|
insertNode(mi);
|
|
#endif
|
|
return retp;
|
|
#endif
|
|
}
|
|
// Frees a previously allocated block of memory
|
|
void mem_free_sub (void *memblock)
|
|
{
|
|
#ifndef MEM_DEBUG
|
|
if (Mem_quick_exit)
|
|
return;
|
|
#endif
|
|
#ifdef MEM_USE_RTL
|
|
free(memblock);
|
|
return;
|
|
#else
|
|
#if defined(WIN32)
|
|
if(!Heap)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
if (memblock==0)
|
|
return;
|
|
if((void *)MEM_NO_MEMORY_PTR == memblock)
|
|
{
|
|
return;
|
|
}
|
|
#ifdef MEM_DEBUG
|
|
mem_alloc_info findmem;
|
|
mem_alloc_info *freemem;
|
|
findmem.ptr = memblock;
|
|
Node * mynode = findNode(&findmem);
|
|
if(mynode)
|
|
{
|
|
freemem = mynode->data;
|
|
freemem->ptr=(void *)MEM_NO_MEMORY_PTR;
|
|
unsigned short mem_sig = MEM_GAURDIAN_SIG;
|
|
if(memcmp((char *)memblock+freemem->len,&mem_sig,2)!=0)
|
|
{
|
|
// Corrupted memory found when we went to free it.
|
|
mprintf((0,"Memory block found to be damaged when it was freed!\n"));
|
|
Int3();
|
|
}
|
|
Total_mem_used-=freemem->len;
|
|
HeapFree(Heap,HEAP_NO_SERIALIZE,memblock);
|
|
deleteNode(mynode->data);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
mprintf((0,"Warning, hash lookup of memory block failed!\n"));
|
|
HeapFree(Heap,HEAP_NO_SERIALIZE,memblock);
|
|
return;
|
|
}
|
|
#endif
|
|
HeapFree(Heap,HEAP_NO_SERIALIZE,memblock);
|
|
#endif
|
|
}
|
|
int handle_program_memory_depletion( size_t size )
|
|
{
|
|
// Release character buffer memory.
|
|
mem_free (Mem_failsafe_block);
|
|
Mem_failsafe_block = NULL;
|
|
Error("Unable to allocate %d bytes of memory.", size);
|
|
// Tell new to stop allocation attempts.
|
|
return 0;
|
|
}
|
|
// prints out a memory error message
|
|
void mem_error_msg(const char *file, int line, int size)
|
|
{
|
|
Error ("Attempt to allocate %d bytes of memory in %s line %d failed.",size, file, line);
|
|
}
|
|
//int strdup_malloc_line = 0;
|
|
char * mem_strdup_sub(const char *src,char *file,int line)
|
|
{
|
|
#if defined(WIN32)
|
|
if(!Heap)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
int len = strlen(src)+1;
|
|
//strdup_malloc_line = __LINE__+1;
|
|
//Leave this line here!
|
|
char * dest = (char *)mem_malloc_sub(len,file,line);
|
|
strcpy(dest,src);
|
|
return dest;
|
|
}
|
|
void * mem_realloc_sub(void * memblock,int size)
|
|
{
|
|
#ifdef MEM_USE_RTL
|
|
void *retp = realloc(memblock,size);
|
|
return retp;
|
|
#else
|
|
#if defined(WIN32)
|
|
if(!Heap)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
if((void *)MEM_NO_MEMORY_PTR == memblock)
|
|
{
|
|
return mem_malloc(size);
|
|
}
|
|
#ifdef MEM_DEBUG
|
|
for(int i=0;i<MEM_MAX_MALLOCS;i++)
|
|
{
|
|
if(mem_info[i].ptr==(void *)memblock)
|
|
{
|
|
mem_info[i].ptr=(void *)MEM_NO_MEMORY_PTR;
|
|
Total_mem_used-=mem_info[i].len;
|
|
|
|
deleteNode(&mem_info[i]);
|
|
|
|
mem_info[i].ptr = HeapReAlloc(Heap,0,memblock,size+2);
|
|
unsigned short mem_sig = MEM_GAURDIAN_SIG;
|
|
memcpy(((char *)mem_info[i].ptr)+size,(void *)&mem_sig,2);
|
|
mem_info[i].len = size;
|
|
Total_mem_used += size;
|
|
|
|
insertNode(&mem_info[i]);
|
|
|
|
return mem_info[i].ptr;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef MACINTOSH
|
|
HeapFree(Heap, HEAP_NO_SERIALIZE, memblock);
|
|
void *retp = HeapAlloc(Heap, HEAP_NO_SERIALIZE, size);
|
|
#else
|
|
void *retp = HeapReAlloc(Heap,0,memblock,size);
|
|
#endif
|
|
return retp;
|
|
#endif
|
|
}
|
|
int mem_size_sub(void *memblock)
|
|
{
|
|
#if defined(WIN32)
|
|
if(!Heap)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
if((void *)MEM_NO_MEMORY_PTR == memblock)
|
|
{
|
|
return 0;
|
|
}
|
|
return HeapSize(Heap,0,memblock);
|
|
}
|
|
void mem_shutdown();
|
|
MemClass::~MemClass()
|
|
{
|
|
mem_shutdown();
|
|
}
|
|
// memory routines
|
|
void mem_shutdown()
|
|
{
|
|
// free failsafe memory block.
|
|
mprintf((0,"Shutting down memory system.\n"));
|
|
if (Mem_failsafe_block) {
|
|
mem_free(Mem_failsafe_block);
|
|
Mem_failsafe_block = NULL;
|
|
}
|
|
#ifdef MEM_DEBUG
|
|
free(hashTable);
|
|
if (Total_mem_used)
|
|
mprintf((0, "%d bytes leaked in mem_malloc heap!\n", Total_mem_used));
|
|
#ifdef MEM_LOGFILE
|
|
fclose(mem_out);
|
|
#endif
|
|
mprintf((0,"Looking for memory leaks.\n"));
|
|
for(int i=0;i<MEM_MAX_MALLOCS;i++)
|
|
{
|
|
if(mem_info[i].ptr!=(void *)MEM_NO_MEMORY_PTR)
|
|
{
|
|
mprintf((0,"Memory leaked from %s line %d length %d.\n",mem_info[i].file,mem_info[i].line,mem_info[i].len));
|
|
/*
|
|
//mprintf((0,"%d\n",strdup_malloc_line));
|
|
if( (strcmp(mem_info[i].file,"main\\mem\\mem.cpp")==0) && (mem_info[i].line==(strdup_malloc_line+1)) )
|
|
{
|
|
|
|
mprintf((0,"Memory leak from mem_strdup() (%s)\n",mem_info[i].ptr));
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
mprintf((0,"Done looking for memory leaks.\n"));
|
|
mprintf((0,"Memory library high water mark: %d\n",Mem_high_water_mark));
|
|
Mem_next_slot = 0;
|
|
#endif
|
|
if(Heap)
|
|
{
|
|
HeapDestroy(Heap);
|
|
Heap = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef MEM_DEBUG
|
|
//accessory stuff for mem_dumpallocstofile()
|
|
typedef struct allocdumpt
|
|
{
|
|
mem_alloc_info *data;
|
|
}allocdumpt;
|
|
|
|
static int *sorted_allocs = NULL;
|
|
|
|
int mem_allocdump_cmp(allocdumpt *elem1,allocdumpt *elem2)
|
|
{
|
|
int res;
|
|
res = strcmp(elem1->data->file,elem2->data->file);
|
|
if(res!=0)
|
|
return (res<0)?-1:1;
|
|
|
|
if(elem1->data->line<elem2->data->line)
|
|
return -1;
|
|
else if(elem1->data->line>elem2->data->line)
|
|
return 1;
|
|
|
|
if(elem1->data->len<elem2->data->len)
|
|
return -1;
|
|
else if(elem1->data->len>elem2->data->len)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
bool mem_dumpmallocstofile(char *filename)
|
|
{
|
|
#ifdef MEM_DEBUG
|
|
FILE *file;
|
|
file = fopen(filename,"wt");
|
|
if(!file)
|
|
return false;
|
|
|
|
int h_table_idx,num_allocs=0;
|
|
mem_alloc_info *alloc_info;
|
|
char buffer[384];
|
|
|
|
for(h_table_idx=0;h_table_idx<MEM_MAX_MALLOCS;h_table_idx++)
|
|
{
|
|
if(mem_info[h_table_idx].ptr!=(void *)MEM_NO_MEMORY_PTR)
|
|
{
|
|
num_allocs++;
|
|
}
|
|
}
|
|
|
|
allocdumpt *allocs;
|
|
int curr_idx,i;
|
|
allocs = (allocdumpt *)malloc(sizeof(allocdumpt)*num_allocs);
|
|
sorted_allocs = (int *)malloc(sizeof(int)*num_allocs);
|
|
curr_idx = 0;
|
|
|
|
for(h_table_idx=0;h_table_idx<MEM_MAX_MALLOCS;h_table_idx++)
|
|
{
|
|
if(mem_info[h_table_idx].ptr!=(void *)MEM_NO_MEMORY_PTR)
|
|
{
|
|
allocs[curr_idx++].data = &mem_info[h_table_idx];
|
|
}
|
|
}
|
|
|
|
// sort the list of allocs for easier reading
|
|
for(i=0;i<num_allocs;i++)
|
|
sorted_allocs[i] = i;
|
|
qsort(allocs,num_allocs,sizeof(allocdumpt),(int (*)(const void *,const void *))mem_allocdump_cmp);
|
|
|
|
// dump it out
|
|
int last_unique;
|
|
last_unique = -1;
|
|
|
|
for(i=0;i<num_allocs;i++)
|
|
{
|
|
curr_idx = sorted_allocs[i];
|
|
alloc_info = (mem_alloc_info *)allocs[curr_idx].data;
|
|
|
|
if(last_unique!=-1)
|
|
{
|
|
//see if we are just repeating
|
|
mem_alloc_info *last_alloc;
|
|
last_alloc = (mem_alloc_info *)allocs[sorted_allocs[last_unique]].data;
|
|
if( (!strcmp(last_alloc->file,alloc_info->file)) &&
|
|
(last_alloc->line==alloc_info->line) )
|
|
{
|
|
//its the same!
|
|
continue;
|
|
}
|
|
}
|
|
|
|
last_unique = i;
|
|
|
|
if(i<num_allocs-1)
|
|
{
|
|
//see if we have multiple of the same line
|
|
mem_alloc_info *next_alloc;
|
|
next_alloc = (mem_alloc_info *)allocs[sorted_allocs[i+1]].data;
|
|
if( (!strcmp(next_alloc->file,alloc_info->file)) &&
|
|
(next_alloc->line==alloc_info->line) )
|
|
{
|
|
// we have multiple of the same line!
|
|
// see how many we have of this line
|
|
int repeat_count = 1;
|
|
int total_size = alloc_info->len;
|
|
i++;
|
|
while(i<num_allocs)
|
|
{
|
|
next_alloc = (mem_alloc_info *)allocs[sorted_allocs[i]].data;
|
|
if( (!strcmp(next_alloc->file,alloc_info->file)) &&
|
|
(next_alloc->line==alloc_info->line) )
|
|
{
|
|
repeat_count++;
|
|
total_size += next_alloc->len;
|
|
}else
|
|
{
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
sprintf(buffer,"\n--Repeated %d Times--\n",repeat_count);
|
|
fputs(buffer,file);
|
|
|
|
sprintf(buffer,"*[%d bytes total]\tFile %s Line %d\n",total_size,alloc_info->file,alloc_info->line);
|
|
fputs(buffer,file);
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
sprintf(buffer,"[%d bytes]\tFile %s Line %d\n",alloc_info->len,alloc_info->file,alloc_info->line);
|
|
fputs(buffer,file);
|
|
}
|
|
|
|
free(sorted_allocs);
|
|
free(allocs);
|
|
|
|
fclose(file);
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
void mem_heapcheck(void)
|
|
{
|
|
#ifdef MEM_DEBUG
|
|
for(int i=0;i<MEM_MAX_MALLOCS;i++)
|
|
{
|
|
mem_alloc_info *freemem;
|
|
if(mem_info[i].ptr==(void *)MEM_NO_MEMORY_PTR)
|
|
continue;
|
|
freemem = &mem_info[i];
|
|
unsigned short mem_sig = MEM_GAURDIAN_SIG;
|
|
if(memcmp((char *)freemem->ptr+freemem->len,&mem_sig,2)!=0)
|
|
{
|
|
mprintf((0,"Memory block found to be damaged in mem_heapcheck()!\n"));
|
|
mprintf((0,"Originally allocated from file %s, line %d\n",freemem->file,freemem->line));
|
|
Int3();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
hashTableIndex hash(T data) {
|
|
/***********************************
|
|
* hash function applied to data *
|
|
***********************************/
|
|
unsigned int hval = (unsigned int)data->ptr;
|
|
return (hval % MEM_MAX_MALLOCS);
|
|
}
|
|
Node *insertNode(T data)
|
|
{
|
|
Node *p, *p0;
|
|
hashTableIndex bucket;
|
|
/************************************************
|
|
* allocate node for data and insert in table *
|
|
************************************************/
|
|
/* insert node at beginning of list */
|
|
bucket = hash(data);
|
|
if ((p = (Node *)malloc(sizeof(Node))) == 0) {
|
|
fprintf (stderr, "out of memory (insertNode)\n");
|
|
exit(1);
|
|
}
|
|
p0 = hashTable[bucket];
|
|
hashTable[bucket] = p;
|
|
p->next = p0;
|
|
p->data = data;
|
|
return p;
|
|
}
|
|
void deleteNode(T data)
|
|
{
|
|
Node *p0, *p;
|
|
hashTableIndex bucket;
|
|
/********************************************
|
|
* delete node containing data from table *
|
|
********************************************/
|
|
/* find node */
|
|
p0 = 0;
|
|
bucket = hash(data);
|
|
p = hashTable[bucket];
|
|
while (p && !compEQ(p->data, data)) {
|
|
p0 = p;
|
|
p = p->next;
|
|
}
|
|
if (!p) return;
|
|
/* p designates node to delete, remove it from list */
|
|
if (p0)
|
|
/* not first node, p0 points to previous node */
|
|
p0->next = p->next;
|
|
else
|
|
/* first node on chain */
|
|
hashTable[bucket] = p->next;
|
|
free (p);
|
|
}
|
|
Node *findNode (T data)
|
|
{
|
|
Node *p;
|
|
/*******************************
|
|
* find node containing data *
|
|
*******************************/
|
|
p = hashTable[hash(data)];
|
|
while (p && !compEQ(p->data, data))
|
|
p = p->next;
|
|
return p;
|
|
}
|
|
#endif
|
|
|