/***************************************************************************
* 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;
}