/*=========================================================================== * * File: DL_Block.CPP * Author: Dave Humphrey (uesp@m0use.net) * Created On: Tuesday, April 03, 2001 * * Implements the memory logging routines for debug builds. * If allocated memory blocks are stored in the block info structure * upon creation and removed upon deletion, the logging routines * can trace memory blocks that are deleted more than once or blocks that * are never freed. * * The code is primarily from "Writing Solid Code" by Steve Maguire. * *=========================================================================*/ #if defined(_DEBUG) /* Include Files */ #include "dl_block.h" #include "dl_log.h" #include <string.h> /*=========================================================================== * * Begin Local Variables * *=========================================================================*/ DEFINE_FILE(); /*=========================================================================== * End of Local Variables *=========================================================================*/ /*=========================================================================== * * Begin Pointer Comparison Macros * * The functions in this file must compare arbitrary pointers (ie, they * could point to anything), which is not always portable according to ANSI. * Thus, the following macros are used to define pointer comparisons to * allow the correct comparisons to be made across various platforms. * Ensure the comparisons are correct for your implemenation. * *=========================================================================*/ #define IsPtrLess(pLeft, pRight) ((pLeft) < (pRight)) #define IsPtrGreater(pLeft, pRight) ((pLeft) > (pRight)) #define IsPtrEqual(pLeft, pRight) ((pLeft) == (pRight)) #define IsPtrLessEq(pLeft, pRight) ((pLeft) <= (pRight)) #define IsPtrGreaterEq(pLeft, pRight) ((pLeft) >= (pRight)) /*=========================================================================== * End of Pointer Comparison Macros *=========================================================================*/ /*=========================================================================== * * Begin Private File Variables * *=========================================================================*/ /* Points to the start of a singly linked list of memory block * information structures */ static blockinfo_t* pBIHead = NULL; /*=========================================================================== * End of Private File Variables *=========================================================================*/ /*=========================================================================== * * Function - static blockinfo_t* GetBlockInfo (pBlock); * * A local function which attempts to return the block information structure * for the given memory block. If the block does not exist, the function * will ASSERT. The function always returns a valid block. * *=========================================================================*/ static blockinfo_t* GetBlockInfo (void* pBlock) { DEFINE_FUNCTION("GetBlockInfo()"); blockinfo_t* pBlockInfo; byte* pByteBlock = (byte *) pBlock; /* Parse through the linked list for a match */ for (pBlockInfo = pBIHead; pBlockInfo != NULL; pBlockInfo = pBlockInfo->pNext) { byte* pStart = pBlockInfo->pPointer; byte* pEnd = pBlockInfo->pPointer + pBlockInfo->Size - 1; if (IsPtrGreaterEq(pByteBlock, pStart) && IsPtrLessEq(pByteBlock, pEnd)) break; } /* Ensure a valid block was found */ ASSERT(pBlockInfo != NULL); return (pBlockInfo); } /*=========================================================================== * End of Function blockinfo_t* GetBlockInfo() *=========================================================================*/ /*=========================================================================== * * Function - void CheckMemoryRefs (void); * * Checks the reference member of all memory block logs looking for blocks * that have not been referenced with NoteMemoryRef(). The function * ASSERTs if any unflagged block is found. Use this function to check * for dangling pointers in your application. * *=========================================================================*/ void CheckMemoryRefs (void) { DEFINE_FUNCTION("CheckMemoryRefs()"); blockinfo_t* pBlockInfo; for (pBlockInfo = pBIHead; pBlockInfo != NULL; pBlockInfo = pBlockInfo->pNext) { /* Simple check for the integrity of the block. If this ASSERT * fires it indicates that the blockinfo code has caused an * error or its memory has been trashed. */ ASSERT(pBlockInfo->pPointer != NULL && pBlockInfo->Size != 0); /* Ensure that the block has been referenced by a call to * NoteMemoryRef(). If this ASSERT fires it indicates either * a dangling pointer or that not all pointers have been * accounted for. */ ASSERT(pBlockInfo->Referenced); } } /*=========================================================================== * End of Function CheckMemoryRefs() *=========================================================================*/ /*=========================================================================== * * Function - void ClearMemoryRefs (void); * * Clears all the reference flags of the current memory block logs. * Use this function before NoteMemoryRefs() to check for dangling pointers * in your application. * *=========================================================================*/ void ClearMemoryRefs (void) { //DEFINE_FUNCTION("ClearMemoryRefs()"); blockinfo_t* pBlockInfo; for (pBlockInfo = pBIHead; pBlockInfo != NULL; pBlockInfo = pBlockInfo->pNext) { pBlockInfo->Referenced = FALSE; } } /*=========================================================================== * End of Function ClearMemoryRefs() *=========================================================================*/ /*=========================================================================== * * Function - boolean CreateBlockInfo (pNewBlock, NewSize); * * Creates a new memory block information structure for the given memory. * Returns TRUE on success or FALSE if the new block log could not be * created. On failure the newly allocated block should be freed. * *=========================================================================*/ boolean CreateBlockInfo (void* pNewBlock, const size_t NewSize) { DEFINE_FUNCTION("CreateBlockInfo(void*, size_t)"); blockinfo_t* pBlockInfo; /* Ensure valid input */ ASSERT(pNewBlock != NULL && NewSize != 0); /* Create the memory log structure */ pBlockInfo = new blockinfo_t; if (pBlockInfo == NULL) return (FALSE); /* Initialize the new memory block */ pBlockInfo->pPointer = (byte*) pNewBlock; pBlockInfo->Size = NewSize; pBlockInfo->Referenced = TRUE; pBlockInfo->pNext = pBIHead; pBlockInfo->pName = NULL; pBIHead = pBlockInfo; return (TRUE); } /*=========================================================================== * End of Function CreateBlockInfo() *=========================================================================*/ /*=========================================================================== * * Function - boolean CreateBlockInfo (pNewBlock, NewSize, pName); * * Creates a new memory block information structure for the given memory. * Returns TRUE on success or FALSE if the new block log could not be * created. On failure the newly allocated block should be freed. * Accepts the given valid name string for thje pointer name. ASSERTs if * the input pNname pointer is not valid. * *=========================================================================*/ boolean CreateBlockInfo (void* pNewBlock, const size_t NewSize, const char* pName) { DEFINE_FUNCTION("CreateBlockInfo(void*, size_t, char*)"); blockinfo_t* pBlockInfo; /* Ensure valid input */ ASSERT(pNewBlock != NULL && NewSize != 0 && pName != NULL); /* Create the memory log structure */ pBlockInfo = new blockinfo_t; if (pBlockInfo == NULL) return (FALSE); /* Initialize the new memory block */ pBlockInfo->pPointer = (byte*) pNewBlock; pBlockInfo->Size = NewSize; pBlockInfo->Referenced = TRUE; pBlockInfo->pNext = pBIHead; /* Attempt to create the name string */ pBlockInfo->pName = new char[strlen(pName)+1]; if (pBlockInfo->pName == NULL) { memset (pBlockInfo, (int)GARBAGE_CHAR, sizeof(blockinfo_t)); delete pBlockInfo; return (FALSE); } strcpy (pBlockInfo->pName, pName); pBIHead = pBlockInfo; return (TRUE); } /*=========================================================================== * End of Function CreateBlockInfo() *=========================================================================*/ /*=========================================================================== * * Function - void FreeBlockInfo (void* pBlock) * * Attempts to remove the block info for the given memory. The function * will ASSERT if given an invalid memory block. * *=========================================================================*/ void FreeBlockInfo (void* pBlock) { DEFINE_FUNCTION("FreeBlockInfo()"); blockinfo_t* pBlockInfo; blockinfo_t* pPrevInfo = NULL; byte* pByteBlock = (byte *) pBlock; /* Ensure valid input */ ASSERT(pBlock != NULL); /* Search the block linked list for a match, from head to tail */ for (pBlockInfo = pBIHead; pBlockInfo != NULL; pBlockInfo = pBlockInfo->pNext) { /* Check for a block match */ if (IsPtrEqual(pBlockInfo->pPointer, pByteBlock)) { /* Rearrange the singly linked list */ if (pPrevInfo == NULL) pBIHead = pBlockInfo->pNext; else pPrevInfo->pNext = pBlockInfo->pNext; break; } pPrevInfo = pBlockInfo; } /* Ensure a valid block was found to be deleted */ ASSERT(pBlockInfo != NULL); /* Delete the pointer name, if any */ if (pBlockInfo->pName != NULL) { memset(pBlockInfo->pName, (int)GARBAGE_CHAR, strlen(pBlockInfo->pName) + 1); delete[] pBlockInfo->pName; } /* Clear the blockinfo structure and delete */ memset(pBlockInfo, (int)GARBAGE_CHAR, sizeof(blockinfo_t)); delete pBlockInfo; } /*=========================================================================== * End of Function FreeBlockInfo() *=========================================================================*/ /*=========================================================================== * * Function - size_t GetNumBlocks (void); * * Returns the total number of allocated blocks. * *=========================================================================*/ size_t GetNumBlocks (void) { //DEFINE_FUNCTION("GetNumBlocks()"); blockinfo_t* pBlockInfo; size_t Counter; /* Does the block info list have a head? */ if (pBIHead == NULL) return (0); pBlockInfo = pBIHead; Counter = 1; while (pBlockInfo->pNext != NULL) { Counter++; pBlockInfo = pBlockInfo->pNext; } return (Counter); } /*=========================================================================== * End of Function GetNumBlocks() *=========================================================================*/ /*=========================================================================== * * Function - boolean IsValidPointer (pBlock, MinSize); * * Ensures that the given block is a valid memory block and that it is * at least MinSize bytes in size (from the given pointer to the * end of the block). The passed block does not have to be the very start * of an allocated block, but can be anywhere in the block. * * The function will ASSERT if the block pointer or size is invalid, and * never actually returns NULL. The value returned is to allow the * function to be used in an ASSERT statement easily, such as * ASSERT(IsValidPointer(pSomeObject, size)); * *=========================================================================*/ boolean IsValidPointer (void* pBlock, const size_t MinSize) { DEFINE_FUNCTION("IsValidPointer(void*, size_t)"); blockinfo_t* pBlockInfo; byte* pByteBlock = (byte*) pBlock; /* Ensure valid input */ ASSERT(pBlock != NULL && MinSize != 0); pBlockInfo = GetBlockInfo(pBlock); /* Ensure that the block size is valid */ ASSERT(IsPtrLessEq(pByteBlock + MinSize, pBlockInfo->pPointer + pBlockInfo->Size)); return (TRUE); } /*=========================================================================== * End of Function IsValidPointer() *=========================================================================*/ /*=========================================================================== * * Function - boolean IsValidPointer (pBlock); * * Same as IsValidPointer(void*, size_t) except that it just checks if * the given pointer is valid without requiring a minimum size. * * The function will ASSERT if the block pointer or size is invalid, and * never actually returns NULL. The value returned is to allow the * function to be used in an ASSERT statement easily, such as * ASSERT(IsValidPointer(pSomeObject)); * *=========================================================================*/ boolean IsValidPointer (void* pBlock) { DEFINE_FUNCTION("IsValidPointer(void*)"); blockinfo_t* pBlockInfo; /* Ensure valid input */ ASSERT(pBlock != NULL); /* Attempt to get the block info */ pBlockInfo = GetBlockInfo(pBlock); if (pBlockInfo == NULL) return (FALSE); return (TRUE); } /*=========================================================================== * End of Function IsValidPointer() *=========================================================================*/ /*=========================================================================== * * Function - void NoteMemoryRef (pBlock); * * Sets the reference flag for the log associated with the given memory * block. The function ASSERTs if the given block is not valid. Note that * the input block does not have to be the start of the allocated block, * but anywhere within the block. * *=========================================================================*/ void NoteMemoryRef (void* pBlock) { //DEFINE_FUNCTION("NoteMemoryRef()"); blockinfo_t* pBlockInfo; pBlockInfo = GetBlockInfo(pBlock); pBlockInfo->Referenced = TRUE; } /*=========================================================================== * End of Function NoteMemoryRef() *=========================================================================*/ #undef __FUNC__ #define __FUNC__ "OutputBlockInfo()" /*=========================================================================== * * Function - void OutputBlockInfo (void); * * Outputs the current block information to the system log file. * *=========================================================================*/ void OutputBlockInfo (void) { //DEFINE_FUNCTION("OutputBlockInfo()"); blockinfo_t* pBlockInfo; long Index = 1; long MemorySize = 0l; SystemLog.Printf ("Outputting custom allocation blocks..."); for (pBlockInfo = pBIHead; pBlockInfo != NULL; pBlockInfo = pBlockInfo->pNext, Index++) { MemorySize += pBlockInfo->Size; SystemLog.Printf ("\t\t%3ld) %p (%s, %8u bytes), %s.", Index, pBlockInfo->pPointer, pBlockInfo->pName == NULL ? "NULL" : pBlockInfo->pName, pBlockInfo->Size, pBlockInfo->Referenced ? "is Referenced" : "NOT Referenced"); } SystemLog.Printf ("\tOutput %ld bytes in %ld blocks...", MemorySize, Index - 1); } /*=========================================================================== * End of Function OutputBlockInfo() *=========================================================================*/ /*=========================================================================== * * Function - size_t SizeOfBlock (pBlock); * * Attempts to get the allocated size of the given block. The function * will ASSERT if the passed pointer is invalid. * *=========================================================================*/ size_t SizeOfBlock (void* pBlock) { DEFINE_FUNCTION("SizeOfBlock()"); blockinfo_t* pBlockInfo; pBlockInfo = GetBlockInfo(pBlock); ASSERT((byte *) pBlock == pBlockInfo->pPointer); return (pBlockInfo->Size); } /*=========================================================================== * End of Function SizeOfBlock() *=========================================================================*/ /*=========================================================================== * * Function - void UpdateBlockInfo (pOldBlock, pNewBlock, NewSize); * * Attempts to update a log structure for a memory block. If any of the * input parameters are invalid, the function will ASSERT. * *=========================================================================*/ void UpdateBlockInfo (void* pOldBlock, void* pNewBlock, const size_t NewSize) { DEFINE_FUNCTION("UpdateBlockInfo()"); blockinfo_t* pBlockInfo; /* Ensure valid input */ ASSERT(pNewBlock != NULL && NewSize != 0); /* Attempt to retrieve the block information */ pBlockInfo = GetBlockInfo(pOldBlock); ASSERT((byte *) pOldBlock == pBlockInfo->pPointer); /* Update the log information */ pBlockInfo->pPointer = (byte *) pNewBlock; pBlockInfo->Size = NewSize; } /*=========================================================================== * End of Function UpdateBlockInfo() *=========================================================================*/ #endif /* End of if defined(_DEBUG) */