/*************************************************************************** * God Wars Mud copyright (C) 1994, 1995, 1996 by Richard Woolcock * * * * Legend of Chrystancia copyright (C) 1999, 2000, 2001 by Matthew Little * * This mud is NOT to be copied in whole or in part, or to be run without * * the permission of Matthew Little. Nobody else has permission to * * authorise the use of this code. * ***************************************************************************/ /* * Matthew Chaplain's Memory Leak Tracer * Copyright (c) 2000-2001, All Rights Reserved */ /* Library includes */ #include <stdlib.h> #include <string.h> #include <stdio.h> /* Local includes */ #include "mem.h" /* Defined constants */ /* * Since in most compilers, memory is allocated on a * 4-byte boundary, I've chosen a nice prime number for * the hash size so that it isn't all clumped into four * or so buckets. * * This can be redefined on the command line if you want * it to be bigger. * * Obviously, the larger the project, the larger you want * this number to be, for efficiency. */ #ifndef MEMORY_HASH #define MEMORY_HASH 31 #endif /* Local type definitions */ typedef struct _mc_memory_t mc_memory_t; struct _mc_memory_t { mc_memory_t *pNext; int nAmountAllocated; const char *szFileName; int nLineNumber; void *pvMemory; }; /* Local functions */ static int report_corruption (void *pvMemory); /* Local variables */ static mc_memory_t *paMemoryHash[MEMORY_HASH]; /* * This must be called before any allocation is done, or * deallocation becomes undefined. */ void mc_memory_init (void) { static short bInitialised = 0; int nIndex; if (!bInitialised) { #ifdef MC_MEM_INTRO printf ("Matthew Chaplain's Memory Tracer\n" "Copyright (c) 2000-2001 Matthew Chaplain All Rights " "Reserved.\n\nOptions set:\n MEMORY_HASH: %d\n\n", MEMORY_HASH); #endif for (nIndex = 0; nIndex < MEMORY_HASH; ++nIndex) { paMemoryHash[nIndex] = NULL; } #ifndef NOREPORTATEXIT atexit (mc_memory_done); #endif bInitialised = 1; } } /* * The malloc replacement function. Allocates memory, stores a structure * describing it in the hash table, and returns the memory */ void *mc_memory_malloc (size_t nSize, const char *szFileName, int nLineNumber) { mc_memory_t *pNewMemory; int nHash; /* allocate a new memory structure */ pNewMemory = malloc (sizeof (*pNewMemory)); pNewMemory->nAmountAllocated = nSize; pNewMemory->szFileName = szFileName; pNewMemory->nLineNumber = nLineNumber; pNewMemory->pvMemory = malloc (nSize); /* use its actual memory address as a hashing code */ nHash = ((int) pNewMemory->pvMemory) % MEMORY_HASH; /* insert the memory structure into the lookup */ pNewMemory->pNext = paMemoryHash[nHash]; paMemoryHash[nHash] = pNewMemory; /* return the actual memory address */ return pNewMemory->pvMemory; } /* * The calloc replacement function. Allocates memory, stores a structure * describing it in the hash table, and returns the memory */ void *mc_memory_calloc (size_t nElements, size_t nSize, const char *szFileName, int nLineNumber) { mc_memory_t *pNewMemory; int nHash; /* allocate a new memory structure */ pNewMemory = malloc (sizeof (*pNewMemory)); pNewMemory->nAmountAllocated = nSize * nElements; pNewMemory->szFileName = szFileName; pNewMemory->nLineNumber = nLineNumber; pNewMemory->pvMemory = calloc (nElements, nSize); /* use its actual memory address as a hashing code */ nHash = ((int) pNewMemory->pvMemory) % MEMORY_HASH; /* insert the memory structure into the lookup */ pNewMemory->pNext = paMemoryHash[nHash]; paMemoryHash[nHash] = pNewMemory; /* return the actual memory address */ return pNewMemory->pvMemory; } /* * The realloc replacement function. Allocates memory, stores a structure * describing it in the hash table, and returns the memory */ void *mc_memory_realloc (void *pvMemory, size_t nSize, const char *szFileName, int nLineNumber) { /* Get hash of memory block */ int nHash = (int) pvMemory % MEMORY_HASH; void *pvReallocMemory = NULL; if (!pvMemory) { pvReallocMemory = mc_memory_malloc (nSize, szFileName, nLineNumber); } else if (!paMemoryHash[nHash]) { /* A memory corruption has taken place */ } else if (paMemoryHash[nHash]->pvMemory == pvMemory) { /* Was the first block */ mc_memory_t *pMemory = paMemoryHash[nHash]; /* Unlink node */ paMemoryHash[nHash] = paMemoryHash[nHash]->pNext; /* Rewrite the structure to reflect new data */ pMemory->nAmountAllocated = nSize; pMemory->szFileName = szFileName; pMemory->nLineNumber = nLineNumber; pMemory->pvMemory = realloc (pvMemory, nSize); pvReallocMemory = pMemory->pvMemory; /* Reinsert structure */ nHash = (int) pMemory->pvMemory % MEMORY_HASH; pMemory->pNext = paMemoryHash[nHash]; paMemoryHash[nHash] = pMemory; } else { /* Search for block */ mc_memory_t *pPrevMemory; mc_memory_t *pCurrentMemory; short bFound = 0; pPrevMemory = paMemoryHash[nHash]; for (pCurrentMemory = pPrevMemory->pNext; pCurrentMemory && !bFound; pPrevMemory = pPrevMemory->pNext, pCurrentMemory = pPrevMemory->pNext) { if (pCurrentMemory->pvMemory == pvMemory) { /* Unlink block */ pPrevMemory->pNext = pCurrentMemory->pNext; /* Rewrite the structure to reflect new data */ pCurrentMemory->nAmountAllocated = nSize; pCurrentMemory->szFileName = szFileName; pCurrentMemory->nLineNumber = nLineNumber; pCurrentMemory->pvMemory = realloc (pvMemory, nSize); pvReallocMemory = pCurrentMemory->pvMemory; /* Reinsert structure */ nHash = (int) pCurrentMemory->pvMemory % MEMORY_HASH; pCurrentMemory->pNext = paMemoryHash[nHash]; paMemoryHash[nHash] = pCurrentMemory; bFound = 1; } } } if (!pvReallocMemory) { fprintf (stderr, "Memory corruption : %p at %s:%d\n\r", pvMemory, szFileName, nLineNumber); report_corruption (pvMemory); pvReallocMemory = mc_memory_malloc (nSize, szFileName, nLineNumber); } return pvReallocMemory; } /* * Frees up memory and takes it out of the hash table. */ void mc_memory_free (void *pvMemory, const char *szFileName, int nLineNumber) { mc_memory_t *pCurrentMemory; int nHash; int bFound; /* obtain the hash value of the memory */ nHash = ((int) pvMemory) % MEMORY_HASH; /* start in that bucket */ pCurrentMemory = paMemoryHash[nHash]; if (pCurrentMemory == NULL) { /* Memory Corruption */ bFound = 0; } else if (pCurrentMemory->pvMemory == pvMemory) { /* The freed memory was at the top of the bucket */ paMemoryHash[nHash] = pCurrentMemory->pNext; free (pCurrentMemory->pvMemory); free (pCurrentMemory); bFound = 1; } else { /* The freed memory is /somewhere/ in this bucket, hopefully * iterate through the bucket to find it and remove it */ mc_memory_t *pNextMemory; bFound = 0; for (pNextMemory = pCurrentMemory->pNext; pNextMemory && !bFound; pNextMemory = pNextMemory->pNext, pCurrentMemory = pCurrentMemory->pNext) { if (pNextMemory->pvMemory == pvMemory) { pCurrentMemory->pNext = pNextMemory->pNext; free (pNextMemory->pvMemory); free (pNextMemory); bFound = 1; } } } if (!bFound) { /* Nothing was freed, so a memory corruption has occurred. */ fprintf (stderr, "Memory corruption : %p at %s:%d\n\r", pvMemory, szFileName, nLineNumber); report_corruption (pvMemory); } } /* * This will report on your memory leaks. * Unless compiled with "-DNOREPORTATEXIT", this will * automatically run upon normal exit of the program. * * However, it does no cleanup of its own, so you * can call it whenever you like to reveal current memory usage. */ void mc_memory_done (void) { int nHash; int nTotal; mc_memory_t *pCurrentMemory; nTotal = 0; for (nHash = 0; nHash < MEMORY_HASH; nHash++) { pCurrentMemory = paMemoryHash[nHash]; while (pCurrentMemory) { fprintf (stderr, "Memory leak : %p in bucket %d at %s:%d, %d bytes\n\r", pCurrentMemory->pvMemory, nHash, pCurrentMemory->szFileName, pCurrentMemory->nLineNumber, pCurrentMemory->nAmountAllocated); nTotal += pCurrentMemory->nAmountAllocated; pCurrentMemory = pCurrentMemory->pNext; } } if (nTotal > 0) { printf ("Total memory leaked: %d bytes\n", nTotal); } } /* * Reports a corruption and the place it occurred. * Returns the size of the corruption. */ int report_corruption (void *pvMemory) { int nHash; int nSize = 0; mc_memory_t *pCurrentMemory; short bFound = 0; /* Cycle through each hash bucket */ for (nHash = 0; nHash < MEMORY_HASH && !bFound; ++nHash) { /* Iterate through the bucket's list */ for (pCurrentMemory = paMemoryHash[nHash]; pCurrentMemory && !bFound; pCurrentMemory = pCurrentMemory->pNext) { if (((unsigned int) pCurrentMemory->pvMemory < (unsigned int) pvMemory) && ((unsigned int) pCurrentMemory->pvMemory + pCurrentMemory->nAmountAllocated >= (unsigned int) pvMemory)) { fprintf (stderr, " This is in the memory space " "allocated for %p in bucket %d (%d bytes at %s:%d)\n\r", pCurrentMemory->pvMemory, nHash, pCurrentMemory->nAmountAllocated, pCurrentMemory->szFileName, pCurrentMemory->nLineNumber); bFound = 1; nSize = pCurrentMemory->nAmountAllocated; } } } return nSize; }