/* * $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 #endif #if defined(MACOSX) #include #else #include #endif #include #include #include #ifdef WIN32 #include // Non-Linux Includes #include #include #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; iptr == (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